refactor: move environment variables to runtime for secure containerized builds
Some checks failed
CI / build-and-deploy (push) Failing after 47s
Some checks failed
CI / build-and-deploy (push) Failing after 47s
Change from $env/static/private to $env/dynamic/private for all environment variables. This allows building in CI without embedding secrets in build artifacts, while keeping secrets secure on the server at runtime. Changes: - Refactor auth configuration to use dynamic env vars - Move database connection string to runtime - Update image API routes to read IMAGE_DIR at runtime - Add .env.example for documentation This enables the containerized build workflow to succeed without requiring a .env file during build, as secrets are only needed when the application starts on the server.
This commit is contained in:
24
.env.example
Normal file
24
.env.example
Normal file
@@ -0,0 +1,24 @@
|
||||
# Database Configuration
|
||||
MONGO_URL="mongodb://user:password@host:port/database?authSource=admin"
|
||||
|
||||
# Authentication Secrets (runtime only - not embedded in build)
|
||||
AUTHENTIK_ID="your-authentik-client-id"
|
||||
AUTHENTIK_SECRET="your-authentik-client-secret"
|
||||
|
||||
# Static Configuration (embedded in build - OK to be public)
|
||||
AUTHENTIK_ISSUER="https://sso.example.com/application/o/your-app/"
|
||||
|
||||
# File Storage
|
||||
IMAGE_DIR="/path/to/static/files"
|
||||
|
||||
# Optional: Development Settings
|
||||
# DEV_DISABLE_AUTH="true"
|
||||
# ORIGIN="http://127.0.0.1:3000"
|
||||
|
||||
# Optional: Additional Configuration
|
||||
# BEARER_TOKEN="your-bearer-token"
|
||||
# COOKIE_SECRET="your-cookie-secret"
|
||||
# PEPPER="your-pepper-value"
|
||||
# ALLOW_REGISTRATION="1"
|
||||
# AUTH_SECRET="your-auth-secret"
|
||||
# USDA_API_KEY="your-usda-api-key"
|
||||
@@ -1,13 +1,13 @@
|
||||
import { SvelteKitAuth } from "@auth/sveltekit"
|
||||
import Authentik from "@auth/core/providers/authentik"
|
||||
import { AUTHENTIK_ID, AUTHENTIK_SECRET, AUTHENTIK_ISSUER } from "$env/static/private";
|
||||
import { env } from "$env/dynamic/private";
|
||||
|
||||
export const { handle, signIn, signOut } = SvelteKitAuth({
|
||||
providers: [
|
||||
Authentik({
|
||||
clientId: AUTHENTIK_ID,
|
||||
clientSecret: AUTHENTIK_SECRET,
|
||||
issuer: AUTHENTIK_ISSUER,
|
||||
clientId: env.AUTHENTIK_ID,
|
||||
clientSecret: env.AUTHENTIK_SECRET,
|
||||
issuer: env.AUTHENTIK_ISSUER,
|
||||
})],
|
||||
callbacks: {
|
||||
// this feels like an extremely hacky way to get nickname and groups into the session object
|
||||
|
||||
@@ -3,7 +3,6 @@ import { redirect } from "@sveltejs/kit"
|
||||
import { error } from "@sveltejs/kit"
|
||||
import { SvelteKitAuth } from "@auth/sveltekit"
|
||||
import Authentik from "@auth/core/providers/authentik"
|
||||
import { AUTHENTIK_ID, AUTHENTIK_SECRET, AUTHENTIK_ISSUER } from "$env/static/private";
|
||||
import { sequence } from "@sveltejs/kit/hooks"
|
||||
import * as auth from "./auth"
|
||||
import { initializeScheduler } from "./lib/server/scheduler"
|
||||
|
||||
@@ -3,7 +3,7 @@ import { error, json } from '@sveltejs/kit';
|
||||
import { writeFileSync, mkdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { IMAGE_DIR } from '$env/static/private';
|
||||
import { env } from '$env/dynamic/private';
|
||||
|
||||
export const POST: RequestHandler = async ({ request, locals }) => {
|
||||
const auth = await locals.auth();
|
||||
@@ -30,13 +30,13 @@ export const POST: RequestHandler = async ({ request, locals }) => {
|
||||
|
||||
const extension = image.type.split('/')[1];
|
||||
const filename = `${randomUUID()}.${extension}`;
|
||||
|
||||
if (!IMAGE_DIR) {
|
||||
|
||||
if (!env.IMAGE_DIR) {
|
||||
throw error(500, 'IMAGE_DIR environment variable not configured');
|
||||
}
|
||||
|
||||
|
||||
// Ensure cospend directory exists in IMAGE_DIR
|
||||
const uploadsDir = join(IMAGE_DIR, 'cospend');
|
||||
const uploadsDir = join(env.IMAGE_DIR, 'cospend');
|
||||
try {
|
||||
mkdirSync(uploadsDir, { recursive: true });
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'path'
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { IMAGE_DIR } from '$env/static/private'
|
||||
import { env } from '$env/dynamic/private'
|
||||
import sharp from 'sharp';
|
||||
|
||||
export const POST = (async ({ request, locals}) => {
|
||||
@@ -20,21 +20,21 @@ export const POST = (async ({ request, locals}) => {
|
||||
//}
|
||||
await sharp(full_res)
|
||||
.toFormat('webp')
|
||||
.toFile(path.join(IMAGE_DIR,
|
||||
.toFile(path.join(env.IMAGE_DIR,
|
||||
"rezepte",
|
||||
"full",
|
||||
data.name + ".webp"))
|
||||
await sharp(full_res)
|
||||
.resize({ width: 800})
|
||||
.toFormat('webp')
|
||||
.toFile(path.join(IMAGE_DIR,
|
||||
.toFile(path.join(env.IMAGE_DIR,
|
||||
"rezepte",
|
||||
"thumb",
|
||||
data.name + ".webp"))
|
||||
await sharp(full_res)
|
||||
.resize({ width: 20})
|
||||
.toFormat('webp')
|
||||
.toFile(path.join(IMAGE_DIR,
|
||||
.toFile(path.join(env.IMAGE_DIR,
|
||||
"rezepte",
|
||||
"placeholder",
|
||||
data.name + ".webp"))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import path from 'path'
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { IMAGE_DIR } from '$env/static/private'
|
||||
import { env } from '$env/dynamic/private'
|
||||
import { unlink } from 'node:fs';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
@@ -10,7 +10,7 @@ export const POST = (async ({ request, locals}) => {
|
||||
if(!auth) throw error(401, "You need to be logged in")
|
||||
|
||||
[ "full", "thumb", "placeholder"].forEach((folder) => {
|
||||
unlink(path.join(IMAGE_DIR, "rezepte", folder, data.name + ".webp"), (e) => {
|
||||
unlink(path.join(env.IMAGE_DIR, "rezepte", folder, data.name + ".webp"), (e) => {
|
||||
if(e) error(404, "could not delete: " + folder + "/" + data.name + ".webp" + e)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import path from 'path'
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { IMAGE_DIR } from '$env/static/private'
|
||||
import { env } from '$env/dynamic/private'
|
||||
import { rename } from 'node:fs';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
@@ -10,8 +10,8 @@ export const POST = (async ({ request, locals}) => {
|
||||
if(!auth ) throw error(401, "need to be logged in")
|
||||
|
||||
[ "full", "thumb", "placeholder"].forEach((folder) => {
|
||||
const old_path = path.join(IMAGE_DIR, "rezepte", folder, data.old_name + ".webp")
|
||||
rename(old_path, path.join(IMAGE_DIR, "rezepte", folder, data.new_name + ".webp"), (e) => {
|
||||
const old_path = path.join(env.IMAGE_DIR, "rezepte", folder, data.old_name + ".webp")
|
||||
rename(old_path, path.join(env.IMAGE_DIR, "rezepte", folder, data.new_name + ".webp"), (e) => {
|
||||
console.log(e)
|
||||
if(e) throw error(500, "could not mv: " + old_path)
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { MONGO_URL } from '$env/static/private';
|
||||
import { env } from '$env/dynamic/private';
|
||||
|
||||
let isConnected = false;
|
||||
|
||||
@@ -17,7 +17,7 @@ export const dbConnect = async () => {
|
||||
socketTimeoutMS: 45000, // Close sockets after 45 seconds of inactivity
|
||||
};
|
||||
|
||||
const connection = await mongoose.connect(MONGO_URL ?? '', options);
|
||||
const connection = await mongoose.connect(env.MONGO_URL ?? '', options);
|
||||
|
||||
isConnected = true;
|
||||
console.log('MongoDB connected with persistent connection');
|
||||
|
||||
Reference in New Issue
Block a user