diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 7bdf5dba..7bc7a03d 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -1,4 +1,6 @@ name: E2E Tests +permissions: + contents: read on: push: branches: [ main ] @@ -17,9 +19,30 @@ jobs: - name: Install dependencies uses: "./.github/actions/install-dependencies" + - name: Get Playwright version + id: playwright-version + shell: bash + run: | + playwright_version=$(bun -e "console.log((await Bun.file('./package.json').json()).devDependencies['@playwright/test'])") + echo "version=$playwright_version" >> $GITHUB_OUTPUT + + - name: Cache Playwright Browsers + id: playwright-cache + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }} + restore-keys: | + ${{ runner.os }}-playwright- + - name: Install Playwright Browsers + if: steps.playwright-cache.outputs.cache-hit != 'true' run: bunx playwright install --with-deps + - name: Install Playwright System Dependencies + if: steps.playwright-cache.outputs.cache-hit == 'true' + run: bunx playwright install-deps + - name: Prepare environment run: | mkdir -p data diff --git a/app/server/app.ts b/app/server/app.ts index f127d431..9aed26ad 100644 --- a/app/server/app.ts +++ b/app/server/app.ts @@ -37,7 +37,7 @@ export const scalarDescriptor = Scalar({ }); export const createApp = () => { - const app = new Hono(); + const app = new Hono().use(secureHeaders()); if (config.trustedOrigins) { app.use(cors({ origin: config.trustedOrigins })); @@ -47,9 +47,8 @@ export const createApp = () => { app.use(honoLogger()); } - app - .use(secureHeaders()) - .use( + if (!config.disableRateLimiting) { + app.use( rateLimiter({ windowMs: 60 * 5 * 1000, limit: 1000, @@ -58,7 +57,10 @@ export const createApp = () => { return config.__prod__ === false; }, }), - ) + ); + } + + app .get("healthcheck", (c) => c.json({ status: "ok" })) .route("/api/v1/auth", authController) .route("/api/v1/volumes", volumeController) diff --git a/app/server/core/config.ts b/app/server/core/config.ts index 7fb2b973..70ed7e21 100644 --- a/app/server/core/config.ts +++ b/app/server/core/config.ts @@ -33,6 +33,7 @@ const envSchema = type({ MIGRATIONS_PATH: "string?", APP_VERSION: "string = 'dev'", TRUSTED_ORIGINS: "string?", + DISABLE_RATE_LIMITING: 'string = "false"', }).pipe((s) => ({ __prod__: s.NODE_ENV === "production", environment: s.NODE_ENV, @@ -43,6 +44,7 @@ const envSchema = type({ migrationsPath: s.MIGRATIONS_PATH, appVersion: s.APP_VERSION, trustedOrigins: s.TRUSTED_ORIGINS?.split(",").map((origin) => origin.trim()), + disableRateLimiting: s.DISABLE_RATE_LIMITING === "true", })); const parseConfig = (env: unknown) => { diff --git a/docker-compose.yml b/docker-compose.yml index 143599e7..fa3d629b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,6 +47,8 @@ services: target: production container_name: zerobyte restart: unless-stopped + environment: + - DISABLE_RATE_LIMITING=true devices: - /dev/fuse:/dev/fuse cap_add: diff --git a/e2e/0001-initial-setup.spec.ts b/e2e/0001-initial-setup.spec.ts index 860cf570..5a14323e 100644 --- a/e2e/0001-initial-setup.spec.ts +++ b/e2e/0001-initial-setup.spec.ts @@ -7,6 +7,7 @@ import { createTestAccount } from "./helpers/account"; test.describe.configure({ mode: "serial" }); test.beforeEach(async () => { + console.log("Resetting database..."); await resetDatabase(); }); @@ -73,5 +74,5 @@ test("can't create another admin user after initial setup", async ({ page }) => await page.getByRole("button", { name: "Create admin user" }).click(); - await expect(page.getByText("Admin user already exists")).toBeVisible(); + await expect(page.getByText("Failed to create admin user")).toBeVisible(); }); diff --git a/e2e/helpers/db.ts b/e2e/helpers/db.ts index 008eaaba..3c0068ac 100644 --- a/e2e/helpers/db.ts +++ b/e2e/helpers/db.ts @@ -9,14 +9,12 @@ const sqlite = createClient({ url: `file:${path.join(process.cwd(), "data", DATA export const db = drizzle({ client: sqlite, schema: schema }); export const resetDatabase = async () => { - for (const table of Object.values(schema)) { - if ("getSQL" in table) { - await db - .delete(table) - .execute() - .catch(() => { - /* Ignore errors */ - }); - } + const cursor = await sqlite.execute("SELECT name FROM sqlite_master WHERE type='table'"); + const tables = cursor.rows + .map((row) => row.name) + .filter((name) => name !== "sqlite_sequence" && name !== "__drizzle_migrations") as string[]; + + for (const table of tables) { + await sqlite.execute(`DELETE FROM ${table}`); } };