From 3c6c5c870b335aa01f6f98ae8ea1826b9d7013df Mon Sep 17 00:00:00 2001 From: Soham Sen Date: Tue, 12 Dec 2023 23:13:54 +0530 Subject: [PATCH] Add Prettier Lint for CI --- .eslintrc.cjs | 56 +- ...push-docker.yml => build-push-docker.yaml} | 0 .github/workflows/ci.yaml | 30 + .prettierignore | 15 +- .prettierrc | 11 +- API.md | 56 +- CONTRIBUTING.md | 32 +- README.md | 24 +- cli/README.md | 2 +- package.json | 104 +- postcss.config.js | 12 +- src/app.css | 18 +- src/app.d.ts | 12 +- src/app.html | 38 +- src/hooks.server.ts | 12 +- src/lib/components/Hamburger.svelte | 60 +- src/lib/crypto.ts | 238 +-- src/lib/data.ts | 1324 ++++++++--------- src/lib/server/auth.ts | 103 +- src/lib/server/email/base.ts | 44 +- src/lib/server/email/reset-password.ts | 24 +- src/lib/server/email/verify.ts | 22 +- src/lib/server/services.ts | 79 +- src/lib/server/validate.ts | 42 +- src/lib/types.ts | 56 +- src/lib/utils/time.ts | 22 +- src/routes/(auth)/+layout.svelte | 78 +- .../(auth)/forgot-password/+page.server.ts | 70 +- .../(auth)/forgot-password/+page.svelte | 61 +- src/routes/(auth)/login/+page.server.ts | 128 +- src/routes/(auth)/login/+page.svelte | 95 +- src/routes/(auth)/logout/+page.server.ts | 12 +- src/routes/(auth)/register/+page.server.ts | 236 +-- src/routes/(auth)/register/+page.svelte | 159 +- .../(auth)/reset-password/+page.server.ts | 113 +- src/routes/(auth)/reset-password/+page.svelte | 80 +- src/routes/(auth)/validate/+page.server.ts | 23 +- src/routes/+layout.svelte | 2 +- src/routes/+page.server.ts | 20 +- src/routes/+page.svelte | 716 ++++----- src/routes/[key]/+error.svelte | 6 +- src/routes/[key]/+page.server.ts | 54 +- src/routes/[key]/+page.svelte | 422 +++--- src/routes/[key]/+server.ts | 98 +- src/routes/[key]/edit/+page.server.ts | 24 +- src/routes/[key]/edit/+page.svelte | 380 ++--- src/routes/api/paste/+server.ts | 292 ++-- src/routes/dashboard/+layout.svelte | 103 +- src/routes/dashboard/settings/+page.server.ts | 58 +- src/routes/dashboard/settings/+page.svelte | 202 +-- src/routes/info/+page.svelte | 255 ++-- svelte.config.js | 30 +- tailwind.config.js | 28 +- tsconfig.json | 30 +- vite.config.ts | 2 +- yarn.lock | 18 +- 56 files changed, 3305 insertions(+), 2926 deletions(-) rename .github/workflows/{build-push-docker.yml => build-push-docker.yaml} (100%) create mode 100644 .github/workflows/ci.yaml diff --git a/.eslintrc.cjs b/.eslintrc.cjs index ebc1958..b359f98 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,30 +1,30 @@ module.exports = { - root: true, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:svelte/recommended', - 'prettier' - ], - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - parserOptions: { - sourceType: 'module', - ecmaVersion: 2020, - extraFileExtensions: ['.svelte'] - }, - env: { - browser: true, - es2017: true, - node: true - }, - overrides: [ - { - files: ['*.svelte'], - parser: 'svelte-eslint-parser', - parserOptions: { - parser: '@typescript-eslint/parser' - } - } - ] + root: true, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:svelte/recommended', + 'prettier', + ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + extraFileExtensions: ['.svelte'], + }, + env: { + browser: true, + es2017: true, + node: true, + }, + overrides: [ + { + files: ['*.svelte'], + parser: 'svelte-eslint-parser', + parserOptions: { + parser: '@typescript-eslint/parser', + }, + }, + ], }; diff --git a/.github/workflows/build-push-docker.yml b/.github/workflows/build-push-docker.yaml similarity index 100% rename from .github/workflows/build-push-docker.yml rename to .github/workflows/build-push-docker.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..ccb0edb --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,30 @@ +name: Continuous Integration + +on: + pull_request: + push: + branches: [main] + +jobs: + prettier: + name: Prettier Check + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Run Prettier + id: prettier-run + uses: rutajdash/prettier-cli-action@v1.0.0 + with: + config_path: ./.prettierrc + prettier_version: "3.1.1" + + # This step only runs if prettier finds errors causing the previous step to fail + # This steps lists the files where errors were found + - name: Prettier Output + if: ${{ failure() }} + shell: bash + run: | + echo "The following files are not formatted:" + echo "${{steps.prettier-run.outputs.prettier_output}}" \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 3897265..680bc55 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,13 +1,2 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example - -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock +*.yml +*.yaml diff --git a/.prettierrc b/.prettierrc index a77fdde..859f2f9 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,9 +1,6 @@ { - "useTabs": true, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 100, - "plugins": ["prettier-plugin-svelte"], - "pluginSearchDirs": ["."], - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] + "singleQuote": true, + "tabWidth": 4, + "plugins": ["prettier-plugin-svelte"], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] } diff --git a/API.md b/API.md index 2950f44..a821951 100644 --- a/API.md +++ b/API.md @@ -8,44 +8,44 @@ #### Body -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| `content` | `string` | Paste content. If encrypted, must be encoded into a string (preferably Base64). | Yes | -| `config` | `object` | Configuration for the paste | No | -| `passwordProtected` | `boolean` | Whether the paste is password protected. | No | -| `initVector` | `string` | Initialization vector for AES encryption. Max length: 64. | No | +| Name | Type | Description | Required | +| ------------------- | --------- | ------------------------------------------------------------------------------- | -------- | +| `content` | `string` | Paste content. If encrypted, must be encoded into a string (preferably Base64). | Yes | +| `config` | `object` | Configuration for the paste | No | +| `passwordProtected` | `boolean` | Whether the paste is password protected. | No | +| `initVector` | `string` | Initialization vector for AES encryption. Max length: 64. | No | **Config Object:** -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| `language` | `string` | Programming language of the paste. Default: `plaintext`. | No | -| `encrypted` | `boolean` | Whether the paste is encrypted. Default: `false`. | No | -| `expiresAfter` | `number` | Time in seconds until the paste expires. | No | -| `burnAfterRead` | `boolean` | Whether the paste is deleted after reading. | No | +| Name | Type | Description | Required | +| --------------- | --------- | -------------------------------------------------------- | -------- | +| `language` | `string` | Programming language of the paste. Default: `plaintext`. | No | +| `encrypted` | `boolean` | Whether the paste is encrypted. Default: `false`. | No | +| `expiresAfter` | `number` | Time in seconds until the paste expires. | No | +| `burnAfterRead` | `boolean` | Whether the paste is deleted after reading. | No | ### Examples ```json { - "content": "i0n3PW6qDUhDaTrzoKg+/ip4qQwu+iq8/fWDVg==", - "config": { - "language": "plaintext", - "encrypted": true, - "expiresAfter": 3600, - "burnAfterRead": false - }, - "passwordProtected": false, - "initVector": "27DIWK00yDiGx001" + "content": "i0n3PW6qDUhDaTrzoKg+/ip4qQwu+iq8/fWDVg==", + "config": { + "language": "plaintext", + "encrypted": true, + "expiresAfter": 3600, + "burnAfterRead": false + }, + "passwordProtected": false, + "initVector": "27DIWK00yDiGx001" } ``` ```json { - "content": "Hello World!", - "config": { - "language": "plaintext" - } + "content": "Hello World!", + "config": { + "language": "plaintext" + } } ``` @@ -57,6 +57,6 @@ #### Query Parameters -| Name | Type | Description | Required | -| ---- | ---- | ----------- | -------- | -| `key` | `string` | Paste key. | Yes | +| Name | Type | Description | Required | +| ----- | -------- | ----------- | -------- | +| `key` | `string` | Paste key. | Yes | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e6304f3..ec02056 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,15 +1,15 @@ # Contributing When contributing to this repository, please first discuss the change you wish to make via issue, -email, or any other method with the owners of this repository before making a change. +email, or any other method with the owners of this repository before making a change. Please note we have a code of conduct, please follow it in all your interactions with the project. ## Pull Request Process -1. Ensure any install or build dependencies are removed before the end of the layer when doing a +1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. -2. Update the README.md with details of changes to the interface, this includes new environment +2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. 3. Ensure that the code is adhering to the existing code style. @@ -29,22 +29,22 @@ orientation. Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting ### Our Responsibilities diff --git a/README.md b/README.md index 9333ca1..618d99e 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,18 @@ Well, cause no pastebin I could find had ALL of the following features: - - Modern and minimal UI (This site's design was inspired by bin). - - Optional end-to-end encryption (we're using AES-256-GCM) with optional password protection (using PBKDF2). - - Syntax highlighting (using Prism) that supports 297 languages. - - API support to create and get pastes from command line. - - View raw pastes. Normally, encrypted pastebins do not have this. With this site, you can either get the Base64-encoded encrypted paste, or decrypt it on the server side (even with the password) and get the raw paste. - - Keyboard shortcuts! - - And of course, being fully open-source and easily self-hostable. - - Ability to edit pastes after creation, and a dashboard for viewing all your pastes. - - **NEW** Feature to use custom path names. - - **Comes with a CLI tool to create and read pastes from the command line!** - - ## API Documentation +- Modern and minimal UI (This site's design was inspired by bin). +- Optional end-to-end encryption (we're using AES-256-GCM) with optional password protection (using PBKDF2). +- Syntax highlighting (using Prism) that supports 297 languages. +- API support to create and get pastes from command line. +- View raw pastes. Normally, encrypted pastebins do not have this. With this site, you can either get the Base64-encoded encrypted paste, or decrypt it on the server side (even with the password) and get the raw paste. +- Keyboard shortcuts! +- And of course, being fully open-source and easily self-hostable. +- Ability to edit pastes after creation, and a dashboard for viewing all your pastes. +- **NEW** Feature to use custom path names. +- **Comes with a CLI tool to create and read pastes from the command line!** + +## API Documentation See [API.md](API.md). diff --git a/cli/README.md b/cli/README.md index a78aeb6..db6e548 100644 --- a/cli/README.md +++ b/cli/README.md @@ -43,4 +43,4 @@ options: -h, --help show this help message and exit --password PASSWORD, -p PASSWORD Password to decrypt the paste with. Only needed if password-protected. -``` \ No newline at end of file +``` diff --git a/package.json b/package.json index 7d5a7bc..7b2ba47 100644 --- a/package.json +++ b/package.json @@ -1,54 +1,54 @@ { - "name": "yabin", - "version": "0.2.3", - "private": true, - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --plugin-search-dir . --check . && eslint .", - "format": "prettier --plugin-search-dir . --write .", - "prepare": "svelte-kit sync", - "postinstall": "prisma generate --schema=./src/lib/server/prisma/schema.prisma" - }, - "devDependencies": { - "@sveltejs/adapter-auto": "^2.0.0", - "@sveltejs/adapter-node": "^1.2.4", - "@sveltejs/adapter-vercel": "^3.0.3", - "@sveltejs/kit": "^1.5.0", - "@types/node-cron": "^3.0.7", - "@types/nodemailer": "^6.4.11", - "@types/prismjs": "^1.26.0", - "@types/sanitize-html": "^2.9.0", - "@typescript-eslint/eslint-plugin": "^5.45.0", - "@typescript-eslint/parser": "^5.45.0", - "autoprefixer": "^10.4.14", - "eslint": "^8.28.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-svelte": "^2.26.0", - "postcss": "^8.4.31", - "prettier": "^2.8.0", - "prettier-plugin-svelte": "^2.8.1", - "prisma": "^4.15.0", - "svelte": "^3.54.0", - "svelte-check": "^3.0.1", - "svelte-select": "^5.6.1", - "tailwindcss": "^3.3.2", - "tslib": "^2.4.1", - "typescript": "^5.0.0", - "vite": "^4.3.0" - }, - "type": "module", - "dependencies": { - "@prisma/client": "^4.15.0", - "base64-js": "^1.5.1", - "nanoid": "^5.0.1", - "node-cron": "^3.0.2", - "nodemailer": "^6.9.5", - "prism-themes": "^1.9.0", - "prismjs": "^1.29.0", - "sanitize-html": "^2.10.0" - } + "name": "yabin", + "version": "0.2.3", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "prettier --plugin-search-dir . --check . && eslint .", + "format": "prettier --write .", + "prepare": "svelte-kit sync", + "postinstall": "prisma generate --schema=./src/lib/server/prisma/schema.prisma" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^2.0.0", + "@sveltejs/adapter-node": "^1.2.4", + "@sveltejs/adapter-vercel": "^3.0.3", + "@sveltejs/kit": "^1.5.0", + "@types/node-cron": "^3.0.7", + "@types/nodemailer": "^6.4.11", + "@types/prismjs": "^1.26.0", + "@types/sanitize-html": "^2.9.0", + "@typescript-eslint/eslint-plugin": "^5.45.0", + "@typescript-eslint/parser": "^5.45.0", + "autoprefixer": "^10.4.14", + "eslint": "^8.28.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-svelte": "^2.26.0", + "postcss": "^8.4.31", + "prettier": "3.1.1", + "prettier-plugin-svelte": "3.1.2", + "prisma": "^4.15.0", + "svelte": "^3.54.0", + "svelte-check": "^3.0.1", + "svelte-select": "^5.6.1", + "tailwindcss": "^3.3.2", + "tslib": "^2.4.1", + "typescript": "^5.0.0", + "vite": "^4.3.0" + }, + "type": "module", + "dependencies": { + "@prisma/client": "^4.15.0", + "base64-js": "^1.5.1", + "nanoid": "^5.0.1", + "node-cron": "^3.0.2", + "nodemailer": "^6.9.5", + "prism-themes": "^1.9.0", + "prismjs": "^1.29.0", + "sanitize-html": "^2.10.0" + } } diff --git a/postcss.config.js b/postcss.config.js index 5559af3..67d9d23 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,8 +1,8 @@ export default { - plugins: { - 'postcss-import': {}, - 'tailwindcss/nesting': {}, - tailwindcss: {}, - autoprefixer: {} - } + plugins: { + 'postcss-import': {}, + 'tailwindcss/nesting': {}, + tailwindcss: {}, + autoprefixer: {}, + }, }; diff --git a/src/app.css b/src/app.css index 34cdd8b..11ec768 100644 --- a/src/app.css +++ b/src/app.css @@ -3,19 +3,19 @@ @tailwind utilities; :root { - --color-primary: #e5e1d1; - --color-background: #101419; - --color-dark: rgba(0, 0, 0, 0.7); + --color-primary: #e5e1d1; + --color-background: #101419; + --color-dark: rgba(0, 0, 0, 0.7); } body { - @apply bg-background; - @apply text-primary; + @apply bg-background; + @apply text-primary; } @layer utilities { - input[type='number']::-webkit-inner-spin-button, - input[type='number']::-webkit-outer-spin-button { - @apply appearance-none; - } + input[type='number']::-webkit-inner-spin-button, + input[type='number']::-webkit-outer-spin-button { + @apply appearance-none; + } } diff --git a/src/app.d.ts b/src/app.d.ts index f59b884..addc02a 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,12 +1,12 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} + } } export {}; diff --git a/src/app.html b/src/app.html index e622076..90f3c7a 100644 --- a/src/app.html +++ b/src/app.html @@ -1,23 +1,23 @@ - + + + + + + + + + YABin - - - - - - - - YABin + - + %sveltekit.head% + - %sveltekit.head% - - - -
%sveltekit.body%
- - - \ No newline at end of file + +
%sveltekit.body%
+ + diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 0d100f9..0ef3a1d 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -2,15 +2,15 @@ import { deleteExpiredPastes } from '$lib/server/services'; import cron from 'node-cron'; export async function handle({ event, resolve }) { - const searchParams = event.url.searchParams; - if (searchParams.get('r') !== null || searchParams.get('raw') !== null) { - event.request.headers.set('Accept', 'text/plain'); - } + const searchParams = event.url.searchParams; + if (searchParams.get('r') !== null || searchParams.get('raw') !== null) { + event.request.headers.set('Accept', 'text/plain'); + } - return await resolve(event); + return await resolve(event); } // Cron-job to delete expired pastes cron.schedule('*/5 * * * *', async () => { - await deleteExpiredPastes(); + await deleteExpiredPastes(); }); diff --git a/src/lib/components/Hamburger.svelte b/src/lib/components/Hamburger.svelte index de78a72..582bed1 100644 --- a/src/lib/components/Hamburger.svelte +++ b/src/lib/components/Hamburger.svelte @@ -1,44 +1,44 @@ diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts index 031c3f1..4df8dfd 100644 --- a/src/lib/crypto.ts +++ b/src/lib/crypto.ts @@ -1,106 +1,156 @@ import base64 from 'base64-js'; -export async function encrypt(plaintext: string, keyStr: string | undefined = undefined) { - const encoder = new TextEncoder(); - - const iv: Uint8Array = crypto.getRandomValues(new Uint8Array(12)); - const ivStr = base64.fromByteArray(iv); - - const alg = { name: 'AES-GCM', iv, length: 256 }; - - let key: CryptoKey; - if (!keyStr) { - key = (await crypto.subtle.generateKey(alg, true, ['encrypt'])) as CryptoKey; - keyStr = base64.fromByteArray(new Uint8Array(await crypto.subtle.exportKey('raw', key))); - } else { - key = await crypto.subtle.importKey('raw', base64.toByteArray(keyStr), alg, false, ['encrypt']); - } - const enc = await crypto.subtle.encrypt(alg, key, encoder.encode(plaintext)); - const encStr = base64.fromByteArray(new Uint8Array(enc)); - - return { ciphertext: encStr, iv: ivStr, key: keyStr }; +export async function encrypt( + plaintext: string, + keyStr: string | undefined = undefined, +) { + const encoder = new TextEncoder(); + + const iv: Uint8Array = crypto.getRandomValues(new Uint8Array(12)); + const ivStr = base64.fromByteArray(iv); + + const alg = { name: 'AES-GCM', iv, length: 256 }; + + let key: CryptoKey; + if (!keyStr) { + key = (await crypto.subtle.generateKey(alg, true, [ + 'encrypt', + ])) as CryptoKey; + keyStr = base64.fromByteArray( + new Uint8Array(await crypto.subtle.exportKey('raw', key)), + ); + } else { + key = await crypto.subtle.importKey( + 'raw', + base64.toByteArray(keyStr), + alg, + false, + ['encrypt'], + ); + } + const enc = await crypto.subtle.encrypt( + alg, + key, + encoder.encode(plaintext), + ); + const encStr = base64.fromByteArray(new Uint8Array(enc)); + + return { ciphertext: encStr, iv: ivStr, key: keyStr }; } -export async function decrypt(ciphertext: string, ivStr: string, keyStr: string) { - const decoder = new TextDecoder('utf-8'); - - const iv = base64.toByteArray(ivStr); - const alg = { name: 'AES-GCM', iv, length: 256 }; - - const key = await crypto.subtle.importKey('raw', base64.toByteArray(keyStr), alg, false, [ - 'decrypt' - ]); - - const dec = await crypto.subtle.decrypt(alg, key, base64.toByteArray(ciphertext)); - return decoder.decode(dec); +export async function decrypt( + ciphertext: string, + ivStr: string, + keyStr: string, +) { + const decoder = new TextDecoder('utf-8'); + + const iv = base64.toByteArray(ivStr); + const alg = { name: 'AES-GCM', iv, length: 256 }; + + const key = await crypto.subtle.importKey( + 'raw', + base64.toByteArray(keyStr), + alg, + false, + ['decrypt'], + ); + + const dec = await crypto.subtle.decrypt( + alg, + key, + base64.toByteArray(ciphertext), + ); + return decoder.decode(dec); } export async function encryptWithPassword(plaintext: string, password: string) { - const encoder = new TextEncoder(); - - const iv: Uint8Array = crypto.getRandomValues(new Uint8Array(12)); - const ivStr = base64.fromByteArray(iv); - const alg = { name: 'AES-GCM', iv, length: 256 }; - - const keyMaterial = await crypto.subtle.importKey( - 'raw', - encoder.encode(password), - 'PBKDF2', - false, - ['deriveBits', 'deriveKey'] - ); - const key = await crypto.subtle.deriveKey( - { - name: 'PBKDF2', - salt: await crypto.subtle.digest('SHA-512', encoder.encode(password)), - iterations: 310000, - hash: 'SHA-512' - }, - keyMaterial, - alg, - false, - ['encrypt'] - ); - const enc = await crypto.subtle.encrypt(alg, key, encoder.encode(plaintext)); - const encStr = base64.fromByteArray(new Uint8Array(enc)); - - return { ciphertext: encStr, iv: ivStr }; + const encoder = new TextEncoder(); + + const iv: Uint8Array = crypto.getRandomValues(new Uint8Array(12)); + const ivStr = base64.fromByteArray(iv); + const alg = { name: 'AES-GCM', iv, length: 256 }; + + const keyMaterial = await crypto.subtle.importKey( + 'raw', + encoder.encode(password), + 'PBKDF2', + false, + ['deriveBits', 'deriveKey'], + ); + const key = await crypto.subtle.deriveKey( + { + name: 'PBKDF2', + salt: await crypto.subtle.digest( + 'SHA-512', + encoder.encode(password), + ), + iterations: 310000, + hash: 'SHA-512', + }, + keyMaterial, + alg, + false, + ['encrypt'], + ); + const enc = await crypto.subtle.encrypt( + alg, + key, + encoder.encode(plaintext), + ); + const encStr = base64.fromByteArray(new Uint8Array(enc)); + + return { ciphertext: encStr, iv: ivStr }; } -export async function decryptWithPassword(ciphertext: string, iv: string, password: string) { - const encoder = new TextEncoder(); - const decoder = new TextDecoder('utf-8'); - - const alg = { name: 'AES-GCM', iv: base64.toByteArray(iv), length: 256 }; - - const keyMaterial = await crypto.subtle.importKey( - 'raw', - encoder.encode(password), - 'PBKDF2', - false, - ['deriveBits', 'deriveKey'] - ); - - const key = await crypto.subtle.deriveKey( - { - name: 'PBKDF2', - salt: await crypto.subtle.digest('SHA-512', encoder.encode(password)), - iterations: 310000, - hash: 'SHA-512' - }, - keyMaterial, - alg, - false, - ['decrypt'] - ); - - const dec = await crypto.subtle.decrypt(alg, key, base64.toByteArray(ciphertext)); - return decoder.decode(dec); +export async function decryptWithPassword( + ciphertext: string, + iv: string, + password: string, +) { + const encoder = new TextEncoder(); + const decoder = new TextDecoder('utf-8'); + + const alg = { name: 'AES-GCM', iv: base64.toByteArray(iv), length: 256 }; + + const keyMaterial = await crypto.subtle.importKey( + 'raw', + encoder.encode(password), + 'PBKDF2', + false, + ['deriveBits', 'deriveKey'], + ); + + const key = await crypto.subtle.deriveKey( + { + name: 'PBKDF2', + salt: await crypto.subtle.digest( + 'SHA-512', + encoder.encode(password), + ), + iterations: 310000, + hash: 'SHA-512', + }, + keyMaterial, + alg, + false, + ['decrypt'], + ); + + const dec = await crypto.subtle.decrypt( + alg, + key, + base64.toByteArray(ciphertext), + ); + return decoder.decode(dec); } -export async function hashPassword(password: string, salt: string): Promise { - const encoder = new TextEncoder(); - const data = encoder.encode(password + salt); - const hash = await crypto.subtle.digest('SHA-512', data); - return base64.fromByteArray(new Uint8Array(hash)); +export async function hashPassword( + password: string, + salt: string, +): Promise { + const encoder = new TextEncoder(); + const data = encoder.encode(password + salt); + const hash = await crypto.subtle.digest('SHA-512', data); + return base64.fromByteArray(new Uint8Array(hash)); } diff --git a/src/lib/data.ts b/src/lib/data.ts index 0a25b7e..49d2e29 100644 --- a/src/lib/data.ts +++ b/src/lib/data.ts @@ -1,671 +1,671 @@ export const languageKeysByName = new Map([ - ['Plain text', 'plaintext'], - ['Markup', 'markup'], - ['HTML', 'html'], - ['XML', 'xml'], - ['SVG', 'svg'], - ['MathML', 'mathml'], - ['SSML', 'ssml'], - ['Atom', 'atom'], - ['RSS', 'rss'], - ['CSS', 'css'], - ['C-like', 'clike'], - ['JavaScript', 'javascript'], - ['ABAP', 'abap'], - ['ABNF', 'abnf'], - ['ActionScript', 'actionscript'], - ['Ada', 'ada'], - ['Agda', 'agda'], - ['AL', 'al'], - ['ANTLR4', 'antlr4'], - ['Apache Configuration', 'apacheconf'], - ['Apex', 'apex'], - ['APL', 'apl'], - ['AppleScript', 'applescript'], - ['AQL', 'aql'], - ['Arduino', 'arduino'], - ['ARFF', 'arff'], - ['ARM Assembly', 'armasm'], - ['Arturo', 'arturo'], - ['AsciiDoc', 'asciidoc'], - ['ASP.NET (C#)', 'aspnet'], - ['6502 Assembly', 'asm6502'], - ['Atmel AVR Assembly', 'asmatmel'], - ['AutoHotkey', 'autohotkey'], - ['AutoIt', 'autoit'], - ['AviSynth', 'avisynth'], - ['Avro IDL', 'avro-idl'], - ['AWK', 'awk'], - ['GAWK', 'gawk'], - ['Bash', 'bash'], - ['Shell', 'sh'], - ['Shell', 'shell'], - ['BASIC', 'basic'], - ['Batch', 'batch'], - ['BBcode', 'bbcode'], - ['Shortcode', 'shortcode'], - ['BBj', 'bbj'], - ['Bicep', 'bicep'], - ['Birb', 'birb'], - ['Bison', 'bison'], - ['BNF', 'bnf'], - ['RBNF', 'rbnf'], - ['BQN', 'bqn'], - ['Brainfuck', 'brainfuck'], - ['BrightScript', 'brightscript'], - ['Bro', 'bro'], - ['BSL (1C:Enterprise)', 'bsl'], - ['OneScript', 'oscript'], - ['C', 'c'], - ['C#', 'csharp'], - ['C++', 'cpp'], - ['CFScript', 'cfscript'], - ['ChaiScript', 'chaiscript'], - ['CIL', 'cil'], - ['Cilk/C', 'cilkc'], - ['Cilk/C++', 'cilkcpp'], - ['Clojure', 'clojure'], - ['CMake', 'cmake'], - ['COBOL', 'cobol'], - ['CoffeeScript', 'coffeescript'], - ['Concurnas', 'concurnas'], - ['Content-Security-Policy', 'csp'], - ['Cooklang', 'cooklang'], - ['Coq', 'coq'], - ['Crystal', 'crystal'], - ['CSS Extras', 'css-extras'], - ['CSV', 'csv'], - ['CUE', 'cue'], - ['Cypher', 'cypher'], - ['D', 'd'], - ['Dart', 'dart'], - ['DataWeave', 'dataweave'], - ['DAX', 'dax'], - ['Dhall', 'dhall'], - ['Diff', 'diff'], - ['Django/Jinja2', 'django'], - ['DNS zone file', 'dns-zone-file'], - ['Docker', 'docker'], - ['DOT (Graphviz)', 'dot'], - ['EBNF', 'ebnf'], - ['EditorConfig', 'editorconfig'], - ['Eiffel', 'eiffel'], - ['EJS', 'ejs'], - ['Eta', 'eta'], - ['Elixir', 'elixir'], - ['Elm', 'elm'], - ['Embedded Lua templating', 'etlua'], - ['ERB', 'erb'], - ['Erlang', 'erlang'], - ['Excel Formula', 'excel-formula'], - ['F#', 'fsharp'], - ['Factor', 'factor'], - ['False', 'false'], - ['Firestore security rules', 'firestore-security-rules'], - ['Flow', 'flow'], - ['Fortran', 'fortran'], - ['FreeMarker Template Language', 'ftl'], - ['GameMaker Language', 'gml'], - ['GAP (CAS)', 'gap'], - ['G-code', 'gcode'], - ['GDScript', 'gdscript'], - ['GEDCOM', 'gedcom'], - ['gettext', 'gettext'], - ['Gherkin', 'gherkin'], - ['Git', 'git'], - ['GLSL', 'glsl'], - ['GN', 'gn'], - ['GNU Linker Script', 'linker-script'], - ['Go', 'go'], - ['Go module', 'go-module'], - ['Gradle', 'gradle'], - ['GraphQL', 'graphql'], - ['Groovy', 'groovy'], - ['Haml', 'haml'], - ['Handlebars', 'handlebars'], - ['Mustache', 'mustache'], - ['Haskell', 'haskell'], - ['Haxe', 'haxe'], - ['HCL', 'hcl'], - ['HLSL', 'hlsl'], - ['Hoon', 'hoon'], - ['HTTP', 'http'], - ['HTTP Public-Key-Pins', 'hpkp'], - ['HTTP Strict-Transport-Security', 'hsts'], - ['IchigoJam', 'ichigojam'], - ['Icon', 'icon'], - ['ICU Message Format', 'icu-message-format'], - ['Idris', 'idris'], - ['.ignore', 'ignore'], - ['.gitignore', 'gitignore'], - ['.hgignore', 'hgignore'], - ['.npmignore', 'npmignore'], - ['Inform 7', 'inform7'], - ['Ini', 'ini'], - ['Io', 'io'], - ['J', 'j'], - ['Java', 'java'], - ['JavaDoc', 'javadoc'], - ['JavaDoc-like', 'javadoclike'], - ['Java stack trace', 'javastacktrace'], - ['Jexl', 'jexl'], - ['Jolie', 'jolie'], - ['JQ', 'jq'], - ['JSDoc', 'jsdoc'], - ['JS Extras', 'js-extras'], - ['JSON', 'json'], - ['Web App Manifest', 'webmanifest'], - ['JSON5', 'json5'], - ['JSONP', 'jsonp'], - ['JS stack trace', 'jsstacktrace'], - ['JS Templates', 'js-templates'], - ['Julia', 'julia'], - ['Keepalived Configure', 'keepalived'], - ['Keyman', 'keyman'], - ['Kotlin', 'kotlin'], - ['Kotlin Script', 'kts'], - ['KuMir (КуМир)', 'kumir'], - ['Kusto', 'kusto'], - ['LaTeX', 'latex'], - ['TeX', 'tex'], - ['ConTeXt', 'context'], - ['Latte', 'latte'], - ['Less', 'less'], - ['LilyPond', 'lilypond'], - ['Liquid', 'liquid'], - ['Lisp', 'lisp'], - ['LiveScript', 'livescript'], - ['LLVM IR', 'llvm'], - ['Log file', 'log'], - ['LOLCODE', 'lolcode'], - ['Lua', 'lua'], - ['Magma (CAS)', 'magma'], - ['Makefile', 'makefile'], - ['Markdown', 'markdown'], - ['Markup templating', 'markup-templating'], - ['Mata', 'mata'], - ['MATLAB', 'matlab'], - ['MAXScript', 'maxscript'], - ['MEL', 'mel'], - ['Mermaid', 'mermaid'], - ['METAFONT', 'metafont'], - ['Mizar', 'mizar'], - ['MongoDB', 'mongodb'], - ['Monkey', 'monkey'], - ['MoonScript', 'moonscript'], - ['N1QL', 'n1ql'], - ['N4JS', 'n4js'], - ['Nand To Tetris HDL', 'nand2tetris-hdl'], - ['Naninovel Script', 'naniscript'], - ['NASM', 'nasm'], - ['NEON', 'neon'], - ['Nevod', 'nevod'], - ['nginx', 'nginx'], - ['Nim', 'nim'], - ['Nix', 'nix'], - ['NSIS', 'nsis'], - ['Objective-C', 'objectivec'], - ['OCaml', 'ocaml'], - ['Odin', 'odin'], - ['OpenCL', 'opencl'], - ['OpenQasm', 'openqasm'], - ['Oz', 'oz'], - ['PARI/GP', 'parigp'], - ['Parser', 'parser'], - ['Pascal', 'pascal'], - ['Object Pascal', 'objectpascal'], - ['Pascaligo', 'pascaligo'], - ['PATROL Scripting Language', 'psl'], - ['PC-Axis', 'pcaxis'], - ['PeopleCode', 'peoplecode'], - ['Perl', 'perl'], - ['PHP', 'php'], - ['PHPDoc', 'phpdoc'], - ['PHP Extras', 'php-extras'], - ['PlantUML', 'plant-uml'], - ['PL/SQL', 'plsql'], - ['PowerQuery', 'powerquery'], - ['PowerShell', 'powershell'], - ['Processing', 'processing'], - ['Prolog', 'prolog'], - ['PromQL', 'promql'], - ['.properties', 'properties'], - ['Protocol Buffers', 'protobuf'], - ['Pug', 'pug'], - ['Puppet', 'puppet'], - ['Pure', 'pure'], - ['PureBasic', 'purebasic'], - ['PureScript', 'purescript'], - ['Python', 'python'], - ['Q#', 'qsharp'], - ['Q (kdb+ database)', 'q'], - ['QML', 'qml'], - ['Qore', 'qore'], - ['R', 'r'], - ['Racket', 'racket'], - ['Razor C#', 'cshtml'], - ['React JSX', 'jsx'], - ['React TSX', 'tsx'], - ['Reason', 'reason'], - ['Regex', 'regex'], - ['Rego', 'rego'], - ["Ren'py", 'renpy'], - ['ReScript', 'rescript'], - ['reST (reStructuredText)', 'rest'], - ['Rip', 'rip'], - ['Roboconf', 'roboconf'], - ['Robot Framework', 'robotframework'], - ['Ruby', 'ruby'], - ['Rust', 'rust'], - ['SAS', 'sas'], - ['Sass (Sass)', 'sass'], - ['Sass (SCSS)', 'scss'], - ['Scala', 'scala'], - ['Scheme', 'scheme'], - ['Shell session', 'shell-session'], - ['Smali', 'smali'], - ['Smalltalk', 'smalltalk'], - ['Smarty', 'smarty'], - ['SML', 'sml'], - ['SML/NJ', 'smlnj'], - ['Solidity (Ethereum)', 'solidity'], - ['Solution file', 'solution-file'], - ['Soy (Closure Template)', 'soy'], - ['SPARQL', 'sparql'], - ['Splunk SPL', 'splunk-spl'], - ['SQF: Status Quo Function (Arma 3)', 'sqf'], - ['SQL', 'sql'], - ['Squirrel', 'squirrel'], - ['Stan', 'stan'], - ['Stata Ado', 'stata'], - ['Structured Text (IEC 61131-3)', 'iecst'], - ['Stylus', 'stylus'], - ['SuperCollider', 'supercollider'], - ['Swift', 'swift'], - ['Systemd configuration file', 'systemd'], - ['T4 templating', 't4-templating'], - ['T4 Text Templates (C#)', 't4-cs'], - ['T4 Text Templates (VB)', 't4-vb'], - ['TAP', 'tap'], - ['Tcl', 'tcl'], - ['Template Toolkit 2', 'tt2'], - ['Textile', 'textile'], - ['TOML', 'toml'], - ['Tremor', 'tremor'], - ['trickle', 'trickle'], - ['troy', 'troy'], - ['Turtle', 'turtle'], - ['TriG', 'trig'], - ['Twig', 'twig'], - ['TypeScript', 'typescript'], - ['TypoScript', 'typoscript'], - ['TSConfig', 'tsconfig'], - ['UnrealScript', 'unrealscript'], - ['UO Razor Script', 'uorazor'], - ['URI', 'uri'], - ['URL', 'url'], - ['V', 'v'], - ['Vala', 'vala'], - ['VB.Net', 'vbnet'], - ['Velocity', 'velocity'], - ['Verilog', 'verilog'], - ['VHDL', 'vhdl'], - ['vim', 'vim'], - ['Visual Basic', 'visual-basic'], - ['VBA', 'vba'], - ['WarpScript', 'warpscript'], - ['WebAssembly', 'wasm'], - ['Web IDL', 'web-idl'], - ['WGSL', 'wgsl'], - ['Wiki markup', 'wiki'], - ['Wolfram language', 'wolfram'], - ['Mathematica', 'mathematica'], - ['Mathematica Notebook', 'nb'], - ['Wren', 'wren'], - ['Xeora', 'xeora'], - ['XeoraCube', 'xeoracube'], - ['XML doc (.net)', 'xml-doc'], - ['Xojo (REALbasic)', 'xojo'], - ['XQuery', 'xquery'], - ['YAML', 'yaml'], - ['YANG', 'yang'], - ['Zig', 'zig'] + ['Plain text', 'plaintext'], + ['Markup', 'markup'], + ['HTML', 'html'], + ['XML', 'xml'], + ['SVG', 'svg'], + ['MathML', 'mathml'], + ['SSML', 'ssml'], + ['Atom', 'atom'], + ['RSS', 'rss'], + ['CSS', 'css'], + ['C-like', 'clike'], + ['JavaScript', 'javascript'], + ['ABAP', 'abap'], + ['ABNF', 'abnf'], + ['ActionScript', 'actionscript'], + ['Ada', 'ada'], + ['Agda', 'agda'], + ['AL', 'al'], + ['ANTLR4', 'antlr4'], + ['Apache Configuration', 'apacheconf'], + ['Apex', 'apex'], + ['APL', 'apl'], + ['AppleScript', 'applescript'], + ['AQL', 'aql'], + ['Arduino', 'arduino'], + ['ARFF', 'arff'], + ['ARM Assembly', 'armasm'], + ['Arturo', 'arturo'], + ['AsciiDoc', 'asciidoc'], + ['ASP.NET (C#)', 'aspnet'], + ['6502 Assembly', 'asm6502'], + ['Atmel AVR Assembly', 'asmatmel'], + ['AutoHotkey', 'autohotkey'], + ['AutoIt', 'autoit'], + ['AviSynth', 'avisynth'], + ['Avro IDL', 'avro-idl'], + ['AWK', 'awk'], + ['GAWK', 'gawk'], + ['Bash', 'bash'], + ['Shell', 'sh'], + ['Shell', 'shell'], + ['BASIC', 'basic'], + ['Batch', 'batch'], + ['BBcode', 'bbcode'], + ['Shortcode', 'shortcode'], + ['BBj', 'bbj'], + ['Bicep', 'bicep'], + ['Birb', 'birb'], + ['Bison', 'bison'], + ['BNF', 'bnf'], + ['RBNF', 'rbnf'], + ['BQN', 'bqn'], + ['Brainfuck', 'brainfuck'], + ['BrightScript', 'brightscript'], + ['Bro', 'bro'], + ['BSL (1C:Enterprise)', 'bsl'], + ['OneScript', 'oscript'], + ['C', 'c'], + ['C#', 'csharp'], + ['C++', 'cpp'], + ['CFScript', 'cfscript'], + ['ChaiScript', 'chaiscript'], + ['CIL', 'cil'], + ['Cilk/C', 'cilkc'], + ['Cilk/C++', 'cilkcpp'], + ['Clojure', 'clojure'], + ['CMake', 'cmake'], + ['COBOL', 'cobol'], + ['CoffeeScript', 'coffeescript'], + ['Concurnas', 'concurnas'], + ['Content-Security-Policy', 'csp'], + ['Cooklang', 'cooklang'], + ['Coq', 'coq'], + ['Crystal', 'crystal'], + ['CSS Extras', 'css-extras'], + ['CSV', 'csv'], + ['CUE', 'cue'], + ['Cypher', 'cypher'], + ['D', 'd'], + ['Dart', 'dart'], + ['DataWeave', 'dataweave'], + ['DAX', 'dax'], + ['Dhall', 'dhall'], + ['Diff', 'diff'], + ['Django/Jinja2', 'django'], + ['DNS zone file', 'dns-zone-file'], + ['Docker', 'docker'], + ['DOT (Graphviz)', 'dot'], + ['EBNF', 'ebnf'], + ['EditorConfig', 'editorconfig'], + ['Eiffel', 'eiffel'], + ['EJS', 'ejs'], + ['Eta', 'eta'], + ['Elixir', 'elixir'], + ['Elm', 'elm'], + ['Embedded Lua templating', 'etlua'], + ['ERB', 'erb'], + ['Erlang', 'erlang'], + ['Excel Formula', 'excel-formula'], + ['F#', 'fsharp'], + ['Factor', 'factor'], + ['False', 'false'], + ['Firestore security rules', 'firestore-security-rules'], + ['Flow', 'flow'], + ['Fortran', 'fortran'], + ['FreeMarker Template Language', 'ftl'], + ['GameMaker Language', 'gml'], + ['GAP (CAS)', 'gap'], + ['G-code', 'gcode'], + ['GDScript', 'gdscript'], + ['GEDCOM', 'gedcom'], + ['gettext', 'gettext'], + ['Gherkin', 'gherkin'], + ['Git', 'git'], + ['GLSL', 'glsl'], + ['GN', 'gn'], + ['GNU Linker Script', 'linker-script'], + ['Go', 'go'], + ['Go module', 'go-module'], + ['Gradle', 'gradle'], + ['GraphQL', 'graphql'], + ['Groovy', 'groovy'], + ['Haml', 'haml'], + ['Handlebars', 'handlebars'], + ['Mustache', 'mustache'], + ['Haskell', 'haskell'], + ['Haxe', 'haxe'], + ['HCL', 'hcl'], + ['HLSL', 'hlsl'], + ['Hoon', 'hoon'], + ['HTTP', 'http'], + ['HTTP Public-Key-Pins', 'hpkp'], + ['HTTP Strict-Transport-Security', 'hsts'], + ['IchigoJam', 'ichigojam'], + ['Icon', 'icon'], + ['ICU Message Format', 'icu-message-format'], + ['Idris', 'idris'], + ['.ignore', 'ignore'], + ['.gitignore', 'gitignore'], + ['.hgignore', 'hgignore'], + ['.npmignore', 'npmignore'], + ['Inform 7', 'inform7'], + ['Ini', 'ini'], + ['Io', 'io'], + ['J', 'j'], + ['Java', 'java'], + ['JavaDoc', 'javadoc'], + ['JavaDoc-like', 'javadoclike'], + ['Java stack trace', 'javastacktrace'], + ['Jexl', 'jexl'], + ['Jolie', 'jolie'], + ['JQ', 'jq'], + ['JSDoc', 'jsdoc'], + ['JS Extras', 'js-extras'], + ['JSON', 'json'], + ['Web App Manifest', 'webmanifest'], + ['JSON5', 'json5'], + ['JSONP', 'jsonp'], + ['JS stack trace', 'jsstacktrace'], + ['JS Templates', 'js-templates'], + ['Julia', 'julia'], + ['Keepalived Configure', 'keepalived'], + ['Keyman', 'keyman'], + ['Kotlin', 'kotlin'], + ['Kotlin Script', 'kts'], + ['KuMir (КуМир)', 'kumir'], + ['Kusto', 'kusto'], + ['LaTeX', 'latex'], + ['TeX', 'tex'], + ['ConTeXt', 'context'], + ['Latte', 'latte'], + ['Less', 'less'], + ['LilyPond', 'lilypond'], + ['Liquid', 'liquid'], + ['Lisp', 'lisp'], + ['LiveScript', 'livescript'], + ['LLVM IR', 'llvm'], + ['Log file', 'log'], + ['LOLCODE', 'lolcode'], + ['Lua', 'lua'], + ['Magma (CAS)', 'magma'], + ['Makefile', 'makefile'], + ['Markdown', 'markdown'], + ['Markup templating', 'markup-templating'], + ['Mata', 'mata'], + ['MATLAB', 'matlab'], + ['MAXScript', 'maxscript'], + ['MEL', 'mel'], + ['Mermaid', 'mermaid'], + ['METAFONT', 'metafont'], + ['Mizar', 'mizar'], + ['MongoDB', 'mongodb'], + ['Monkey', 'monkey'], + ['MoonScript', 'moonscript'], + ['N1QL', 'n1ql'], + ['N4JS', 'n4js'], + ['Nand To Tetris HDL', 'nand2tetris-hdl'], + ['Naninovel Script', 'naniscript'], + ['NASM', 'nasm'], + ['NEON', 'neon'], + ['Nevod', 'nevod'], + ['nginx', 'nginx'], + ['Nim', 'nim'], + ['Nix', 'nix'], + ['NSIS', 'nsis'], + ['Objective-C', 'objectivec'], + ['OCaml', 'ocaml'], + ['Odin', 'odin'], + ['OpenCL', 'opencl'], + ['OpenQasm', 'openqasm'], + ['Oz', 'oz'], + ['PARI/GP', 'parigp'], + ['Parser', 'parser'], + ['Pascal', 'pascal'], + ['Object Pascal', 'objectpascal'], + ['Pascaligo', 'pascaligo'], + ['PATROL Scripting Language', 'psl'], + ['PC-Axis', 'pcaxis'], + ['PeopleCode', 'peoplecode'], + ['Perl', 'perl'], + ['PHP', 'php'], + ['PHPDoc', 'phpdoc'], + ['PHP Extras', 'php-extras'], + ['PlantUML', 'plant-uml'], + ['PL/SQL', 'plsql'], + ['PowerQuery', 'powerquery'], + ['PowerShell', 'powershell'], + ['Processing', 'processing'], + ['Prolog', 'prolog'], + ['PromQL', 'promql'], + ['.properties', 'properties'], + ['Protocol Buffers', 'protobuf'], + ['Pug', 'pug'], + ['Puppet', 'puppet'], + ['Pure', 'pure'], + ['PureBasic', 'purebasic'], + ['PureScript', 'purescript'], + ['Python', 'python'], + ['Q#', 'qsharp'], + ['Q (kdb+ database)', 'q'], + ['QML', 'qml'], + ['Qore', 'qore'], + ['R', 'r'], + ['Racket', 'racket'], + ['Razor C#', 'cshtml'], + ['React JSX', 'jsx'], + ['React TSX', 'tsx'], + ['Reason', 'reason'], + ['Regex', 'regex'], + ['Rego', 'rego'], + ["Ren'py", 'renpy'], + ['ReScript', 'rescript'], + ['reST (reStructuredText)', 'rest'], + ['Rip', 'rip'], + ['Roboconf', 'roboconf'], + ['Robot Framework', 'robotframework'], + ['Ruby', 'ruby'], + ['Rust', 'rust'], + ['SAS', 'sas'], + ['Sass (Sass)', 'sass'], + ['Sass (SCSS)', 'scss'], + ['Scala', 'scala'], + ['Scheme', 'scheme'], + ['Shell session', 'shell-session'], + ['Smali', 'smali'], + ['Smalltalk', 'smalltalk'], + ['Smarty', 'smarty'], + ['SML', 'sml'], + ['SML/NJ', 'smlnj'], + ['Solidity (Ethereum)', 'solidity'], + ['Solution file', 'solution-file'], + ['Soy (Closure Template)', 'soy'], + ['SPARQL', 'sparql'], + ['Splunk SPL', 'splunk-spl'], + ['SQF: Status Quo Function (Arma 3)', 'sqf'], + ['SQL', 'sql'], + ['Squirrel', 'squirrel'], + ['Stan', 'stan'], + ['Stata Ado', 'stata'], + ['Structured Text (IEC 61131-3)', 'iecst'], + ['Stylus', 'stylus'], + ['SuperCollider', 'supercollider'], + ['Swift', 'swift'], + ['Systemd configuration file', 'systemd'], + ['T4 templating', 't4-templating'], + ['T4 Text Templates (C#)', 't4-cs'], + ['T4 Text Templates (VB)', 't4-vb'], + ['TAP', 'tap'], + ['Tcl', 'tcl'], + ['Template Toolkit 2', 'tt2'], + ['Textile', 'textile'], + ['TOML', 'toml'], + ['Tremor', 'tremor'], + ['trickle', 'trickle'], + ['troy', 'troy'], + ['Turtle', 'turtle'], + ['TriG', 'trig'], + ['Twig', 'twig'], + ['TypeScript', 'typescript'], + ['TypoScript', 'typoscript'], + ['TSConfig', 'tsconfig'], + ['UnrealScript', 'unrealscript'], + ['UO Razor Script', 'uorazor'], + ['URI', 'uri'], + ['URL', 'url'], + ['V', 'v'], + ['Vala', 'vala'], + ['VB.Net', 'vbnet'], + ['Velocity', 'velocity'], + ['Verilog', 'verilog'], + ['VHDL', 'vhdl'], + ['vim', 'vim'], + ['Visual Basic', 'visual-basic'], + ['VBA', 'vba'], + ['WarpScript', 'warpscript'], + ['WebAssembly', 'wasm'], + ['Web IDL', 'web-idl'], + ['WGSL', 'wgsl'], + ['Wiki markup', 'wiki'], + ['Wolfram language', 'wolfram'], + ['Mathematica', 'mathematica'], + ['Mathematica Notebook', 'nb'], + ['Wren', 'wren'], + ['Xeora', 'xeora'], + ['XeoraCube', 'xeoracube'], + ['XML doc (.net)', 'xml-doc'], + ['Xojo (REALbasic)', 'xojo'], + ['XQuery', 'xquery'], + ['YAML', 'yaml'], + ['YANG', 'yang'], + ['Zig', 'zig'], ]); export interface LanguageData { - ext: string; + ext: string; } // Not fully accurate, generated with (mostly) ChatGPT 4 export const languageDataByKey = new Map([ - ['plaintext', { ext: 'txt' }], - ['markup', { ext: 'txt' }], - ['html', { ext: 'html' }], - ['xml', { ext: 'xml' }], - ['svg', { ext: 'svg' }], - ['mathml', { ext: 'txt' }], - ['ssml', { ext: 'ssml' }], - ['atom', { ext: 'xml' }], - ['rss', { ext: 'xml' }], - ['css', { ext: 'css' }], - ['clike', { ext: 'txt' }], - ['javascript', { ext: 'js' }], - ['abap', { ext: 'abap' }], - ['abnf', { ext: 'txt' }], - ['actionscript', { ext: 'as' }], - ['ada', { ext: 'ada' }], - ['agda', { ext: 'agda' }], - ['al', { ext: 'al' }], - ['antlr4', { ext: 'g4' }], - ['apacheconf', { ext: 'conf' }], - ['apex', { ext: 'cls' }], - ['apl', { ext: 'apl' }], - ['applescript', { ext: 'applescript' }], - ['aql', { ext: 'txt' }], - ['arduino', { ext: 'ino' }], - ['arff', { ext: 'arff' }], - ['armasm', { ext: 's' }], - ['arturo', { ext: 'arturo' }], - ['asciidoc', { ext: 'adoc' }], - ['aspnet', { ext: 'aspx' }], - ['asm6502', { ext: 'asm' }], - ['asmatmel', { ext: 'txt' }], - ['autohotkey', { ext: 'ahk' }], - ['autoit', { ext: 'au3' }], - ['avisynth', { ext: 'avs' }], - ['avro-idl', { ext: 'avdl' }], - ['awk', { ext: 'awk' }], - ['gawk', { ext: 'awk' }], - ['bash', { ext: 'sh' }], - ['shell', { ext: 'sh' }], - ['basic', { ext: 'bas' }], - ['batch', { ext: 'bat' }], - ['bbcode', { ext: 'txt' }], - ['shortcode', { ext: 'txt' }], - ['bbj', { ext: 'bbj' }], - ['bicep', { ext: 'bicep' }], - ['birb', { ext: 'birb' }], - ['bison', { ext: 'y' }], - ['bnf', { ext: 'txt' }], - ['rbnf', { ext: 'txt' }], - ['bqn', { ext: 'bqn' }], - ['brainfuck', { ext: 'bf' }], - ['brightscript', { ext: 'brs' }], - ['bro', { ext: 'bro' }], - ['bsl', { ext: 'bsl' }], - ['oscript', { ext: 'os' }], - ['c', { ext: 'c' }], - ['csharp', { ext: 'cs' }], - ['cpp', { ext: 'cpp' }], - ['cfscript', { ext: 'cfc' }], - ['chaiscript', { ext: 'chai' }], - ['cil', { ext: 'cil' }], - ['cilkc', { ext: 'txt' }], - ['cilkcpp', { ext: 'txt' }], - ['clojure', { ext: 'clj' }], - ['cmake', { ext: 'txt' }], - ['cobol', { ext: 'cbl' }], - ['coffeescript', { ext: 'coffee' }], - ['concurnas', { ext: 'conc' }], - ['csp', { ext: 'txt' }], - ['cooklang', { ext: 'ckl' }], - ['coq', { ext: 'v' }], - ['crystal', { ext: 'cr' }], - ['css-extras', { ext: 'css' }], - ['csv', { ext: 'csv' }], - ['cue', { ext: 'cue' }], - ['cypher', { ext: 'cyp' }], - ['d', { ext: 'd' }], - ['dart', { ext: 'dart' }], - ['dataweave', { ext: 'dwl' }], - ['dax', { ext: 'txt' }], - ['dhall', { ext: 'dhall' }], - ['diff', { ext: 'diff' }], - ['django', { ext: 'py' }], - ['dns-zone-file', { ext: 'zone' }], - ['docker', { ext: 'dockerfile' }], - ['dot', { ext: 'dot' }], - ['ebnf', { ext: 'ebnf' }], - ['editorconfig', { ext: 'editorconfig' }], - ['eiffel', { ext: 'e' }], - ['ejs', { ext: 'ejs' }], - ['eta', { ext: 'eta' }], - ['elixir', { ext: 'ex' }], - ['elm', { ext: 'elm' }], - ['etlua', { ext: 'lua' }], - ['erb', { ext: 'erb' }], - ['erlang', { ext: 'erl' }], - ['excel-formula', { ext: 'xlsx' }], - ['fsharp', { ext: 'fs' }], - ['factor', { ext: 'factor' }], - ['false', { ext: 'fls' }], - ['firestore-security-rules', { ext: 'rules' }], - ['flow', { ext: 'txt' }], - ['fortran', { ext: 'f' }], - ['ftl', { ext: 'ftl' }], - ['gml', { ext: 'gml' }], - ['gap', { ext: 'g' }], - ['gcode', { ext: 'gcode' }], - ['gdscript', { ext: 'gd' }], - ['gedcom', { ext: 'ged' }], - ['gettext', { ext: 'po' }], - ['gherkin', { ext: 'feature' }], - ['git', { ext: 'txt' }], - ['glsl', { ext: 'glsl' }], - ['gn', { ext: 'gn' }], - ['linker-script', { ext: 'lds' }], - ['go', { ext: 'go' }], - ['go-module', { ext: 'mod' }], - ['gradle', { ext: 'gradle' }], - ['graphql', { ext: 'graphql' }], - ['groovy', { ext: 'groovy' }], - ['haml', { ext: 'haml' }], - ['handlebars', { ext: 'hbs' }], - ['mustache', { ext: 'mustache' }], - ['haskell', { ext: 'hs' }], - ['haxe', { ext: 'hx' }], - ['hcl', { ext: 'hcl' }], - ['hlsl', { ext: 'hlsl' }], - ['hoon', { ext: 'hoon' }], - ['http', { ext: 'http' }], - ['hpkp', { ext: 'txt' }], - ['hsts', { ext: 'txt' }], - ['ichigojam', { ext: 'txt' }], - ['icon', { ext: 'icn' }], - ['icu-message-format', { ext: 'txt' }], - ['idris', { ext: 'idr' }], - ['ignore', { ext: 'ignore' }], - ['gitignore', { ext: 'gitignore' }], - ['hgignore', { ext: 'hgignore' }], - ['npmignore', { ext: 'npmignore' }], - ['inform7', { ext: 'ni' }], - ['ini', { ext: 'ini' }], - ['io', { ext: 'io' }], - ['j', { ext: 'ijs' }], - ['java', { ext: 'java' }], - ['javadoc', { ext: 'txt' }], - ['javadoclike', { ext: 'txt' }], - ['javastacktrace', { ext: 'txt' }], - ['jexl', { ext: 'txt' }], - ['jolie', { ext: 'ol' }], - ['jq', { ext: 'jq' }], - ['jsdoc', { ext: 'txt' }], - ['js-extras', { ext: 'txt' }], - ['json', { ext: 'json' }], - ['webmanifest', { ext: 'webmanifest' }], - ['json5', { ext: 'json5' }], - ['jsonp', { ext: 'txt' }], - ['jsstacktrace', { ext: 'txt' }], - ['js-templates', { ext: 'txt' }], - ['julia', { ext: 'jl' }], - ['keepalived', { ext: 'conf' }], - ['keyman', { ext: 'kmn' }], - ['kotlin', { ext: 'kt' }], - ['kts', { ext: 'kts' }], - ['kumir', { ext: 'kum' }], - ['kusto', { ext: 'csl' }], - ['latex', { ext: 'tex' }], - ['tex', { ext: 'tex' }], - ['context', { ext: 'tex' }], - ['latte', { ext: 'latte' }], - ['less', { ext: 'less' }], - ['lilypond', { ext: 'ly' }], - ['liquid', { ext: 'liquid' }], - ['lisp', { ext: 'lisp' }], - ['livescript', { ext: 'ls' }], - ['llvm', { ext: 'll' }], - ['log', { ext: 'log' }], - ['lolcode', { ext: 'lol' }], - ['lua', { ext: 'lua' }], - ['magma', { ext: 'magma' }], - ['makefile', { ext: 'make' }], - ['markdown', { ext: 'md' }], - ['markup-templating', { ext: 'txt' }], - ['mata', { ext: 'ado' }], - ['matlab', { ext: 'm' }], - ['maxscript', { ext: 'ms' }], - ['mel', { ext: 'mel' }], - ['mermaid', { ext: 'mmd' }], - ['metafont', { ext: 'mf' }], - ['mizar', { ext: 'miz' }], - ['mongodb', { ext: 'txt' }], - ['monkey', { ext: 'monkey' }], - ['moonscript', { ext: 'moon' }], - ['n1ql', { ext: 'txt' }], - ['n4js', { ext: 'n4js' }], - ['nand2tetris-hdl', { ext: 'hdl' }], - ['naniscript', { ext: 'nani' }], - ['nasm', { ext: 'asm' }], - ['neon', { ext: 'neon' }], - ['nevod', { ext: 'txt' }], - ['nginx', { ext: 'conf' }], - ['nim', { ext: 'nim' }], - ['nix', { ext: 'nix' }], - ['nsis', { ext: 'nsi' }], - ['objectivec', { ext: 'm' }], - ['ocaml', { ext: 'ml' }], - ['odin', { ext: 'odin' }], - ['opencl', { ext: 'cl' }], - ['openqasm', { ext: 'qasm' }], - ['oz', { ext: 'oz' }], - ['parigp', { ext: 'gp' }], - ['parser', { ext: 'txt' }], - ['pascal', { ext: 'pas' }], - ['objectpascal', { ext: 'pas' }], - ['pascaligo', { ext: 'ligo' }], - ['psl', { ext: 'psl' }], - ['pcaxis', { ext: 'txt' }], - ['peoplecode', { ext: 'ppl' }], - ['perl', { ext: 'pl' }], - ['php', { ext: 'php' }], - ['phpdoc', { ext: 'txt' }], - ['php-extras', { ext: 'txt' }], - ['plant-uml', { ext: 'txt' }], - ['plsql', { ext: 'pls' }], - ['powerquery', { ext: 'txt' }], - ['powershell', { ext: 'ps1' }], - ['processing', { ext: 'pde' }], - ['prolog', { ext: 'pl' }], - ['promql', { ext: 'promql' }], - ['properties', { ext: 'properties' }], - ['protobuf', { ext: 'proto' }], - ['pug', { ext: 'pug' }], - ['puppet', { ext: 'pp' }], - ['pure', { ext: 'pure' }], - ['purebasic', { ext: 'pb' }], - ['purescript', { ext: 'purs' }], - ['python', { ext: 'py' }], - ['qsharp', { ext: 'qs' }], - ['q', { ext: 'q' }], - ['qml', { ext: 'qml' }], - ['qore', { ext: 'q' }], - ['r', { ext: 'r' }], - ['racket', { ext: 'rkt' }], - ['cshtml', { ext: 'cshtml' }], - ['jsx', { ext: 'jsx' }], - ['tsx', { ext: 'tsx' }], - ['reason', { ext: 're' }], - ['regex', { ext: 'regex' }], - ['rego', { ext: 'rego' }], - ['renpy', { ext: 'rpy' }], - ['rescript', { ext: 'res' }], - ['rest', { ext: 'txt' }], - ['rip', { ext: 'rip' }], - ['roboconf', { ext: 'txt' }], - ['robotframework', { ext: 'robot' }], - ['ruby', { ext: 'rb' }], - ['rust', { ext: 'rs' }], - ['sas', { ext: 'sas' }], - ['sass', { ext: 'sass' }], - ['scss', { ext: 'scss' }], - ['scala', { ext: 'scala' }], - ['scheme', { ext: 'scm' }], - ['shell-session', { ext: 'txt' }], - ['smali', { ext: 'smali' }], - ['smalltalk', { ext: 'st' }], - ['smarty', { ext: 'tpl' }], - ['sml', { ext: 'sml' }], - ['smlnj', { ext: 'sml' }], - ['solidity', { ext: 'sol' }], - ['solution-file', { ext: 'sln' }], - ['soy', { ext: 'soy' }], - ['sparql', { ext: 'rq' }], - ['splunk-spl', { ext: 'spl' }], - ['sqf', { ext: 'sqf' }], - ['sql', { ext: 'sql' }], - ['squirrel', { ext: 'nut' }], - ['stan', { ext: 'stan' }], - ['stata', { ext: 'do' }], - ['iecst', { ext: 'st' }], - ['stylus', { ext: 'styl' }], - ['supercollider', { ext: 'sc' }], - ['swift', { ext: 'swift' }], - ['systemd', { ext: 'service' }], - ['t4-templating', { ext: 't4' }], - ['t4-cs', { ext: 't4' }], - ['t4-vb', { ext: 't4' }], - ['tap', { ext: 'tap' }], - ['tcl', { ext: 'tcl' }], - ['tt2', { ext: 'tt2' }], - ['textile', { ext: 'textile' }], - ['toml', { ext: 'toml' }], - ['tremor', { ext: 'trickle' }], - ['trickle', { ext: 'trickle' }], - ['troy', { ext: 'troy' }], - ['turtle', { ext: 'ttl' }], - ['trig', { ext: 'trig' }], - ['twig', { ext: 'twig' }], - ['typescript', { ext: 'ts' }], - ['typoscript', { ext: 'ts' }], - ['tsconfig', { ext: 'json' }], - ['unrealscript', { ext: 'uc' }], - ['uorazor', { ext: 'txt' }], - ['uri', { ext: 'uri' }], - ['url', { ext: 'url' }], - ['v', { ext: 'v' }], - ['vala', { ext: 'vala' }], - ['vbnet', { ext: 'vb' }], - ['velocity', { ext: 'vm' }], - ['verilog', { ext: 'v' }], - ['vhdl', { ext: 'vhd' }], - ['vim', { ext: 'vim' }], - ['visual-basic', { ext: 'vb' }], - ['vba', { ext: 'bas' }], - ['warpscript', { ext: 'warpscript' }], - ['wasm', { ext: 'wasm' }], - ['web-idl', { ext: 'idl' }], - ['wgsl', { ext: 'wgsl' }], - ['wiki', { ext: 'wiki' }], - ['wolfram', { ext: 'wl' }], - ['mathematica', { ext: 'nb' }], - ['nb', { ext: 'nb' }], - ['wren', { ext: 'wren' }], - ['xeora', { ext: 'x' }], - ['xeoracube', { ext: 'txt' }], - ['xml-doc', { ext: 'xml' }], - ['xojo', { ext: 'xojo_code' }], - ['xquery', { ext: 'xq' }], - ['yaml', { ext: 'yml' }], - ['yang', { ext: 'yang' }], - ['zig', { ext: 'zig' }] + ['plaintext', { ext: 'txt' }], + ['markup', { ext: 'txt' }], + ['html', { ext: 'html' }], + ['xml', { ext: 'xml' }], + ['svg', { ext: 'svg' }], + ['mathml', { ext: 'txt' }], + ['ssml', { ext: 'ssml' }], + ['atom', { ext: 'xml' }], + ['rss', { ext: 'xml' }], + ['css', { ext: 'css' }], + ['clike', { ext: 'txt' }], + ['javascript', { ext: 'js' }], + ['abap', { ext: 'abap' }], + ['abnf', { ext: 'txt' }], + ['actionscript', { ext: 'as' }], + ['ada', { ext: 'ada' }], + ['agda', { ext: 'agda' }], + ['al', { ext: 'al' }], + ['antlr4', { ext: 'g4' }], + ['apacheconf', { ext: 'conf' }], + ['apex', { ext: 'cls' }], + ['apl', { ext: 'apl' }], + ['applescript', { ext: 'applescript' }], + ['aql', { ext: 'txt' }], + ['arduino', { ext: 'ino' }], + ['arff', { ext: 'arff' }], + ['armasm', { ext: 's' }], + ['arturo', { ext: 'arturo' }], + ['asciidoc', { ext: 'adoc' }], + ['aspnet', { ext: 'aspx' }], + ['asm6502', { ext: 'asm' }], + ['asmatmel', { ext: 'txt' }], + ['autohotkey', { ext: 'ahk' }], + ['autoit', { ext: 'au3' }], + ['avisynth', { ext: 'avs' }], + ['avro-idl', { ext: 'avdl' }], + ['awk', { ext: 'awk' }], + ['gawk', { ext: 'awk' }], + ['bash', { ext: 'sh' }], + ['shell', { ext: 'sh' }], + ['basic', { ext: 'bas' }], + ['batch', { ext: 'bat' }], + ['bbcode', { ext: 'txt' }], + ['shortcode', { ext: 'txt' }], + ['bbj', { ext: 'bbj' }], + ['bicep', { ext: 'bicep' }], + ['birb', { ext: 'birb' }], + ['bison', { ext: 'y' }], + ['bnf', { ext: 'txt' }], + ['rbnf', { ext: 'txt' }], + ['bqn', { ext: 'bqn' }], + ['brainfuck', { ext: 'bf' }], + ['brightscript', { ext: 'brs' }], + ['bro', { ext: 'bro' }], + ['bsl', { ext: 'bsl' }], + ['oscript', { ext: 'os' }], + ['c', { ext: 'c' }], + ['csharp', { ext: 'cs' }], + ['cpp', { ext: 'cpp' }], + ['cfscript', { ext: 'cfc' }], + ['chaiscript', { ext: 'chai' }], + ['cil', { ext: 'cil' }], + ['cilkc', { ext: 'txt' }], + ['cilkcpp', { ext: 'txt' }], + ['clojure', { ext: 'clj' }], + ['cmake', { ext: 'txt' }], + ['cobol', { ext: 'cbl' }], + ['coffeescript', { ext: 'coffee' }], + ['concurnas', { ext: 'conc' }], + ['csp', { ext: 'txt' }], + ['cooklang', { ext: 'ckl' }], + ['coq', { ext: 'v' }], + ['crystal', { ext: 'cr' }], + ['css-extras', { ext: 'css' }], + ['csv', { ext: 'csv' }], + ['cue', { ext: 'cue' }], + ['cypher', { ext: 'cyp' }], + ['d', { ext: 'd' }], + ['dart', { ext: 'dart' }], + ['dataweave', { ext: 'dwl' }], + ['dax', { ext: 'txt' }], + ['dhall', { ext: 'dhall' }], + ['diff', { ext: 'diff' }], + ['django', { ext: 'py' }], + ['dns-zone-file', { ext: 'zone' }], + ['docker', { ext: 'dockerfile' }], + ['dot', { ext: 'dot' }], + ['ebnf', { ext: 'ebnf' }], + ['editorconfig', { ext: 'editorconfig' }], + ['eiffel', { ext: 'e' }], + ['ejs', { ext: 'ejs' }], + ['eta', { ext: 'eta' }], + ['elixir', { ext: 'ex' }], + ['elm', { ext: 'elm' }], + ['etlua', { ext: 'lua' }], + ['erb', { ext: 'erb' }], + ['erlang', { ext: 'erl' }], + ['excel-formula', { ext: 'xlsx' }], + ['fsharp', { ext: 'fs' }], + ['factor', { ext: 'factor' }], + ['false', { ext: 'fls' }], + ['firestore-security-rules', { ext: 'rules' }], + ['flow', { ext: 'txt' }], + ['fortran', { ext: 'f' }], + ['ftl', { ext: 'ftl' }], + ['gml', { ext: 'gml' }], + ['gap', { ext: 'g' }], + ['gcode', { ext: 'gcode' }], + ['gdscript', { ext: 'gd' }], + ['gedcom', { ext: 'ged' }], + ['gettext', { ext: 'po' }], + ['gherkin', { ext: 'feature' }], + ['git', { ext: 'txt' }], + ['glsl', { ext: 'glsl' }], + ['gn', { ext: 'gn' }], + ['linker-script', { ext: 'lds' }], + ['go', { ext: 'go' }], + ['go-module', { ext: 'mod' }], + ['gradle', { ext: 'gradle' }], + ['graphql', { ext: 'graphql' }], + ['groovy', { ext: 'groovy' }], + ['haml', { ext: 'haml' }], + ['handlebars', { ext: 'hbs' }], + ['mustache', { ext: 'mustache' }], + ['haskell', { ext: 'hs' }], + ['haxe', { ext: 'hx' }], + ['hcl', { ext: 'hcl' }], + ['hlsl', { ext: 'hlsl' }], + ['hoon', { ext: 'hoon' }], + ['http', { ext: 'http' }], + ['hpkp', { ext: 'txt' }], + ['hsts', { ext: 'txt' }], + ['ichigojam', { ext: 'txt' }], + ['icon', { ext: 'icn' }], + ['icu-message-format', { ext: 'txt' }], + ['idris', { ext: 'idr' }], + ['ignore', { ext: 'ignore' }], + ['gitignore', { ext: 'gitignore' }], + ['hgignore', { ext: 'hgignore' }], + ['npmignore', { ext: 'npmignore' }], + ['inform7', { ext: 'ni' }], + ['ini', { ext: 'ini' }], + ['io', { ext: 'io' }], + ['j', { ext: 'ijs' }], + ['java', { ext: 'java' }], + ['javadoc', { ext: 'txt' }], + ['javadoclike', { ext: 'txt' }], + ['javastacktrace', { ext: 'txt' }], + ['jexl', { ext: 'txt' }], + ['jolie', { ext: 'ol' }], + ['jq', { ext: 'jq' }], + ['jsdoc', { ext: 'txt' }], + ['js-extras', { ext: 'txt' }], + ['json', { ext: 'json' }], + ['webmanifest', { ext: 'webmanifest' }], + ['json5', { ext: 'json5' }], + ['jsonp', { ext: 'txt' }], + ['jsstacktrace', { ext: 'txt' }], + ['js-templates', { ext: 'txt' }], + ['julia', { ext: 'jl' }], + ['keepalived', { ext: 'conf' }], + ['keyman', { ext: 'kmn' }], + ['kotlin', { ext: 'kt' }], + ['kts', { ext: 'kts' }], + ['kumir', { ext: 'kum' }], + ['kusto', { ext: 'csl' }], + ['latex', { ext: 'tex' }], + ['tex', { ext: 'tex' }], + ['context', { ext: 'tex' }], + ['latte', { ext: 'latte' }], + ['less', { ext: 'less' }], + ['lilypond', { ext: 'ly' }], + ['liquid', { ext: 'liquid' }], + ['lisp', { ext: 'lisp' }], + ['livescript', { ext: 'ls' }], + ['llvm', { ext: 'll' }], + ['log', { ext: 'log' }], + ['lolcode', { ext: 'lol' }], + ['lua', { ext: 'lua' }], + ['magma', { ext: 'magma' }], + ['makefile', { ext: 'make' }], + ['markdown', { ext: 'md' }], + ['markup-templating', { ext: 'txt' }], + ['mata', { ext: 'ado' }], + ['matlab', { ext: 'm' }], + ['maxscript', { ext: 'ms' }], + ['mel', { ext: 'mel' }], + ['mermaid', { ext: 'mmd' }], + ['metafont', { ext: 'mf' }], + ['mizar', { ext: 'miz' }], + ['mongodb', { ext: 'txt' }], + ['monkey', { ext: 'monkey' }], + ['moonscript', { ext: 'moon' }], + ['n1ql', { ext: 'txt' }], + ['n4js', { ext: 'n4js' }], + ['nand2tetris-hdl', { ext: 'hdl' }], + ['naniscript', { ext: 'nani' }], + ['nasm', { ext: 'asm' }], + ['neon', { ext: 'neon' }], + ['nevod', { ext: 'txt' }], + ['nginx', { ext: 'conf' }], + ['nim', { ext: 'nim' }], + ['nix', { ext: 'nix' }], + ['nsis', { ext: 'nsi' }], + ['objectivec', { ext: 'm' }], + ['ocaml', { ext: 'ml' }], + ['odin', { ext: 'odin' }], + ['opencl', { ext: 'cl' }], + ['openqasm', { ext: 'qasm' }], + ['oz', { ext: 'oz' }], + ['parigp', { ext: 'gp' }], + ['parser', { ext: 'txt' }], + ['pascal', { ext: 'pas' }], + ['objectpascal', { ext: 'pas' }], + ['pascaligo', { ext: 'ligo' }], + ['psl', { ext: 'psl' }], + ['pcaxis', { ext: 'txt' }], + ['peoplecode', { ext: 'ppl' }], + ['perl', { ext: 'pl' }], + ['php', { ext: 'php' }], + ['phpdoc', { ext: 'txt' }], + ['php-extras', { ext: 'txt' }], + ['plant-uml', { ext: 'txt' }], + ['plsql', { ext: 'pls' }], + ['powerquery', { ext: 'txt' }], + ['powershell', { ext: 'ps1' }], + ['processing', { ext: 'pde' }], + ['prolog', { ext: 'pl' }], + ['promql', { ext: 'promql' }], + ['properties', { ext: 'properties' }], + ['protobuf', { ext: 'proto' }], + ['pug', { ext: 'pug' }], + ['puppet', { ext: 'pp' }], + ['pure', { ext: 'pure' }], + ['purebasic', { ext: 'pb' }], + ['purescript', { ext: 'purs' }], + ['python', { ext: 'py' }], + ['qsharp', { ext: 'qs' }], + ['q', { ext: 'q' }], + ['qml', { ext: 'qml' }], + ['qore', { ext: 'q' }], + ['r', { ext: 'r' }], + ['racket', { ext: 'rkt' }], + ['cshtml', { ext: 'cshtml' }], + ['jsx', { ext: 'jsx' }], + ['tsx', { ext: 'tsx' }], + ['reason', { ext: 're' }], + ['regex', { ext: 'regex' }], + ['rego', { ext: 'rego' }], + ['renpy', { ext: 'rpy' }], + ['rescript', { ext: 'res' }], + ['rest', { ext: 'txt' }], + ['rip', { ext: 'rip' }], + ['roboconf', { ext: 'txt' }], + ['robotframework', { ext: 'robot' }], + ['ruby', { ext: 'rb' }], + ['rust', { ext: 'rs' }], + ['sas', { ext: 'sas' }], + ['sass', { ext: 'sass' }], + ['scss', { ext: 'scss' }], + ['scala', { ext: 'scala' }], + ['scheme', { ext: 'scm' }], + ['shell-session', { ext: 'txt' }], + ['smali', { ext: 'smali' }], + ['smalltalk', { ext: 'st' }], + ['smarty', { ext: 'tpl' }], + ['sml', { ext: 'sml' }], + ['smlnj', { ext: 'sml' }], + ['solidity', { ext: 'sol' }], + ['solution-file', { ext: 'sln' }], + ['soy', { ext: 'soy' }], + ['sparql', { ext: 'rq' }], + ['splunk-spl', { ext: 'spl' }], + ['sqf', { ext: 'sqf' }], + ['sql', { ext: 'sql' }], + ['squirrel', { ext: 'nut' }], + ['stan', { ext: 'stan' }], + ['stata', { ext: 'do' }], + ['iecst', { ext: 'st' }], + ['stylus', { ext: 'styl' }], + ['supercollider', { ext: 'sc' }], + ['swift', { ext: 'swift' }], + ['systemd', { ext: 'service' }], + ['t4-templating', { ext: 't4' }], + ['t4-cs', { ext: 't4' }], + ['t4-vb', { ext: 't4' }], + ['tap', { ext: 'tap' }], + ['tcl', { ext: 'tcl' }], + ['tt2', { ext: 'tt2' }], + ['textile', { ext: 'textile' }], + ['toml', { ext: 'toml' }], + ['tremor', { ext: 'trickle' }], + ['trickle', { ext: 'trickle' }], + ['troy', { ext: 'troy' }], + ['turtle', { ext: 'ttl' }], + ['trig', { ext: 'trig' }], + ['twig', { ext: 'twig' }], + ['typescript', { ext: 'ts' }], + ['typoscript', { ext: 'ts' }], + ['tsconfig', { ext: 'json' }], + ['unrealscript', { ext: 'uc' }], + ['uorazor', { ext: 'txt' }], + ['uri', { ext: 'uri' }], + ['url', { ext: 'url' }], + ['v', { ext: 'v' }], + ['vala', { ext: 'vala' }], + ['vbnet', { ext: 'vb' }], + ['velocity', { ext: 'vm' }], + ['verilog', { ext: 'v' }], + ['vhdl', { ext: 'vhd' }], + ['vim', { ext: 'vim' }], + ['visual-basic', { ext: 'vb' }], + ['vba', { ext: 'bas' }], + ['warpscript', { ext: 'warpscript' }], + ['wasm', { ext: 'wasm' }], + ['web-idl', { ext: 'idl' }], + ['wgsl', { ext: 'wgsl' }], + ['wiki', { ext: 'wiki' }], + ['wolfram', { ext: 'wl' }], + ['mathematica', { ext: 'nb' }], + ['nb', { ext: 'nb' }], + ['wren', { ext: 'wren' }], + ['xeora', { ext: 'x' }], + ['xeoracube', { ext: 'txt' }], + ['xml-doc', { ext: 'xml' }], + ['xojo', { ext: 'xojo_code' }], + ['xquery', { ext: 'xq' }], + ['yaml', { ext: 'yml' }], + ['yang', { ext: 'yang' }], + ['zig', { ext: 'zig' }], ]); diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index fbbacd5..da8f1f5 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -5,65 +5,74 @@ import type { Cookies } from '@sveltejs/kit'; import { nanoid } from 'nanoid'; export const getUserIdFromCookie = async (cookies: Cookies) => { - const token = cookies.get('token'); - if (!token) return null; + const token = cookies.get('token'); + if (!token) return null; - const authToken = await prisma.authToken.findFirst({ - where: { token, expiresAt: { gt: new Date() } }, - include: { user: { select: { id: true, verified: true } } } - }); - if (!authToken) return null; - if (!authToken.user.verified) return null; + const authToken = await prisma.authToken.findFirst({ + where: { token, expiresAt: { gt: new Date() } }, + include: { user: { select: { id: true, verified: true } } }, + }); + if (!authToken) return null; + if (!authToken.user.verified) return null; - return authToken.user.id; + return authToken.user.id; }; export const generateVerificationHash = async (userId: string) => { - const user = await prisma.user.findUnique({ where: { id: userId } }); - if (!user) throw new Error('User not found'); + const user = await prisma.user.findUnique({ where: { id: userId } }); + if (!user) throw new Error('User not found'); - const hash = await hashPassword(`${user.email}${user.id}${user.password}${user.verified}`, env.SALT); - return hash; + const hash = await hashPassword( + `${user.email}${user.id}${user.password}${user.verified}`, + env.SALT, + ); + return hash; }; -export const validateVerificationHash = async (userId: string, hash: string) => { - const user = await prisma.user.findUnique({ where: { id: userId } }); - if (!user) return false; +export const validateVerificationHash = async ( + userId: string, + hash: string, +) => { + const user = await prisma.user.findUnique({ where: { id: userId } }); + if (!user) return false; - const newHash = await hashPassword( - `${user.email}${user.id}${user.password}${user.verified}`, - env.SALT - ); - if (newHash !== hash) return false; + const newHash = await hashPassword( + `${user.email}${user.id}${user.password}${user.verified}`, + env.SALT, + ); + if (newHash !== hash) return false; - await prisma.user.update({ where: { id: userId }, data: { verified: true } }); - return true; + await prisma.user.update({ + where: { id: userId }, + data: { verified: true }, + }); + return true; }; export const generatePasswordResetToken = async (userId: string) => { - const user = await prisma.user.findUnique({ where: { id: userId } }); - if (!user) return false; + const user = await prisma.user.findUnique({ where: { id: userId } }); + if (!user) return false; - const resetToken = await prisma.resetToken.upsert({ - where: { - userId: user.id - }, - update: { - createdAt: new Date(), - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days - token: nanoid(32) - }, - create: { - user: { - connect: { - id: user.id - } - }, - createdAt: new Date(), - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days - token: nanoid(32) - } - }); + const resetToken = await prisma.resetToken.upsert({ + where: { + userId: user.id, + }, + update: { + createdAt: new Date(), + expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days + token: nanoid(32), + }, + create: { + user: { + connect: { + id: user.id, + }, + }, + createdAt: new Date(), + expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days + token: nanoid(32), + }, + }); - return resetToken.token; -}; \ No newline at end of file + return resetToken.token; +}; diff --git a/src/lib/server/email/base.ts b/src/lib/server/email/base.ts index 1ad9485..d242eca 100644 --- a/src/lib/server/email/base.ts +++ b/src/lib/server/email/base.ts @@ -2,30 +2,30 @@ import nodemailer from 'nodemailer'; import { env } from '$env/dynamic/private'; export async function sendEmail(to: string, subject: string, content: string) { - if (env.MAIL_ENABLED !== 'true') { - return false; - } + if (env.MAIL_ENABLED !== 'true') { + return false; + } - const transporter = nodemailer.createTransport({ - host: env.MAIL_SERVER, - port: Number(env.MAIL_PORT), - secure: env.MAIL_USE_SSL === 'true', - auth: { - user: env.MAIL_USERNAME, - pass: env.MAIL_PASSWORD - } - }); + const transporter = nodemailer.createTransport({ + host: env.MAIL_SERVER, + port: Number(env.MAIL_PORT), + secure: env.MAIL_USE_SSL === 'true', + auth: { + user: env.MAIL_USERNAME, + pass: env.MAIL_PASSWORD, + }, + }); - const info = await transporter.sendMail({ - from: env.MAIL_FROM, - to, - subject, - text: content - }); + const info = await transporter.sendMail({ + from: env.MAIL_FROM, + to, + subject, + text: content, + }); - if (info.accepted.length === 0) { - return false; - } + if (info.accepted.length === 0) { + return false; + } - return true; + return true; } diff --git a/src/lib/server/email/reset-password.ts b/src/lib/server/email/reset-password.ts index b09913d..176c391 100644 --- a/src/lib/server/email/reset-password.ts +++ b/src/lib/server/email/reset-password.ts @@ -4,23 +4,25 @@ import { generatePasswordResetToken } from '../auth'; import { sendEmail } from './base'; export const sendResetEmail = async (userId: string) => { - const user = await prisma.user.findUnique({ where: { id: userId } }); - if (!user) return false; + const user = await prisma.user.findUnique({ where: { id: userId } }); + if (!user) return false; - const resetToken = await generatePasswordResetToken(userId); + const resetToken = await generatePasswordResetToken(userId); - const verifyUrl = `${env.PUBLIC_URL}/reset-password?token=${encodeURIComponent( - resetToken - )}&userId=${encodeURIComponent(userId)}`; + const verifyUrl = `${ + env.PUBLIC_URL + }/reset-password?token=${encodeURIComponent( + resetToken, + )}&userId=${encodeURIComponent(userId)}`; - const content = `To reset your password, please click the following link: ${verifyUrl} + const content = `To reset your password, please click the following link: ${verifyUrl} If you did not make this request, please disgregard this email.`; - const subject = 'YABin: Password reset request'; + const subject = 'YABin: Password reset request'; - const sent = await sendEmail(user.email, subject, content); - if (!sent) return false; + const sent = await sendEmail(user.email, subject, content); + if (!sent) return false; - return true; + return true; }; diff --git a/src/lib/server/email/verify.ts b/src/lib/server/email/verify.ts index a585536..0a1426b 100644 --- a/src/lib/server/email/verify.ts +++ b/src/lib/server/email/verify.ts @@ -4,20 +4,20 @@ import { generateVerificationHash } from '../auth'; import { sendEmail } from './base'; export const sendVerificationEmail = async (userId: string) => { - const user = await prisma.user.findUnique({ where: { id: userId } }); - if (!user) return false; + const user = await prisma.user.findUnique({ where: { id: userId } }); + if (!user) return false; - const hash = await generateVerificationHash(userId); + const hash = await generateVerificationHash(userId); - const verifyUrl = `${env.PUBLIC_URL}/validate?hash=${encodeURIComponent( - hash - )}&userId=${encodeURIComponent(userId)}`; + const verifyUrl = `${env.PUBLIC_URL}/validate?hash=${encodeURIComponent( + hash, + )}&userId=${encodeURIComponent(userId)}`; - const content = `To verify your email, please click the following link: ${verifyUrl}`; - const subject = 'YABin: Verify your email'; + const content = `To verify your email, please click the following link: ${verifyUrl}`; + const subject = 'YABin: Verify your email'; - const sent = await sendEmail(user.email, subject, content); - if (!sent) return false; + const sent = await sendEmail(user.email, subject, content); + if (!sent) return false; - return true; + return true; }; diff --git a/src/lib/server/services.ts b/src/lib/server/services.ts index 883ff2c..8f13994 100644 --- a/src/lib/server/services.ts +++ b/src/lib/server/services.ts @@ -2,44 +2,59 @@ import { error } from '@sveltejs/kit'; import prisma from '@db'; export async function getPaste(key: string) { - let data = await prisma.paste.findUnique({ - where: { key }, - select: { - content: true, - encrypted: true, - passwordProtected: true, - initVector: true, - language: true, - expiresCount: true, - readCount: true, - ownerId: true - } - }); + let data = await prisma.paste.findUnique({ + where: { key }, + select: { + content: true, + encrypted: true, + passwordProtected: true, + initVector: true, + language: true, + expiresCount: true, + readCount: true, + ownerId: true, + }, + }); - if (!data) throw error(404, 'Not found'); + if (!data) throw error(404, 'Not found'); - data = await prisma.paste.update({ - where: { key }, - data: { readCount: { increment: 1 } } - }); + data = await prisma.paste.update({ + where: { key }, + data: { readCount: { increment: 1 } }, + }); - const { expiresCount, readCount } = data; - if (expiresCount !== null && expiresCount < readCount) { - await prisma.paste.delete({ where: { key } }); - throw error(404, 'Not found'); - } + const { expiresCount, readCount } = data; + if (expiresCount !== null && expiresCount < readCount) { + await prisma.paste.delete({ where: { key } }); + throw error(404, 'Not found'); + } - const { content, encrypted, passwordProtected, initVector, language, ownerId } = data; + const { + content, + encrypted, + passwordProtected, + initVector, + language, + ownerId, + } = data; - return { key, content, encrypted, passwordProtected, initVector, language, ownerId }; + return { + key, + content, + encrypted, + passwordProtected, + initVector, + language, + ownerId, + }; } export async function deleteExpiredPastes() { - await prisma.paste.deleteMany({ - where: { - expiresAt: { - lt: new Date() - } - } - }); + await prisma.paste.deleteMany({ + where: { + expiresAt: { + lt: new Date(), + }, + }, + }); } diff --git a/src/lib/server/validate.ts b/src/lib/server/validate.ts index 4c4bceb..071c134 100644 --- a/src/lib/server/validate.ts +++ b/src/lib/server/validate.ts @@ -4,33 +4,35 @@ const nameLength = 50; const usernameLength = 50; export function validateEmail(email: FormDataEntryValue) { - if (emailRegex.test(email?.toString())) { - return true; - } else { - throw new Error('Invalid email address'); - } + if (emailRegex.test(email?.toString())) { + return true; + } else { + throw new Error('Invalid email address'); + } } export function validatePassword(password: FormDataEntryValue) { - if (password.toString().length >= passwordLength) { - return true; - } else { - throw new Error(`Password must be at least ${passwordLength} characters long`); - } + if (password.toString().length >= passwordLength) { + return true; + } else { + throw new Error( + `Password must be at least ${passwordLength} characters long`, + ); + } } export function validateName(name: FormDataEntryValue) { - if (name.toString().length <= nameLength) { - return true; - } else { - throw new Error(`Name is too long`); - } + if (name.toString().length <= nameLength) { + return true; + } else { + throw new Error(`Name is too long`); + } } export function validateUsername(username: FormDataEntryValue) { - if (username.toString().length <= usernameLength) { - return true; - } else { - throw new Error(`Username is too long`); - } + if (username.toString().length <= usernameLength) { + return true; + } else { + throw new Error(`Username is too long`); + } } diff --git a/src/lib/types.ts b/src/lib/types.ts index 6c75b80..618fb5b 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,45 +1,45 @@ export interface PasteConfig { - language?: string; - encrypted?: boolean; - expiresAfter?: number; - burnAfterRead?: boolean; - customPath?: string; + language?: string; + encrypted?: boolean; + expiresAfter?: number; + burnAfterRead?: boolean; + customPath?: string; } export interface Paste { - content: string; - config?: PasteConfig; - passwordProtected?: boolean; - initVector?: string; + content: string; + config?: PasteConfig; + passwordProtected?: boolean; + initVector?: string; } export interface PastePatch { - key: string; - content: string; - encrypted?: boolean; - initVector?: string; + key: string; + content: string; + encrypted?: boolean; + initVector?: string; } export interface PasteCreateResponse { - success: boolean; - data?: { - key: string; - }; - error?: string; + success: boolean; + data?: { + key: string; + }; + error?: string; } export interface PastePatchResponse { - success: boolean; - data?: { - key: string; - }; - error?: string; + success: boolean; + data?: { + key: string; + }; + error?: string; } export interface UserSettings { - defaults?: { - encrypted?: boolean; - burnAfterRead?: boolean; - expiresAfterSeconds?: number; - }; + defaults?: { + encrypted?: boolean; + burnAfterRead?: boolean; + expiresAfterSeconds?: number; + }; } diff --git a/src/lib/utils/time.ts b/src/lib/utils/time.ts index 3d23de6..5377677 100644 --- a/src/lib/utils/time.ts +++ b/src/lib/utils/time.ts @@ -1,18 +1,18 @@ export function secondsToDHM(seconds: number) { - const days = Math.floor(seconds / (3600 * 24)); - const hours = Math.floor((seconds % (3600 * 24)) / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - return { days, hours, minutes }; + const days = Math.floor(seconds / (3600 * 24)); + const hours = Math.floor((seconds % (3600 * 24)) / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + return { days, hours, minutes }; } export function DHMToSeconds({ - days, - hours, - minutes + days, + hours, + minutes, }: { - days?: number; - hours?: number; - minutes?: number; + days?: number; + hours?: number; + minutes?: number; }) { - return (days ?? 0) * 3600 * 24 + (hours ?? 0) * 3600 + (minutes ?? 0) * 60; + return (days ?? 0) * 3600 * 24 + (hours ?? 0) * 3600 + (minutes ?? 0) * 60; } diff --git a/src/routes/(auth)/+layout.svelte b/src/routes/(auth)/+layout.svelte index 32adb0c..45756dc 100644 --- a/src/routes/(auth)/+layout.svelte +++ b/src/routes/(auth)/+layout.svelte @@ -1,50 +1,50 @@
-
-
-

YABin

+
+
+

YABin

- + - -
-
+ +
+
- +
diff --git a/src/routes/(auth)/forgot-password/+page.server.ts b/src/routes/(auth)/forgot-password/+page.server.ts index 32e6030..5b94fc4 100644 --- a/src/routes/(auth)/forgot-password/+page.server.ts +++ b/src/routes/(auth)/forgot-password/+page.server.ts @@ -5,36 +5,48 @@ import { env } from '$env/dynamic/private'; import { sendResetEmail } from '$lib/server/email/reset-password'; export const load: PageServerLoad = async () => { - if (env.MAIL_ENABLED === 'false') { - throw redirect(303, '/'); - } + if (env.MAIL_ENABLED === 'false') { + throw redirect(303, '/'); + } }; export const actions: Actions = { - default: async ({ request }) => { - if (env.MAIL_ENABLED !== 'true') { - return fail(400, { success: false, errors: ['E-mail is disabled'] }); - } - - const data = await request.formData(); - - const usernameOrEmail = data.get('username-email'); - - if (!usernameOrEmail) { - return fail(400, { success: false, errors: ['All fields are required'] }); - } - - const user = await prisma.user.findFirst({ - where: { - OR: [{ username: usernameOrEmail.toString() }, { email: usernameOrEmail.toString() }] - } - }); - - if (user) { - sendResetEmail(user.id); - } - - // Return success regardless of whether username/email is found or not - return { success: true, message: 'Please check e-mail for a password reset link' }; - } + default: async ({ request }) => { + if (env.MAIL_ENABLED !== 'true') { + return fail(400, { + success: false, + errors: ['E-mail is disabled'], + }); + } + + const data = await request.formData(); + + const usernameOrEmail = data.get('username-email'); + + if (!usernameOrEmail) { + return fail(400, { + success: false, + errors: ['All fields are required'], + }); + } + + const user = await prisma.user.findFirst({ + where: { + OR: [ + { username: usernameOrEmail.toString() }, + { email: usernameOrEmail.toString() }, + ], + }, + }); + + if (user) { + sendResetEmail(user.id); + } + + // Return success regardless of whether username/email is found or not + return { + success: true, + message: 'Please check e-mail for a password reset link', + }; + }, }; diff --git a/src/routes/(auth)/forgot-password/+page.svelte b/src/routes/(auth)/forgot-password/+page.svelte index 0565253..f65608c 100644 --- a/src/routes/(auth)/forgot-password/+page.svelte +++ b/src/routes/(auth)/forgot-password/+page.svelte @@ -1,34 +1,43 @@
-

Reset Password

-
- {#if form?.errors} -
    - {#each form.errors as error} -
  • {error}
  • - {/each} -
- {/if} - {#if form?.success} -
{form?.message}
- {/if} +

Reset Password

+
+ {#if form?.errors} +
    + {#each form.errors as error} +
  • {error}
  • + {/each} +
+ {/if} + {#if form?.success} +
{form?.message}
+ {/if} -
-
- - -
+ +
+ + +
-
- -
- -
-
+
+ +
+ +
diff --git a/src/routes/(auth)/login/+page.server.ts b/src/routes/(auth)/login/+page.server.ts index f62e6d3..8418345 100644 --- a/src/routes/(auth)/login/+page.server.ts +++ b/src/routes/(auth)/login/+page.server.ts @@ -1,4 +1,4 @@ -import type { Actions,PageServerLoad } from './$types'; +import type { Actions, PageServerLoad } from './$types'; import { fail, redirect } from '@sveltejs/kit'; import prisma from '@db'; import { hashPassword } from '$lib/crypto'; @@ -6,58 +6,78 @@ import { nanoid } from 'nanoid'; import { env } from '$env/dynamic/private'; import { getUserIdFromCookie } from '$lib/server/auth'; -export const load:PageServerLoad = async({cookies})=>{ - const userId = await getUserIdFromCookie(cookies); - if (userId) throw redirect(303, '/'); -} +export const load: PageServerLoad = async ({ cookies }) => { + const userId = await getUserIdFromCookie(cookies); + if (userId) throw redirect(303, '/'); +}; export const actions: Actions = { - default: async ({ cookies, request }) => { - const data = await request.formData(); - - const usernameOrEmail = data.get('username-email'); - const password = data.get('password'); - - if (!usernameOrEmail || !password) { - return fail(400, { success: false, errors: ['All fields are required'] }); - } - - const hashedPassword = await hashPassword(password.toString(), env.SALT); - const user = await prisma.user.findFirst({ - where: { - OR: [ - { username: usernameOrEmail.toString(), password: hashedPassword }, - { email: usernameOrEmail.toString(), password: hashedPassword } - ] - } - }); - - if (!user) { - return fail(400, { success: false, errors: ['Invalid username or password'] }); - } - - if (!user.verified) { - return fail(401, { success: false, errors: ['Account not verified'] }); - } - - await prisma.authToken.deleteMany({ where: { expiresAt: { lte: new Date() } } }); - - const authToken = await prisma.authToken.create({ - data: { - user: { connect: { id: user.id } }, - createdAt: new Date(), - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days - token: nanoid(32) - } - }); - - cookies.set('token', authToken.token, { - path: '/', - maxAge: 60 * 60 * 24 * 30, // 30 days - secure: true, - httpOnly: true, - sameSite: 'strict' - }); - - throw redirect(303, '/'); - } + default: async ({ cookies, request }) => { + const data = await request.formData(); + + const usernameOrEmail = data.get('username-email'); + const password = data.get('password'); + + if (!usernameOrEmail || !password) { + return fail(400, { + success: false, + errors: ['All fields are required'], + }); + } + + const hashedPassword = await hashPassword( + password.toString(), + env.SALT, + ); + const user = await prisma.user.findFirst({ + where: { + OR: [ + { + username: usernameOrEmail.toString(), + password: hashedPassword, + }, + { + email: usernameOrEmail.toString(), + password: hashedPassword, + }, + ], + }, + }); + + if (!user) { + return fail(400, { + success: false, + errors: ['Invalid username or password'], + }); + } + + if (!user.verified) { + return fail(401, { + success: false, + errors: ['Account not verified'], + }); + } + + await prisma.authToken.deleteMany({ + where: { expiresAt: { lte: new Date() } }, + }); + + const authToken = await prisma.authToken.create({ + data: { + user: { connect: { id: user.id } }, + createdAt: new Date(), + expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days + token: nanoid(32), + }, + }); + + cookies.set('token', authToken.token, { + path: '/', + maxAge: 60 * 60 * 24 * 30, // 30 days + secure: true, + httpOnly: true, + sameSite: 'strict', + }); + + throw redirect(303, '/'); + }, }; diff --git a/src/routes/(auth)/login/+page.svelte b/src/routes/(auth)/login/+page.svelte index 9c36273..d9a076d 100644 --- a/src/routes/(auth)/login/+page.svelte +++ b/src/routes/(auth)/login/+page.svelte @@ -1,48 +1,67 @@
-

User Login

-
- {#if form?.errors} -
    - {#each form.errors as error} -
  • {error}
  • - {/each} -
- {/if} - {#if form?.success} -
Success, redirecting...
- {/if} +

User Login

+
+ {#if form?.errors} +
    + {#each form.errors as error} +
  • {error}
  • + {/each} +
+ {/if} + {#if form?.success} +
+ Success, redirecting... +
+ {/if} -
-
- - -
-
- - -
+ +
+ + +
+
+ + +
-
- - Don't have an account? Register. - +
+ + Don't have an account? Register. + - -
+ +
-
- Forgot password? - Click here. -
-
-
+
+ Forgot password? + Click here. +
+ +
diff --git a/src/routes/(auth)/logout/+page.server.ts b/src/routes/(auth)/logout/+page.server.ts index 089ff75..4372c5d 100644 --- a/src/routes/(auth)/logout/+page.server.ts +++ b/src/routes/(auth)/logout/+page.server.ts @@ -2,13 +2,13 @@ import { redirect, type Actions } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ cookies }) => { - cookies.delete('token'); - throw redirect(303, '/'); + cookies.delete('token'); + throw redirect(303, '/'); }; export const actions: Actions = { - default({ cookies }) { - cookies.delete('token'); - throw redirect(303, '/'); - } + default({ cookies }) { + cookies.delete('token'); + throw redirect(303, '/'); + }, }; diff --git a/src/routes/(auth)/register/+page.server.ts b/src/routes/(auth)/register/+page.server.ts index 03ff3a6..a328fab 100644 --- a/src/routes/(auth)/register/+page.server.ts +++ b/src/routes/(auth)/register/+page.server.ts @@ -7,119 +7,131 @@ import { env } from '$env/dynamic/private'; import { env as envPublic } from '$env/dynamic/public'; import { sendVerificationEmail } from '$lib/server/email/verify'; import { - validateEmail, - validatePassword, - validateName, - validateUsername + validateEmail, + validatePassword, + validateName, + validateUsername, } from '$lib/server/validate'; export const actions: Actions = { - default: async ({ cookies, request }) => { - if (envPublic.PUBLIC_REGISRATION_ENABLED !== 'true') { - return fail(404, { success: false, errors: ['Not found'] }); - } - - const data = await request.formData(); - - const name = data.get('name'); - const username = data.get('username'); - const email = data.get('email'); - const password = data.get('password'); - const cnfPassword = data.get('confirm-password'); - - const errors: string[] = []; - - if (!name || !username || !email || !password || !cnfPassword) { - errors.push('All fields are required'); - } - - try { - if (email) validateEmail(email); - } catch (e: any) { - errors.push(e.message); - } - - try { - if (password) validatePassword(password); - } catch (e: any) { - errors.push(e.message); - } - - try { - if (name) validateName(name); - } catch (e: any) { - errors.push(e.message); - } - - try { - if (username) validateUsername(username); - } catch (e: any) { - errors.push(e.message); - } - - if (password && password !== cnfPassword) { - errors.push('Passwords do not match'); - } - - if (username && email) { - const existingCount = await prisma.user.count({ - where: { - OR: [{ username: username.toString() }, { email: email.toString() }] - } - }); - if (existingCount > 0) { - errors.push('Username or email already exists'); - } - } - - if (errors.length > 0) { - return fail(400, { success: false, errors }); - } - - if (name && username && email && password) { - const user = await prisma.user.create({ - data: { - name: name.toString(), - username: username.toString(), - email: email.toString(), - password: await hashPassword(password.toString(), env.SALT), - verified: false - } - }); - - if (env.MAIL_ENABLED === 'true') { - const sentVerificationEmail = await sendVerificationEmail(user.id); - if (sentVerificationEmail) { - return { success: true, message: 'Please check your e-mail for verification link' }; - } - } - - await prisma.user.update({ where: { id: user.id }, data: { verified: true } }); - - const authToken = await prisma.authToken.create({ - data: { - user: { - connect: { - id: user.id - } - }, - createdAt: new Date(), - expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days - token: nanoid(32) - } - }); - - cookies.set('token', authToken.token, { - path: '/', - maxAge: 60 * 60 * 24 * 30, // 30 days - secure: true, - httpOnly: true, - sameSite: 'strict' - }); - - throw redirect(303, '/'); - } - - return { success: false, errors: ['Unknown error'] }; - } + default: async ({ cookies, request }) => { + if (envPublic.PUBLIC_REGISRATION_ENABLED !== 'true') { + return fail(404, { success: false, errors: ['Not found'] }); + } + + const data = await request.formData(); + + const name = data.get('name'); + const username = data.get('username'); + const email = data.get('email'); + const password = data.get('password'); + const cnfPassword = data.get('confirm-password'); + + const errors: string[] = []; + + if (!name || !username || !email || !password || !cnfPassword) { + errors.push('All fields are required'); + } + + try { + if (email) validateEmail(email); + } catch (e: any) { + errors.push(e.message); + } + + try { + if (password) validatePassword(password); + } catch (e: any) { + errors.push(e.message); + } + + try { + if (name) validateName(name); + } catch (e: any) { + errors.push(e.message); + } + + try { + if (username) validateUsername(username); + } catch (e: any) { + errors.push(e.message); + } + + if (password && password !== cnfPassword) { + errors.push('Passwords do not match'); + } + + if (username && email) { + const existingCount = await prisma.user.count({ + where: { + OR: [ + { username: username.toString() }, + { email: email.toString() }, + ], + }, + }); + if (existingCount > 0) { + errors.push('Username or email already exists'); + } + } + + if (errors.length > 0) { + return fail(400, { success: false, errors }); + } + + if (name && username && email && password) { + const user = await prisma.user.create({ + data: { + name: name.toString(), + username: username.toString(), + email: email.toString(), + password: await hashPassword(password.toString(), env.SALT), + verified: false, + }, + }); + + if (env.MAIL_ENABLED === 'true') { + const sentVerificationEmail = await sendVerificationEmail( + user.id, + ); + if (sentVerificationEmail) { + return { + success: true, + message: + 'Please check your e-mail for verification link', + }; + } + } + + await prisma.user.update({ + where: { id: user.id }, + data: { verified: true }, + }); + + const authToken = await prisma.authToken.create({ + data: { + user: { + connect: { + id: user.id, + }, + }, + createdAt: new Date(), + expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30), // 30 days + token: nanoid(32), + }, + }); + + cookies.set('token', authToken.token, { + path: '/', + maxAge: 60 * 60 * 24 * 30, // 30 days + secure: true, + httpOnly: true, + sameSite: 'strict', + }); + + throw redirect(303, '/'); + } + + return { success: false, errors: ['Unknown error'] }; + }, }; diff --git a/src/routes/(auth)/register/+page.svelte b/src/routes/(auth)/register/+page.svelte index 4f92f83..869134d 100644 --- a/src/routes/(auth)/register/+page.svelte +++ b/src/routes/(auth)/register/+page.svelte @@ -1,83 +1,94 @@
-

New User Registration

-
- {#if form?.errors} -
    - {#each form.errors as error} -
  • {error}
  • - {/each} -
- {/if} - {#if form?.success} -
{form.message}
- {/if} +

New User Registration

+
+ {#if form?.errors} +
    + {#each form.errors as error} +
  • {error}
  • + {/each} +
+ {/if} + {#if form?.success} +
{form.message}
+ {/if} - {#if env.PUBLIC_REGISRATION_ENABLED == 'true'} -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
+ {#if env.PUBLIC_REGISRATION_ENABLED == 'true'} + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
-
- - Already have an account? Login. - +
+ + Already have an account? Login. + - -
- - {:else} -

Registration has been disabled.

- {/if} -
+ +
+ + {:else} +

Registration has been disabled.

+ {/if} +
diff --git a/src/routes/(auth)/reset-password/+page.server.ts b/src/routes/(auth)/reset-password/+page.server.ts index 7ba4ec7..e5f5d09 100644 --- a/src/routes/(auth)/reset-password/+page.server.ts +++ b/src/routes/(auth)/reset-password/+page.server.ts @@ -7,71 +7,82 @@ import { env } from '$env/dynamic/private'; import { validatePassword } from '$lib/server/validate'; export const load: PageServerLoad = async ({ url }) => { - const userId = url.searchParams.get('userId'); - const token = url.searchParams.get('token'); + const userId = url.searchParams.get('userId'); + const token = url.searchParams.get('token'); - if (!userId || !token) { - throw error(404, 'Not found'); - } + if (!userId || !token) { + throw error(404, 'Not found'); + } - const user = await prisma.user.findUnique({ where: { id: userId } }); - const resetToken = await prisma.resetToken.findUnique({ where: { token: token } }); + const user = await prisma.user.findUnique({ where: { id: userId } }); + const resetToken = await prisma.resetToken.findUnique({ + where: { token: token }, + }); - if (!user || !resetToken) { - throw error(404, 'Not found'); - } + if (!user || !resetToken) { + throw error(404, 'Not found'); + } - if (resetToken.expiresAt <= new Date()) { - throw error(404, 'Expired link'); - } + if (resetToken.expiresAt <= new Date()) { + throw error(404, 'Expired link'); + } }; export const actions: Actions = { - default: async ({ url, request }) => { - const userId = url.searchParams.get('userId'); - if (!userId) throw error(404, 'Not found'); + default: async ({ url, request }) => { + const userId = url.searchParams.get('userId'); + if (!userId) throw error(404, 'Not found'); - const user = await prisma.user.findUnique({ where: { id: userId } }); - if (!user) throw error(404, 'Not found'); + const user = await prisma.user.findUnique({ where: { id: userId } }); + if (!user) throw error(404, 'Not found'); - const data = await request.formData(); - const newPassword = data.get('new-password'); - const cnfPassword = data.get('confirm-password'); - const errors: string[] = []; + const data = await request.formData(); + const newPassword = data.get('new-password'); + const cnfPassword = data.get('confirm-password'); + const errors: string[] = []; - if (!newPassword || !cnfPassword) { - errors.push('All fields are required'); - } + if (!newPassword || !cnfPassword) { + errors.push('All fields are required'); + } - if (newPassword !== cnfPassword) { - errors.push('Passwords need to match'); - } + if (newPassword !== cnfPassword) { + errors.push('Passwords need to match'); + } - try { - if (newPassword) validatePassword(newPassword); - } catch (e: any) { - errors.push(e.message); - } + try { + if (newPassword) validatePassword(newPassword); + } catch (e: any) { + errors.push(e.message); + } - if (newPassword) { - const oldPasswordHash = user.password; - const newPasswordHash = await hashPassword(newPassword.toString(), env.SALT); - if (oldPasswordHash === newPasswordHash) { - errors.push('Cannot use existing password'); - } - } + if (newPassword) { + const oldPasswordHash = user.password; + const newPasswordHash = await hashPassword( + newPassword.toString(), + env.SALT, + ); + if (oldPasswordHash === newPasswordHash) { + errors.push('Cannot use existing password'); + } + } - if (errors.length > 0) { - return fail(400, { success: false, errors }); - } + if (errors.length > 0) { + return fail(400, { success: false, errors }); + } - if (newPassword && cnfPassword) { - const newPasswordHash = await hashPassword(newPassword.toString(), env.SALT); - await prisma.user.update({ where: { id: userId }, data: { password: newPasswordHash } }); - await prisma.resetToken.delete({ where: { userId: userId } }); - throw redirect(303, '/login'); - } + if (newPassword && cnfPassword) { + const newPasswordHash = await hashPassword( + newPassword.toString(), + env.SALT, + ); + await prisma.user.update({ + where: { id: userId }, + data: { password: newPasswordHash }, + }); + await prisma.resetToken.delete({ where: { userId: userId } }); + throw redirect(303, '/login'); + } - return { success: false, errors: ['Unknown error'] }; - } + return { success: false, errors: ['Unknown error'] }; + }, }; diff --git a/src/routes/(auth)/reset-password/+page.svelte b/src/routes/(auth)/reset-password/+page.svelte index 18b1d13..054c753 100644 --- a/src/routes/(auth)/reset-password/+page.svelte +++ b/src/routes/(auth)/reset-password/+page.svelte @@ -1,47 +1,51 @@
-

Change Password

-
- {#if form?.errors} -
    - {#each form.errors as error} -
  • {error}
  • - {/each} -
- {/if} +

Change Password

+
+ {#if form?.errors} +
    + {#each form.errors as error} +
  • {error}
  • + {/each} +
+ {/if} -
-
- - -
+ +
+ + +
-
- - -
+
+ + +
-
- -
-
-
+
+ +
+ +
diff --git a/src/routes/(auth)/validate/+page.server.ts b/src/routes/(auth)/validate/+page.server.ts index 396cceb..bf51453 100644 --- a/src/routes/(auth)/validate/+page.server.ts +++ b/src/routes/(auth)/validate/+page.server.ts @@ -3,18 +3,21 @@ import type { PageServerLoad } from './$types'; import { validateVerificationHash } from '$lib/server/auth'; export const load: PageServerLoad = async ({ url }) => { - const userId = url.searchParams.get('userId'); - const hash = url.searchParams.get('hash'); + const userId = url.searchParams.get('userId'); + const hash = url.searchParams.get('hash'); - if (!userId || !hash) { - throw error(404, 'Not found'); - } + if (!userId || !hash) { + throw error(404, 'Not found'); + } - const isValid = await validateVerificationHash(decodeURIComponent(userId), decodeURIComponent(hash)); + const isValid = await validateVerificationHash( + decodeURIComponent(userId), + decodeURIComponent(hash), + ); - if (!isValid) { - throw error(404, 'Not found'); - } + if (!isValid) { + throw error(404, 'Not found'); + } - throw redirect(303, '/login'); + throw redirect(303, '/login'); }; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 2e511e0..3649e9a 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,5 +1,5 @@ diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 9e266e1..c31eb65 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -3,17 +3,17 @@ import type { UserSettings } from '$lib/types'; import prisma from '@db'; export async function load({ cookies }) { - const userId = await getUserIdFromCookie(cookies); + const userId = await getUserIdFromCookie(cookies); - let settings: UserSettings | undefined; + let settings: UserSettings | undefined; - if (userId) { - const user = await prisma.user.findUnique({ - where: { id: userId }, - select: { settings: true } - }); - settings = user?.settings as UserSettings; - } + if (userId) { + const user = await prisma.user.findUnique({ + where: { id: userId }, + select: { settings: true }, + }); + settings = user?.settings as UserSettings; + } - return { loggedIn: !!userId, settings }; + return { loggedIn: !!userId, settings }; } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 6fbeff9..0cfa9db 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,344 +1,398 @@
-

YABin

+

YABin

- + - +
-
-