diff --git a/.github/workflows/main-deploy.yml b/.github/workflows/main-deploy.yml new file mode 100644 index 0000000..591d392 --- /dev/null +++ b/.github/workflows/main-deploy.yml @@ -0,0 +1,40 @@ +name: Main Branch Deploy + +on: + push: + branches: [main] + +jobs: + test-build-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "24" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Run type checking + run: npm run check + + - name: Run linting + run: npm run lint + + - name: Run unit tests + run: npm run test:unit -- --run + + - name: Build application + run: npm run build + + - name: Install Playwright browsers + run: npx playwright install --with-deps + + - name: Run E2E tests + run: npm run test:e2e diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml new file mode 100644 index 0000000..fcc352e --- /dev/null +++ b/.github/workflows/pr-checks.yml @@ -0,0 +1,35 @@ +name: PR Checks + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + +jobs: + test-and-build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "24" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Run type checking + run: npm run check + + - name: Run linting + run: npm run lint + + - name: Run unit tests + run: npm run test:unit -- --run + + - name: Build application + run: npm run build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..165ea30 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,80 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "24" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Update package.json version + run: | + TAG_VERSION="${{ github.ref_name }}" + VERSION="${TAG_VERSION#v}" + npm version "$VERSION" --no-git-tag-version + + - name: Run type checking + run: npm run check + + - name: Run linting + run: npm run lint + + - name: Run unit tests + run: npm run test:unit -- --run + + - name: Build application + run: npm run build + + - name: Install Playwright browsers + run: npx playwright install --with-deps + + - name: Run E2E tests + run: npm run test:e2e + + - name: Build and push Docker images + run: npm run docker:build-and-push + + - name: Create Release Archive + run: | + mkdir -p release-assets + tar -czf release-assets/open-reception-${{ github.ref_name }}.tar.gz build/ + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + name: Release ${{ github.ref_name }} + generate_release_notes: true + body: | + ## Release ${{ github.ref_name }} + + This release includes the latest version of Open Reception appointment booking software. + + ### What's Changed + + + ### Installation + Extract the archive and follow the deployment instructions in the README. + + **Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ github.event.before }}...${{ github.ref_name }} + files: | + release-assets/open-reception-${{ github.ref_name }}.tar.gz + draft: false + prerelease: ${{ contains(github.ref_name, '-') }} diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 883e141..e3bbc0d 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -16,7 +16,11 @@ services: networks: - appointment-booking-dev healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-appointment_booking}"] + test: + [ + "CMD-SHELL", + "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-appointment_booking}" + ] interval: 10s timeout: 5s retries: 5 @@ -33,4 +37,4 @@ volumes: networks: appointment-booking-dev: - driver: bridge \ No newline at end of file + driver: bridge diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 2826371..0cba9ec 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -19,7 +19,11 @@ services: networks: - open-reception-internal healthcheck: - test: ["CMD-SHELL", "pg_isready -U $$(cat /run/secrets/postgres_user) -d $$(cat /run/secrets/postgres_db)"] + test: + [ + "CMD-SHELL", + "pg_isready -U $$(cat /run/secrets/postgres_user) -d $$(cat /run/secrets/postgres_db)" + ] interval: 10s timeout: 5s retries: 5 @@ -56,7 +60,8 @@ services: networks: - open-reception-internal healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/api/health"] + test: + ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/api/health"] interval: 30s timeout: 10s retries: 3 @@ -119,4 +124,4 @@ volumes: networks: open-reception-internal: driver: bridge - internal: false \ No newline at end of file + internal: false diff --git a/docs/infrastructure.md b/docs/infrastructure.md index bcc6996..a8ab6b2 100644 --- a/docs/infrastructure.md +++ b/docs/infrastructure.md @@ -23,16 +23,20 @@ Development runs only the PostgreSQL database with a public port for development ### Setup Steps 1. **Clone the repository** + ```bash git clone cd appointment-booking-software ``` 2. **Configure environment** + ```bash cp .env.example .env ``` + Edit `.env` and set secure values: + ``` POSTGRES_DB=appointment_booking POSTGRES_USER=postgres @@ -41,20 +45,23 @@ Development runs only the PostgreSQL database with a public port for development ``` 3. **Start development database** + ```bash npm run docker:dev:up ``` 4. **Verify database connection** + ```bash # Check container status docker ps - + # Test connection psql -h localhost -U postgres -d appointment_booking ``` 5. **Install SvelteKit dependencies** (when SvelteKit code is added) + ```bash npm install ``` @@ -91,21 +98,23 @@ Production runs all three containers with security hardening and HTTPS. ### Setup Steps 1. **Configure production secrets** - + Edit the secret files in the `secrets/` directory: + ```bash # Database configuration echo "appointment_booking" > secrets/postgres_db.txt echo "postgres" > secrets/postgres_user.txt echo "$(openssl rand -base64 32)" > secrets/postgres_password.txt - + # Set proper permissions chmod 600 secrets/*.txt ``` 2. **Configure domain** - + Edit `Caddyfile` and replace `your-domain.com` with your actual domain: + ``` yourdomain.com { # ... rest of configuration @@ -113,22 +122,24 @@ Production runs all three containers with security hardening and HTTPS. ``` 3. **Build and start production stack** + ```bash # Build application image npm run docker:prod:build - + # Start all services npm run docker:prod:up ``` 4. **Verify deployment** + ```bash # Check all containers are running docker ps - + # Check application health npm run docker:health - + # View logs npm run docker:prod:logs ``` @@ -143,7 +154,7 @@ npm run docker:prod:down # Stop all services npm run docker:prod:logs # View all logs npm run docker:prod:clean # Remove containers and volumes npm run docker:health # Check container status -npm run docker:tag-and-push # Build, tag and push the app image to GHCR (Remember to change username accordingly first) +npm run docker:tag-and-push # Build, tag and push the app image to GHCR (Remember to change username accordingly first) ``` ## SvelteKit Application Setup @@ -153,34 +164,35 @@ When implementing the SvelteKit application, follow these steps: ### Database Connection Create `src/lib/database.js`: + ```javascript -import { readFileSync } from 'fs'; -import pg from 'pg'; +import { readFileSync } from "fs"; +import pg from "pg"; function getDatabaseConfig() { - if (process.env.NODE_ENV === 'production') { - // Read from Docker secrets - const user = readFileSync('/run/secrets/postgres_user', 'utf8').trim(); - const password = readFileSync('/run/secrets/postgres_password', 'utf8').trim(); - const database = readFileSync('/run/secrets/postgres_db', 'utf8').trim(); - - return { - host: 'postgres', - port: 5432, - user, - password, - database - }; - } else { - // Development configuration - return { - host: 'localhost', - port: process.env.POSTGRES_PORT || 5432, - user: process.env.POSTGRES_USER || 'postgres', - password: process.env.POSTGRES_PASSWORD, - database: process.env.POSTGRES_DB || 'appointment_booking' - }; - } + if (process.env.NODE_ENV === "production") { + // Read from Docker secrets + const user = readFileSync("/run/secrets/postgres_user", "utf8").trim(); + const password = readFileSync("/run/secrets/postgres_password", "utf8").trim(); + const database = readFileSync("/run/secrets/postgres_db", "utf8").trim(); + + return { + host: "postgres", + port: 5432, + user, + password, + database + }; + } else { + // Development configuration + return { + host: "localhost", + port: process.env.POSTGRES_PORT || 5432, + user: process.env.POSTGRES_USER || "postgres", + password: process.env.POSTGRES_PASSWORD, + database: process.env.POSTGRES_DB || "appointment_booking" + }; + } } export const pool = new pg.Pool(getDatabaseConfig()); @@ -189,54 +201,55 @@ export const pool = new pg.Pool(getDatabaseConfig()); ### Health Check Endpoint Create `src/routes/health/+server.js`: + ```javascript -import { json } from '@sveltejs/kit'; -import { pool } from '$lib/database.js'; +import { json } from "@sveltejs/kit"; +import { pool } from "$lib/database.js"; export async function GET() { - try { - await pool.query('SELECT 1'); - return json({ status: 'healthy', timestamp: new Date().toISOString() }); - } catch (error) { - return json( - { status: 'unhealthy', error: error.message }, - { status: 500 } - ); - } + try { + await pool.query("SELECT 1"); + return json({ status: "healthy", timestamp: new Date().toISOString() }); + } catch (error) { + return json({ status: "unhealthy", error: error.message }, { status: 500 }); + } } ``` ### SvelteKit Configuration - Update `vite.config.js`: + ```javascript -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; +import { sveltekit } from "@sveltejs/kit/vite"; +import { defineConfig } from "vite"; export default defineConfig({ - plugins: [sveltekit()], - server: { - host: '0.0.0.0', - port: 5173 - } + plugins: [sveltekit()], + server: { + host: "0.0.0.0", + port: 5173 + } }); ``` ## Security Features ### Container Security + - **Rootless containers**: All containers (sans caddy, which needs to access privileged ports) run as non-root users - **Read-only filesystems**: Containers have read-only root filesystems where possible - **No new privileges**: Containers cannot escalate privileges - **Minimal capabilities**: Only necessary Linux capabilities are granted ### Network Security + - **Internal networks**: Database and app communicate via internal Docker network - **No exposed ports**: Only Caddy exposes ports to the host - **HTTPS only**: Caddy handles automatic HTTPS with Let's Encrypt ### Data Security + - **Docker secrets**: Sensitive data stored as Docker secrets - **Encrypted connections**: Database connections use SSL/TLS - **Security headers**: Comprehensive security headers via Caddy @@ -244,16 +257,19 @@ export default defineConfig({ ## Monitoring and Logs ### Log Locations + - **Caddy logs**: Stored in `/data/access.log` inside Caddy container - **Application logs**: Available via `docker logs` - **Database logs**: Available via `docker logs` ### Health Checks + - **Application**: `GET /health` endpoint - **Database**: Built-in PostgreSQL health check - **Caddy**: HTTP response check ### Monitoring Commands + ```bash # View real-time logs npm run docker:prod:logs @@ -273,37 +289,41 @@ docker logs appointment-booking-caddy ### Common Issues 1. **Container won't start** + ```bash # Check container logs docker logs - + # Check resource usage docker stats ``` 2. **Database connection issues** + ```bash # Test database connectivity docker exec -it appointment-booking-postgres psql -U postgres -d appointment_booking - + # Check database logs docker logs appointment-booking-postgres ``` 3. **HTTPS certificate issues** + ```bash # Check Caddy logs docker logs appointment-booking-caddy - + # Verify domain DNS nslookup yourdomain.com ``` 4. **Application not accessible** + ```bash # Check if all containers are running docker ps - + # Test internal connectivity docker exec -it appointment-booking-caddy wget -q --spider http://app:3000/health ``` @@ -323,6 +343,7 @@ npm run docker:prod:up ## Backup and Restore ### Database Backup + ```bash # Create backup docker exec appointment-booking-postgres pg_dump -U postgres appointment_booking > backup.sql @@ -332,7 +353,8 @@ docker exec -i appointment-booking-postgres psql -U postgres appointment_booking ``` ### Volume Backup + ```bash # Backup PostgreSQL data volume docker run --rm -v appointment-booking-software_postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz /data -``` \ No newline at end of file +``` diff --git a/package-lock.json b/package-lock.json index d6c163c..6bcb12b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "AGPL-3.0", "dependencies": { "@sveltejs/adapter-node": "^5.2.12", - "@types/dotenv": "^6.1.1", "dotenv": "^16.5.0", "dotenv-expand": "^12.0.2", "drizzle-orm": "^0.40.0", @@ -27,6 +26,7 @@ "@tailwindcss/vite": "^4.0.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/svelte": "^5.2.4", + "@types/dotenv": "^6.1.1", "@types/node": "^22", "drizzle-kit": "^0.30.2", "eslint": "^9.18.0", @@ -1886,9 +1886,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.21.5", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.21.5.tgz", - "integrity": "sha512-P5m7yZtvD1Kx/Z6JcjgJtdMqef/tCGMDrd9B9S2q8j+FMnkeKTMxW1nidnjVzk4HEDRGf4IlBI94/niy6t3hLA==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.22.0.tgz", + "integrity": "sha512-DJm0UxVgzXq+1MUfiJK4Ridk7oIQsIets6JwHiEl97sI6nXScfXe+BeqNhzB7jQIVBb3BM51U4hNk8qQxRXBAA==", "license": "MIT", "dependencies": { "@sveltejs/acorn-typescript": "^1.0.5", @@ -1912,9 +1912,9 @@ "node": ">=18.13" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3 || ^6.0.0" + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" } }, "node_modules/@sveltejs/vite-plugin-svelte": { @@ -2383,6 +2383,7 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz", "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -2405,6 +2406,7 @@ "version": "22.15.32", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.32.tgz", "integrity": "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==", + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -6031,6 +6033,7 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, "license": "MIT" }, "node_modules/uri-js": { diff --git a/package.json b/package.json index 7b2d5be..6d357d2 100644 --- a/package.json +++ b/package.json @@ -1,76 +1,76 @@ { - "name": "open-reception", - "private": true, - "version": "0.0.1", - "type": "module", - "description": "End-to-end encrypted appointment booking platform", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "format": "prettier --write .", - "lint": "prettier --check . && eslint .", - "test:unit": "vitest", - "test": "npm run test:unit -- --run && npm run test:e2e", - "test:e2e": "playwright test", - "db:push": "drizzle-kit push", - "db:migrate": "drizzle-kit migrate", - "db:studio": "drizzle-kit studio", - "docker:dev:up": "docker compose -f docker-compose.dev.yml up -d", - "docker:dev:down": "docker compose -f docker-compose.dev.yml down", - "docker:dev:logs": "docker compose -f docker-compose.dev.yml logs -f", - "docker:dev:clean": "docker compose -f docker-compose.dev.yml down -v --remove-orphans", - "docker:build": "docker build -t open-reception:latest .", - "docker:build:tag": "docker tag open-reception:latest open-reception:$npm_package_version", - "docker:push": "docker push open-reception:$npm_package_version && docker push open-reception:latest", - "docker:build-and-push": "npm run docker:build && npm run docker:build:tag && npm run docker:push", - "docker:prod:up": "docker compose -f docker-compose.prod.yml up -d", - "docker:prod:down": "docker compose -f docker-compose.prod.yml down", - "docker:prod:logs": "docker compose -f docker-compose.prod.yml logs -f", - "docker:prod:clean": "docker compose -f docker-compose.prod.yml down -v --remove-orphans" - }, - "devDependencies": { - "@eslint/compat": "^1.2.5", - "@eslint/js": "^9.18.0", - "@playwright/test": "^1.49.1", - "@sveltejs/adapter-auto": "^6.0.0", - "@sveltejs/kit": "^2.16.0", - "@sveltejs/vite-plugin-svelte": "^5.0.0", - "@tailwindcss/typography": "^0.5.15", - "@tailwindcss/vite": "^4.0.0", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/svelte": "^5.2.4", - "@types/node": "^22", + "name": "open-reception", + "private": true, + "version": "0.0.1", + "type": "module", + "description": "End-to-end encrypted appointment booking platform", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "format": "prettier --write .", + "lint": "prettier --check . && eslint .", + "test:unit": "vitest", + "test": "npm run test:unit -- --run && npm run test:e2e", + "test:e2e": "playwright test", + "db:push": "drizzle-kit push", + "db:migrate": "drizzle-kit migrate", + "db:studio": "drizzle-kit studio", + "docker:dev:up": "docker compose -f docker-compose.dev.yml up -d", + "docker:dev:down": "docker compose -f docker-compose.dev.yml down", + "docker:dev:logs": "docker compose -f docker-compose.dev.yml logs -f", + "docker:dev:clean": "docker compose -f docker-compose.dev.yml down -v --remove-orphans", + "docker:build": "docker build -t open-reception:latest .", + "docker:build:tag": "docker tag open-reception:latest open-reception:$npm_package_version", + "docker:push": "docker push open-reception:$npm_package_version && docker push open-reception:latest", + "docker:build-and-push": "npm run docker:build && npm run docker:build:tag && npm run docker:push", + "docker:prod:up": "docker compose -f docker-compose.prod.yml up -d", + "docker:prod:down": "docker compose -f docker-compose.prod.yml down", + "docker:prod:logs": "docker compose -f docker-compose.prod.yml logs -f", + "docker:prod:clean": "docker compose -f docker-compose.prod.yml down -v --remove-orphans" + }, + "devDependencies": { + "@eslint/compat": "^1.2.5", + "@eslint/js": "^9.18.0", + "@playwright/test": "^1.49.1", + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@tailwindcss/typography": "^0.5.15", + "@tailwindcss/vite": "^4.0.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/svelte": "^5.2.4", + "@types/node": "^22", "@types/dotenv": "^6.1.1", - "drizzle-kit": "^0.30.2", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-svelte": "^3.0.0", - "globals": "^16.0.0", - "jsdom": "^26.0.0", - "prettier": "^3.4.2", - "prettier-plugin-svelte": "^3.3.3", - "prettier-plugin-tailwindcss": "^0.6.11", - "svelte": "^5.0.0", - "svelte-check": "^4.0.0", - "tailwindcss": "^4.0.0", - "typescript": "^5.0.0", - "typescript-eslint": "^8.20.0", - "vite": "^6.2.6", - "vitest": "^3.2.3" - }, - "dependencies": { - "@sveltejs/adapter-node": "^5.2.12", - "dotenv": "^16.5.0", - "dotenv-expand": "^12.0.2", - "drizzle-orm": "^0.40.0", - "postgres": "^3.4.5" - }, - "engines": { - "node": ">=24.0.0" - }, - "license": "AGPL-3.0" + "drizzle-kit": "^0.30.2", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-svelte": "^3.0.0", + "globals": "^16.0.0", + "jsdom": "^26.0.0", + "prettier": "^3.4.2", + "prettier-plugin-svelte": "^3.3.3", + "prettier-plugin-tailwindcss": "^0.6.11", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^4.0.0", + "typescript": "^5.0.0", + "typescript-eslint": "^8.20.0", + "vite": "^6.2.6", + "vitest": "^3.2.3" + }, + "dependencies": { + "@sveltejs/adapter-node": "^5.2.12", + "dotenv": "^16.5.0", + "dotenv-expand": "^12.0.2", + "drizzle-orm": "^0.40.0", + "postgres": "^3.4.5" + }, + "engines": { + "node": ">=24.0.0" + }, + "license": "AGPL-3.0" } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 2a876aa..8052b31 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,15 +1,15 @@

Welcome to SvelteKit

Visit svelte.dev/docs/kit to read the documentation

-

Environment configuration is {envVarsOkay}

\ No newline at end of file +

Environment configuration is {envVarsOkay}

diff --git a/src/routes/api/env/+server.ts b/src/routes/api/env/+server.ts index 2dc274c..8f8699f 100644 --- a/src/routes/api/env/+server.ts +++ b/src/routes/api/env/+server.ts @@ -1,21 +1,19 @@ -import { json } from '@sveltejs/kit'; -import dotenv from 'dotenv'; -import dotenvExpand from 'dotenv-expand' +import { json } from "@sveltejs/kit"; +import dotenv from "dotenv"; +import dotenvExpand from "dotenv-expand"; -const cfg = dotenv.config() -dotenvExpand.expand(cfg) +const cfg = dotenv.config(); +dotenvExpand.expand(cfg); export async function GET() { + let envOkay = true; + if (process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "development") + envOkay = false; + if (!process.env.DATABASE_URL?.startsWith("postgres:")) envOkay = false; - let envOkay = true; - if (process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "development") - envOkay = false; - if (!process.env.DATABASE_URL?.startsWith("postgres:")) - envOkay = false; + console.warn("Environment is okay", envOkay); - console.warn("Environment is okay", envOkay) - - return json({ - envOkay - }); -} \ No newline at end of file + return json({ + envOkay + }); +} diff --git a/src/routes/api/health/+server.ts b/src/routes/api/health/+server.ts index 61b5cb6..4162e15 100644 --- a/src/routes/api/health/+server.ts +++ b/src/routes/api/health/+server.ts @@ -1,5 +1,5 @@ -import { json } from '@sveltejs/kit'; +import { json } from "@sveltejs/kit"; export async function GET() { - return json(true); -} \ No newline at end of file + return json(true); +} diff --git a/src/routes/page.svelte.test.ts b/src/routes/page.svelte.test.ts index 0161070..6bf82c9 100644 --- a/src/routes/page.svelte.test.ts +++ b/src/routes/page.svelte.test.ts @@ -1,9 +1,18 @@ -import { describe, test, expect } from "vitest"; +import { describe, test, expect, vi, beforeEach } from "vitest"; import "@testing-library/jest-dom/vitest"; import { render, screen } from "@testing-library/svelte"; import Page from "./+page.svelte"; describe("/+page.svelte", () => { + beforeEach(() => { + // Mock fetch to avoid the "Invalid URL" error + global.fetch = vi.fn(() => + Promise.resolve({ + json: () => Promise.resolve({ envOkay: true }) + } as Response) + ); + }); + test("should render h1", () => { render(Page); expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument(); diff --git a/svelte.config.js b/svelte.config.js index e0a641e..9d6e59e 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,5 +1,5 @@ -import adapter from '@sveltejs/adapter-node'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +import adapter from "@sveltejs/adapter-node"; +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; /** @type {import('@sveltejs/kit').Config} */ const config = {