diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0fb12e8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,33 @@ +node_modules + +# Output +.output +.vercel +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +# SQLite +*.db + +# Git +/.git + +# IntelliJ +/.idea + +# Bruno (API Docs) +/bruno diff --git a/.env.example b/.env.example index c01debe..bdcb8f5 100644 --- a/.env.example +++ b/.env.example @@ -16,3 +16,5 @@ IP_MAX_INDEX=100 VPN_ENDPOINT=vpn.lab.cazzzer.com:51820 VPN_DNS=10.18.11.1,fd00:10:18:11::1 MAX_CLIENTS_PER_USER=20 + +ORIGIN=http://localhost:5173 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4cd40ea --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +# use the official Bun image +# see all versions at https://hub.docker.com/r/oven/bun/tags +FROM oven/bun:1-alpine AS base +WORKDIR /app +COPY package.json bun.lockb /app/ + +# install dependencies into temp directory +# this will cache them and speed up future builds +FROM base AS install +RUN mkdir -p /temp/dev +COPY package.json bun.lockb /temp/dev/ +RUN cd /temp/dev && bun install --frozen-lockfile + +# install with --production (exclude devDependencies) +RUN mkdir -p /temp/prod +COPY package.json bun.lockb /temp/prod/ +RUN cd /temp/prod && bun install --frozen-lockfile --production + +# copy node_modules from temp directory +# then copy all (non-ignored) project files into the image +FROM base AS builder +COPY --from=install /temp/dev/node_modules /app/node_modules +COPY . /app +RUN bun run build + +FROM base +# Metadata +LABEL org.opencontainers.image.title="VPGen" +LABEL org.opencontainers.image.description="A VPN config generator built with SvelteKit." +LABEL org.opencontainers.image.url="https://gitea.cazzzer.com/CaZzzer/vpgen" +LABEL org.opencontainers.image.source="https://gitea.cazzzer.com/CaZzzer/vpgen" +LABEL org.opencontainers.image.version="0.1" + +COPY ./entrypoint.sh /entrypoint.sh +COPY --from=install /temp/prod/node_modules /app/node_modules +COPY --from=builder /app/build /app/build +COPY --from=builder /app/drizzle /app/drizzle +COPY --from=builder /app/drizzle.config.ts /app/ + +EXPOSE 3000 + +# entrypoint for drizzle migrations +ENTRYPOINT ["sh", "/entrypoint.sh"] + +CMD ["bun", "./build"] diff --git a/README.md b/README.md index b5b2950..0d62b61 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,5 @@ npm run build You can preview the production build with `npm run preview`. > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. + +When deploying, set `ORIGIN` to the URL of your site to prevent cross-site request errors. diff --git a/bun.lockb b/bun.lockb index 476dd0b..6c477e6 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..d33f1e8 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +# Run database migrations +bun run db:migrate + +# Execute the CMD passed to the container +exec "$@" diff --git a/package.json b/package.json index ab6269a..0c3e24d 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,6 @@ "autoprefixer": "^10.4.20", "bits-ui": "^0.21.16", "clsx": "^2.1.1", - "drizzle-kit": "^0.30.1", - "drizzle-orm": "^0.38.2", "eslint": "^9.7.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.36.0", @@ -55,6 +53,8 @@ "vite": "^5.0.3" }, "dependencies": { - "@libsql/client": "^0.14.0" + "@libsql/client": "^0.14.0", + "drizzle-kit": "^0.30.1", + "drizzle-orm": "^0.38.2" } } diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 72151c6..ae1ad34 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,7 +1,11 @@ import { type Handle, redirect } from '@sveltejs/kit'; +import { sequence } from '@sveltejs/kit/hooks'; import { dev } from '$app/environment'; import * as auth from '$lib/server/auth'; -import { sequence } from '@sveltejs/kit/hooks'; +import { fetchOpnsenseServer } from '$lib/server/opnsense'; + +// fetch opnsense server info on startup +await fetchOpnsenseServer(); const handleAuth: Handle = async ({ event, resolve }) => { const sessionId = event.cookies.get(auth.sessionCookieName); diff --git a/src/lib/server/opnsense/index.ts b/src/lib/server/opnsense/index.ts index f2a3ee4..6f4618b 100644 --- a/src/lib/server/opnsense/index.ts +++ b/src/lib/server/opnsense/index.ts @@ -12,33 +12,38 @@ export const opnsenseIfname = env.OPNSENSE_WG_IFNAME; // unset secret for security if (!dev) env.OPNSENSE_API_SECRET = ''; +export let serverUuid: string, serverPublicKey: string; + +export async function fetchOpnsenseServer() { // this might be pretty bad if the server is down and in a bunch of other cases // TODO: write a retry loop later -const resServers = await fetch(`${opnsenseUrl}/api/wireguard/client/list_servers`, { - method: 'GET', - headers: { - Authorization: opnsenseAuth, - Accept: 'application/json', - }, -}); -assert(resServers.ok, 'Failed to fetch OPNsense WireGuard servers'); -const servers = (await resServers.json()) as OpnsenseWgServers; -assert.equal(servers.status, 'ok', 'Failed to fetch OPNsense WireGuard servers'); -export const serverUuid = servers.rows.find((server) => server.name === opnsenseIfname)?.uuid; -assert(serverUuid, 'Failed to find server UUID for OPNsense WireGuard server'); -console.log('OPNsense WireGuard server UUID:', serverUuid); - -const resServerInfo = await fetch( - `${opnsenseUrl}/api/wireguard/client/get_server_info/${serverUuid}`, - { + const resServers = await fetch(`${opnsenseUrl}/api/wireguard/client/list_servers`, { method: 'GET', headers: { Authorization: opnsenseAuth, Accept: 'application/json', }, - }, -); -assert(resServerInfo.ok, 'Failed to fetch OPNsense WireGuard server info'); -const serverInfo = await resServerInfo.json(); -assert.equal(serverInfo.status, 'ok', 'Failed to fetch OPNsense WireGuard server info'); -export const serverPublicKey = serverInfo['pubkey']; + }); + assert(resServers.ok, 'Failed to fetch OPNsense WireGuard servers'); + const servers = (await resServers.json()) as OpnsenseWgServers; + assert.equal(servers.status, 'ok', 'Failed to fetch OPNsense WireGuard servers'); + const uuid = servers.rows.find((server) => server.name === opnsenseIfname)?.uuid; + assert(uuid, 'Failed to find server UUID for OPNsense WireGuard server'); + serverUuid = uuid; + console.log('OPNsense WireGuard server UUID:', serverUuid); + + const resServerInfo = await fetch( + `${opnsenseUrl}/api/wireguard/client/get_server_info/${serverUuid}`, + { + method: 'GET', + headers: { + Authorization: opnsenseAuth, + Accept: 'application/json', + }, + }, + ); + assert(resServerInfo.ok, 'Failed to fetch OPNsense WireGuard server info'); + const serverInfo = await resServerInfo.json(); + assert.equal(serverInfo.status, 'ok', 'Failed to fetch OPNsense WireGuard server info'); + serverPublicKey = serverInfo['pubkey']; +}