diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..992f00f --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm install -g pnpm && pnpm install + - name: Install Playwright Browsers + run: pnpm exec playwright install --with-deps + - name: Run Playwright tests + run: pnpm exec playwright test + - uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 9044b40..397e161 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,9 @@ gitignore # System Files .DS_Store Thumbs.db + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/README.md b/README.md index 2a865a3..5618e6f 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ To get started with li'l Judd, follow the instructions below. VITE_DISCORD_CLIENT_ID=your_discord_client_id VITE_DISCORD_CLIENT_SECRET=your_discord_client_secret VITE_DISCORD_BOT_TOKEN=your_discord_bot_token - VITE_DISCORD_BOT_PERMISSIONS=18977581952080 + VITE_DISCORD_OAUTH2_PERMISSIONS=18977581952080 VITE_AUTH_SECRET=your_auth_secret diff --git a/discord_client_testing.http b/discord_client_testing.http index 523e72f..0d30605 100644 --- a/discord_client_testing.http +++ b/discord_client_testing.http @@ -3,6 +3,11 @@ Authorization: Bearer {{$dotenv DISCORD_ACCESS_TOKEN}} ### +GET https://discord.com/api/users/@me +Authorization: Bot {{$dotenv DISCORD_BOT_TOKEN}} + +### + GET https://discord.com/api/users/@me/guilds Authorization: Bearer {{$dotenv DISCORD_ACCESS_TOKEN}} diff --git a/e2e/auth.spec.ts b/e2e/auth.spec.ts new file mode 100644 index 0000000..8938745 --- /dev/null +++ b/e2e/auth.spec.ts @@ -0,0 +1,122 @@ +import { createId } from "@paralleldrive/cuid2"; +import { expect, test, type BrowserContext, type Page } from "@playwright/test"; +import { Lucia, type Cookie } from "lucia"; +import createClient from "openapi-fetch"; +import * as schema from "~/drizzle/schema"; +import { paths } from "~/types/discord"; + +import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; +import "dotenv/config"; +import { drizzle } from "drizzle-orm/postgres-js"; +import postgres from "postgres"; + +const queryClient = postgres(process.env.DATABASE_URL!); +const db = drizzle(queryClient, { + schema, +}); + +const adapter = new DrizzlePostgreSQLAdapter(db, schema.sessions, schema.users); +export const lucia = new Lucia(adapter, { + getUserAttributes: (attributes) => attributes, +}); + +let context: BrowserContext; +let page: Page; + +let sessionCookie: Cookie | undefined; + +test.describe.serial("User auth process", () => { + test.beforeAll(async ({ browser }) => { + context = await browser.newContext(); + page = await context.newPage(); + }); + + test.beforeEach(async () => { + if (!sessionCookie) return; + + const sameSiteProps = { + lax: "Lax", + strict: "Strict", + none: "None", + } as const; + const expires = sessionCookie.attributes.expires + ? sessionCookie.attributes.expires.getTime() / 1000 + : undefined; + const sameSite = sessionCookie.attributes.sameSite + ? sameSiteProps[sessionCookie.attributes.sameSite] + : undefined; + await context.addCookies([ + { + name: sessionCookie.name, + value: sessionCookie.value, + ...sessionCookie.attributes, + sameSite, + expires, + secure: false, + domain: "localhost", + path: "/", + }, + ]); + }); + + test.afterAll(async () => { + await context.close(); + }); + + test("Landing page", async () => { + await page.goto("/"); + await page.waitForLoadState("load"); + + expect(await page.screenshot()).toMatchSnapshot("landing_page.png"); + }); + + test("Unauthorized Access Redirect Test", async () => { + await page.goto("/config"); + await page.waitForURL("/"); + }); + + test("Generate auth session for further tests", async () => { + const { GET } = createClient({ + baseUrl: "https://discord.com/api/v10", + }); + const discordUserResponse = await GET("/users/@me", { + headers: { + Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN}`, + }, + }); + if (discordUserResponse.error) throw discordUserResponse.error; + const discordUser = discordUserResponse.data; + const userId = createId(); + await db.insert(schema.users).values({ + id: userId, + discord_id: discordUser.id, + name: discordUser.global_name, + image: discordUser.avatar, + }); + const session = await lucia.createSession( + userId, + {}, + { sessionId: createId() }, + ); + sessionCookie = lucia.createSessionCookie(session.id); + await db + .insert(schema.discordTokens) + .values({ + userId, + accessToken: "tokens.accessToken", + expiresAt: sessionCookie.attributes.expires ?? new Date(), + refreshToken: "tokens.refreshToken", + }) + .returning() + .execute(); + }); + + test("Landing page when logged in", async () => { + await page.goto("/"); + await page.waitForLoadState("load"); + + expect(await page.screenshot()).toMatchSnapshot( + "landing_page_logged_in.png", + ); + }); +}); diff --git a/e2e/auth.spec.ts-snapshots/landing-page-chromium-linux.png b/e2e/auth.spec.ts-snapshots/landing-page-chromium-linux.png new file mode 100644 index 0000000..e936eb3 Binary files /dev/null and b/e2e/auth.spec.ts-snapshots/landing-page-chromium-linux.png differ diff --git a/e2e/auth.spec.ts-snapshots/landing-page-firefox-linux.png b/e2e/auth.spec.ts-snapshots/landing-page-firefox-linux.png new file mode 100644 index 0000000..89dd0ec Binary files /dev/null and b/e2e/auth.spec.ts-snapshots/landing-page-firefox-linux.png differ diff --git a/e2e/auth.spec.ts-snapshots/landing-page-logged-in-chromium-linux.png b/e2e/auth.spec.ts-snapshots/landing-page-logged-in-chromium-linux.png new file mode 100644 index 0000000..ceadef9 Binary files /dev/null and b/e2e/auth.spec.ts-snapshots/landing-page-logged-in-chromium-linux.png differ diff --git a/e2e/auth.spec.ts-snapshots/landing-page-logged-in-firefox-linux.png b/e2e/auth.spec.ts-snapshots/landing-page-logged-in-firefox-linux.png new file mode 100644 index 0000000..aa20fc8 Binary files /dev/null and b/e2e/auth.spec.ts-snapshots/landing-page-logged-in-firefox-linux.png differ diff --git a/e2e/auth.spec.ts-snapshots/landing-page-logged-in-webkit-linux.png b/e2e/auth.spec.ts-snapshots/landing-page-logged-in-webkit-linux.png new file mode 100644 index 0000000..4203c20 Binary files /dev/null and b/e2e/auth.spec.ts-snapshots/landing-page-logged-in-webkit-linux.png differ diff --git a/e2e/auth.spec.ts-snapshots/landing-page-webkit-linux.png b/e2e/auth.spec.ts-snapshots/landing-page-webkit-linux.png new file mode 100644 index 0000000..4dd1e5a Binary files /dev/null and b/e2e/auth.spec.ts-snapshots/landing-page-webkit-linux.png differ diff --git a/package.json b/package.json index 5b5a496..536f8f5 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "discord-openapi-gen": "openapi-typescript https://raw.githubusercontent.com/discord/discord-api-spec/main/specs/openapi.json -o ./src/types/discord.d.ts", "liljudd-openapi-gen": "openapi-typescript ./public/api/specs/liljudd.json -o ./src/types/liljudd.d.ts", "typecheck": "tsc --noEmit --checkJs false --skipLibCheck --preserveSymLinks", - "drizzle-studio": "drizzle-kit studio" + "drizzle-studio": "drizzle-kit studio", + "test": "pnpm exec playwright test", + "test-ui": "pnpm exec playwright test --ui" }, "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.5.1", @@ -38,7 +40,9 @@ "vinxi": "^0.3.4" }, "devDependencies": { + "@playwright/test": "^1.42.0", "@types/json-stable-stringify": "^1.0.36", + "@types/node": "^20.11.22", "@types/object-hash": "^3.0.6", "@typescript-eslint/eslint-plugin": "^7.1.0", "dotenv": "^16.4.5", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..6bbf1ef --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,77 @@ +import { defineConfig, devices } from "@playwright/test"; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: "./e2e", + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: "html", + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: "http://localhost:3000", + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + + { + name: "firefox", + use: { ...devices["Desktop Firefox"] }, + }, + + { + name: "webkit", + use: { ...devices["Desktop Safari"] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: "pnpm start", + url: "http://localhost:3000", + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6578ad7..c2b1388 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -73,12 +73,18 @@ dependencies: version: 1.8.15 vinxi: specifier: ^0.3.4 - version: 0.3.4(preact@10.19.6)(sass@1.71.1) + version: 0.3.4(@types/node@20.11.22)(preact@10.19.6)(sass@1.71.1) devDependencies: + '@playwright/test': + specifier: ^1.42.0 + version: 1.42.0 '@types/json-stable-stringify': specifier: ^1.0.36 version: 1.0.36 + '@types/node': + specifier: ^20.11.22 + version: 20.11.22 '@types/object-hash': specifier: ^3.0.6 version: 3.0.6 @@ -316,7 +322,6 @@ packages: /@babel/parser@7.24.0: resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==} engines: {node: '>=6.0.0'} - hasBin: true dependencies: '@babel/types': 7.24.0 dev: false @@ -988,7 +993,6 @@ packages: /@mapbox/node-pre-gyp@1.0.11: resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} - hasBin: true dependencies: detect-libc: 2.0.2 https-proxy-agent: 5.0.1 @@ -1508,6 +1512,13 @@ packages: '@parcel/watcher-win32-x64': 2.4.1 dev: false + /@playwright/test@1.42.0: + resolution: {integrity: sha512-2k1HzC28Fs+HiwbJOQDUwrWMttqSLUVdjCqitBOjdCD0svWOMQUVqrXX6iFD7POps6xXAojsX/dGBpKnjZctLA==} + engines: {node: '>=16'} + dependencies: + playwright: 1.42.0 + dev: true + /@polka/url@1.0.0-next.24: resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} dev: false @@ -1529,7 +1540,7 @@ packages: magic-string: 0.30.5 node-html-parser: 6.1.12 resolve: 1.22.8 - vite: 5.1.1(sass@1.71.1) + vite: 5.1.1(@types/node@20.11.22)(sass@1.71.1) transitivePeerDependencies: - preact - supports-color @@ -1563,7 +1574,7 @@ packages: '@prefresh/utils': 1.2.0 '@rollup/pluginutils': 4.2.1 preact: 10.19.6 - vite: 5.1.1(sass@1.71.1) + vite: 5.1.1(@types/node@20.11.22)(sass@1.71.1) transitivePeerDependencies: - supports-color dev: false @@ -1947,7 +1958,6 @@ packages: resolution: {integrity: sha512-/G+IxWxma6V3E+pqK1tSl2Fo1kl41pK1yeCyDsgkF9WlVAme4j5ISYM2zR11bgLFJGLN5sVK40T4RJNuiZbEjA==} dependencies: undici-types: 5.26.5 - dev: false /@types/object-hash@3.0.6: resolution: {integrity: sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w==} @@ -2176,7 +2186,6 @@ packages: /@vercel/nft@0.24.4: resolution: {integrity: sha512-KjYAZty7boH5fi5udp6p+lNu6nawgs++pHW+3koErMgbRkkHuToGX/FwjN5clV1FcaM3udfd4zW/sUapkMgpZw==} engines: {node: '>=16'} - hasBin: true dependencies: '@mapbox/node-pre-gyp': 1.0.11 '@rollup/pluginutils': 4.2.1 @@ -2218,7 +2227,6 @@ packages: /@vinxi/listhen@1.5.6: resolution: {integrity: sha512-WSN1z931BtasZJlgPp704zJFnQFRg7yzSjkm3MzAWQYe4uXFXlFr1hc5Ac2zae5/HDOz5x1/zDM5Cb54vTCnWw==} - hasBin: true dependencies: '@parcel/watcher': 2.4.1 '@parcel/watcher-wasm': 2.3.0 @@ -2255,7 +2263,7 @@ packages: magicast: 0.2.11 recast: 0.23.4 tslib: 2.6.2 - vinxi: 0.3.4(preact@10.19.6)(sass@1.71.1) + vinxi: 0.3.4(@types/node@20.11.22)(preact@10.19.6)(sass@1.71.1) dev: false /@vinxi/server-components@0.3.0(vinxi@0.3.4): @@ -2270,7 +2278,7 @@ packages: astring: 1.8.6 magicast: 0.2.11 recast: 0.23.4 - vinxi: 0.3.4(preact@10.19.6)(sass@1.71.1) + vinxi: 0.3.4(@types/node@20.11.22)(preact@10.19.6)(sass@1.71.1) dev: false /@vinxi/server-functions@0.3.0(vinxi@0.3.4): @@ -2285,7 +2293,7 @@ packages: astring: 1.8.6 magicast: 0.2.11 recast: 0.23.4 - vinxi: 0.3.4(preact@10.19.6)(sass@1.71.1) + vinxi: 0.3.4(@types/node@20.11.22)(preact@10.19.6)(sass@1.71.1) dev: false /abbrev@1.1.1: @@ -2317,7 +2325,6 @@ packages: /acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} - hasBin: true /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} @@ -2451,7 +2458,6 @@ packages: /astring@1.8.6: resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} - hasBin: true dev: false /async-sema@3.1.1: @@ -2576,7 +2582,6 @@ packages: /browserslist@4.23.0: resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true dependencies: caniuse-lite: 1.0.30001591 electron-to-chromium: 1.4.686 @@ -2754,7 +2759,6 @@ packages: /color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true dev: false /commander@2.20.3: @@ -2816,7 +2820,6 @@ packages: /crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} - hasBin: true dev: false /crc32-stream@5.0.0: @@ -2981,7 +2984,6 @@ packages: /detect-libc@1.0.3: resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} engines: {node: '>=0.10'} - hasBin: true dev: false /detect-libc@2.0.2: @@ -3056,7 +3058,6 @@ packages: /drizzle-kit@0.20.14: resolution: {integrity: sha512-0fHv3YIEaUcSVPSGyaaBfOi9bmpajjhbJNdPsRMIUvYdLVxBu9eGjH8mRc3Qk7HVmEidFc/lhG1YyJhoXrn5yA==} - hasBin: true dependencies: '@drizzle-team/studio': 0.0.39 '@esbuild-kit/esm-loader': 2.6.5 @@ -3162,7 +3163,6 @@ packages: /dts-buddy@0.2.5: resolution: {integrity: sha512-66HTWHyXS3JwgpRwcu88rsDyZfPUb0oPYmiNg5f4BgCAFTVorJXpygf339QyXOXX1PuqHpvB+qo7O+8Ni1vXUQ==} - hasBin: true dependencies: '@jridgewell/source-map': 0.3.5 '@jridgewell/sourcemap-codec': 1.4.15 @@ -3290,7 +3290,6 @@ packages: /esbuild@0.18.20: resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} engines: {node: '>=12'} - hasBin: true requiresBuild: true optionalDependencies: '@esbuild/android-arm': 0.18.20 @@ -3319,7 +3318,6 @@ packages: /esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} - hasBin: true requiresBuild: true optionalDependencies: '@esbuild/aix-ppc64': 0.19.12 @@ -3372,7 +3370,6 @@ packages: /eslint-config-prettier@9.1.0(eslint@8.57.0): resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} - hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: @@ -3412,7 +3409,6 @@ packages: /eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.10.0 @@ -3478,7 +3474,6 @@ packages: /esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} - hasBin: true dev: false /esquery@1.5.0: @@ -3649,7 +3644,6 @@ packages: /flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true dev: false /flatted@3.3.1: @@ -3696,6 +3690,14 @@ packages: /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + /fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -3770,7 +3772,6 @@ packages: /giget@1.2.1: resolution: {integrity: sha512-4VG22mopWtIeHwogGSy1FViXVo0YT+m6BrqZfz0JJFwbSsePsCdOzdLIIli5BtMp7Xe8f/o2OmBpQX2NBOC24g==} - hasBin: true dependencies: citty: 0.1.6 consola: 3.2.3 @@ -3963,7 +3964,6 @@ packages: /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true dev: false /heap@0.2.7: @@ -4136,13 +4136,11 @@ packages: /is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} - hasBin: true dev: false /is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true dev: false /is-extglob@2.1.1: @@ -4177,7 +4175,6 @@ packages: /is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} - hasBin: true dependencies: is-docker: 3.0.0 dev: false @@ -4278,7 +4275,6 @@ packages: /jiti@1.21.0: resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} - hasBin: true dev: false /js-tokens@4.0.0: @@ -4287,14 +4283,12 @@ packages: /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true dependencies: argparse: 2.0.1 /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} - hasBin: true dev: false /json-buffer@3.0.1: @@ -4303,7 +4297,6 @@ packages: /json-diff@0.9.0: resolution: {integrity: sha512-cVnggDrVkAAA3OvFfHpFEhOnmcsUpleEKq4d4O8sQWWSH40MBrWstKigVB1kGrgLWzuom+7rRdaCsnBD6VyObQ==} - hasBin: true dependencies: cli-color: 2.0.3 difflib: 0.2.4 @@ -4331,7 +4324,6 @@ packages: /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} - hasBin: true dev: false /jsonc-parser@3.2.1: @@ -4399,7 +4391,6 @@ packages: /listhen@1.7.2: resolution: {integrity: sha512-7/HamOm5YD9Wb7CFgAZkKgVPA96WwhcTQoqtm2VTZGVbVVn3IWKRBTgrU7cchA3Q8k9iCsG8Osoi9GX4JsGM9g==} - hasBin: true dependencies: '@parcel/watcher': 2.4.1 '@parcel/watcher-wasm': 2.4.1 @@ -4558,13 +4549,11 @@ packages: /mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} - hasBin: true dev: false /mime@3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} - hasBin: true /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} @@ -4624,7 +4613,6 @@ packages: /mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} - hasBin: true dev: false /mlly@1.6.1: @@ -4670,7 +4658,6 @@ packages: /nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true dev: false /natural-compare@1.4.0: @@ -4684,7 +4671,6 @@ packages: /nitropack@2.8.1: resolution: {integrity: sha512-pODv2kEEzZSDQR+1UMXbGyNgMedUDq/qUomtiAnQKQvLy52VGlecXO1xDfH3i0kP1yKEcKTnWsx1TAF5gHM7xQ==} engines: {node: ^16.11.0 || >=17.0.0} - hasBin: true peerDependencies: xml2js: ^0.6.2 peerDependenciesMeta: @@ -4800,7 +4786,6 @@ packages: /node-gyp-build@4.8.0: resolution: {integrity: sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==} - hasBin: true dev: false /node-html-parser@6.1.12: @@ -4817,7 +4802,6 @@ packages: /nopt@5.0.0: resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} engines: {node: '>=6'} - hasBin: true dependencies: abbrev: 1.1.1 dev: false @@ -4858,7 +4842,6 @@ packages: /nypm@0.3.6: resolution: {integrity: sha512-2CATJh3pd6CyNfU5VZM7qSwFu0ieyabkEdnogE30Obn1czrmOYiZ8DOZLe1yBdLKWoyD3Mcy2maUs+0MR3yVjQ==} engines: {node: ^14.16.0 || >=16.10.0} - hasBin: true dependencies: citty: 0.1.6 execa: 8.0.1 @@ -4967,7 +4950,6 @@ packages: /openapi-typescript@6.7.4: resolution: {integrity: sha512-EZyeW9Wy7UDCKv0iYmKrq2pVZtquXiD/YHiUClAKqiMi42nodx/EQH11K6fLqjt1IZlJmVokrAsExsBMM2RROQ==} - hasBin: true dependencies: ansi-colors: 4.1.3 fast-glob: 3.3.2 @@ -5139,6 +5121,20 @@ packages: pathe: 1.1.2 dev: false + /playwright-core@1.42.0: + resolution: {integrity: sha512-0HD9y8qEVlcbsAjdpBaFjmaTHf+1FeIddy8VJLeiqwhcNqGCBe4Wp2e8knpqiYbzxtxarxiXyNDw2cG8sCaNMQ==} + engines: {node: '>=16'} + dev: true + + /playwright@1.42.0: + resolution: {integrity: sha512-Ko7YRUgj5xBHbntrgt4EIw/nE//XBHOKVKnBjO1KuZkmkhlbgyggTe5s9hjqQ1LpN+Xg+kHsQyt5Pa0Bw5XpvQ==} + engines: {node: '>=16'} + dependencies: + playwright-core: 1.42.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -5204,7 +5200,6 @@ packages: /prettier@3.2.5: resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} - hasBin: true dev: true /pretty-bytes@6.1.1: @@ -5331,7 +5326,6 @@ packages: /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true dependencies: is-core-module: 2.13.1 path-parse: 1.0.7 @@ -5344,14 +5338,12 @@ packages: /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true dependencies: glob: 7.2.3 /rollup-plugin-visualizer@5.12.0(rollup@4.12.0): resolution: {integrity: sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==} engines: {node: '>=14'} - hasBin: true peerDependencies: rollup: 2.x || 3.x || 4.x peerDependenciesMeta: @@ -5368,7 +5360,6 @@ packages: /rollup@4.12.0: resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: @@ -5418,7 +5409,6 @@ packages: /sass@1.71.1: resolution: {integrity: sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==} engines: {node: '>=14.0.0'} - hasBin: true dependencies: chokidar: 3.6.0 immutable: 4.3.5 @@ -5430,13 +5420,11 @@ packages: /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true dev: false /semver@7.6.0: resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} engines: {node: '>=10'} - hasBin: true dependencies: lru-cache: 6.0.0 @@ -5795,7 +5783,6 @@ packages: /terser@5.28.1: resolution: {integrity: sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==} engines: {node: '>=10'} - hasBin: true dependencies: '@jridgewell/source-map': 0.3.5 acorn: 8.11.3 @@ -5906,13 +5893,11 @@ packages: /typescript@5.0.4: resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} engines: {node: '>=12.20'} - hasBin: true dev: false /typescript@5.3.3: resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} - hasBin: true dev: true /ufo@1.4.0: @@ -5932,7 +5917,6 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: false /undici-types@5.28.3: resolution: {integrity: sha512-VJD0un4i6M1/lFOJPhacHdq6FadtlkdhKBed2W6yBqmrAr/W58oqENaOIX031stDVFwz9AemOLkIj/2AXAMLCg==} @@ -6056,7 +6040,6 @@ packages: /untun@0.1.3: resolution: {integrity: sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==} - hasBin: true dependencies: citty: 0.1.6 consola: 3.2.3 @@ -6065,7 +6048,6 @@ packages: /update-browserslist-db@1.0.13(browserslist@4.23.0): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} - hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: @@ -6106,9 +6088,8 @@ packages: resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} dev: false - /vinxi@0.3.4(preact@10.19.6)(sass@1.71.1): + /vinxi@0.3.4(@types/node@20.11.22)(preact@10.19.6)(sass@1.71.1): resolution: {integrity: sha512-WAQbutVIX9zE2U5jraq+uvW2ytzb59HtX+/KVKqZSgjfQQpCHyi3XNxRkf46WTM5gASDEDFaG0l5ZHunmf7/cw==} - hasBin: true dependencies: '@babel/core': 7.24.0 '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.0) @@ -6153,7 +6134,7 @@ packages: unenv: 1.9.0 unimport: 3.7.1(rollup@4.12.0) unstorage: 1.10.1 - vite: 5.1.1(sass@1.71.1) + vite: 5.1.1(@types/node@20.11.22)(sass@1.71.1) ws: 8.16.0 zod: 3.22.4 transitivePeerDependencies: @@ -6207,7 +6188,7 @@ packages: open: 9.1.0 picocolors: 1.0.0 sirv: 2.0.4 - vite: 5.1.1(sass@1.71.1) + vite: 5.1.1(@types/node@20.11.22)(sass@1.71.1) transitivePeerDependencies: - rollup - supports-color @@ -6231,7 +6212,7 @@ packages: open: 9.1.0 picocolors: 1.0.0 sirv: 2.0.4 - vite: 5.1.4(sass@1.71.1) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1) transitivePeerDependencies: - rollup - supports-color @@ -6253,7 +6234,7 @@ packages: merge-anything: 5.1.7 solid-js: 1.8.15 solid-refresh: 0.6.3(solid-js@1.8.15) - vite: 5.1.1(sass@1.71.1) + vite: 5.1.1(@types/node@20.11.22)(sass@1.71.1) vitefu: 0.2.5(vite@5.1.1) transitivePeerDependencies: - supports-color @@ -6275,16 +6256,15 @@ packages: merge-anything: 5.1.7 solid-js: 1.8.15 solid-refresh: 0.6.3(solid-js@1.8.15) - vite: 5.1.4(sass@1.71.1) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1) vitefu: 0.2.5(vite@5.1.4) transitivePeerDependencies: - supports-color dev: false - /vite@5.1.1(sass@1.71.1): + /vite@5.1.1(@types/node@20.11.22)(sass@1.71.1): resolution: {integrity: sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==} engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true peerDependencies: '@types/node': ^18.0.0 || >=20.0.0 less: '*' @@ -6309,6 +6289,7 @@ packages: terser: optional: true dependencies: + '@types/node': 20.11.22 esbuild: 0.19.12 postcss: 8.4.35 rollup: 4.12.0 @@ -6317,7 +6298,7 @@ packages: fsevents: 2.3.3 dev: false - /vite@5.1.4(sass@1.71.1): + /vite@5.1.4(@types/node@20.11.22)(sass@1.71.1): resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -6345,6 +6326,7 @@ packages: terser: optional: true dependencies: + '@types/node': 20.11.22 esbuild: 0.19.12 postcss: 8.4.35 rollup: 4.12.0 @@ -6361,7 +6343,7 @@ packages: vite: optional: true dependencies: - vite: 5.1.1(sass@1.71.1) + vite: 5.1.1(@types/node@20.11.22)(sass@1.71.1) dev: false /vitefu@0.2.5(vite@5.1.4): @@ -6372,7 +6354,7 @@ packages: vite: optional: true dependencies: - vite: 5.1.4(sass@1.71.1) + vite: 5.1.4(@types/node@20.11.22)(sass@1.71.1) dev: false /webidl-conversions@3.0.1: @@ -6409,14 +6391,12 @@ packages: /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} - hasBin: true dependencies: isexe: 2.0.0 /which@4.0.0: resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} engines: {node: ^16.13.0 || >=18.0.0} - hasBin: true dependencies: isexe: 3.1.1 dev: false diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index 7986256..82d9bfc 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -4,6 +4,12 @@ import "../styles/components/NavBar.scss"; import { FontAwesomeIcon } from "./FontAwesomeIcon"; import NavUser from "./NavUser"; +if (typeof import.meta.env.VITE_DISCORD_CLIENT_ID === "undefined") + throw new Error("No env VITE_DISCORD_CLIENT_ID found!"); + +if (typeof import.meta.env.VITE_DISCORD_OAUTH2_PERMISSIONS === "undefined") + throw new Error("No env VITE_DISCORD_OAUTH2_PERMISSIONS found!"); + export function Li(props: { href: string; rel?: string; @@ -36,7 +42,7 @@ function NavBar() {