chore: smaller docker image, simplification pass on environment variables, fix direct navigation without vite
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 46s
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 46s
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
export const PROTOCOL = Deno.env.get("GERBEUR_PROTOCOL") || "http";
|
||||
export const HOSTNAME = Deno.env.get("GERBEUR_HOSTNAME") || "localhost";
|
||||
export const PORT = Number(Deno.env.get("GERBEUR_PORT")) || 8000;
|
||||
export const SMTPS_URL = Deno.env.get("GERBEUR_SMTPS_URL")?.trim() || "";
|
||||
export const FROM_EMAIL = Deno.env.get("GERBEUR_FROM_EMAIL")?.trim() || "";
|
||||
@@ -18,12 +16,18 @@ export const JWT_SECRET = Deno.env.get("GERBEUR_JWT_SECRET")?.trim() || "";
|
||||
// Defaults to 0.0.0.0 so Docker port-forwarding works out of the box.
|
||||
// Set to 127.0.0.1 to restrict to loopback only.
|
||||
export const LISTEN_HOST = Deno.env.get("GERBEUR_LISTEN_HOST") || "0.0.0.0";
|
||||
export const BASE_URL = `${PROTOCOL}://${HOSTNAME}:${PORT}`;
|
||||
// Public-facing URL of the server — used for CORS, WebSocket origin checks,
|
||||
// email links, and OG image URLs. Behind a reverse proxy, set this to the
|
||||
// externally-visible URL (e.g. https://example.com). No trailing slash.
|
||||
// Defaults to http://localhost:PORT for local development.
|
||||
export const PUBLIC_URL =
|
||||
Deno.env.get("GERBEUR_PUBLIC_URL")?.trim().replace(/\/$/, "") ||
|
||||
`http://localhost:${PORT}`;
|
||||
// In single-container deployments the API serves the frontend, so FRONTEND_URL
|
||||
// equals BASE_URL. Override with GERBEUR_FRONTEND_URL when running the frontend
|
||||
// on a separate host/port (e.g. Vite dev server or a dedicated CDN origin).
|
||||
// equals PUBLIC_URL. Override with GERBEUR_FRONTEND_URL when running the
|
||||
// frontend on a separate host/port (e.g. Vite dev server or a CDN origin).
|
||||
export const FRONTEND_URL = Deno.env.get("GERBEUR_FRONTEND_URL")?.trim() ||
|
||||
BASE_URL;
|
||||
PUBLIC_URL;
|
||||
export const DB_PATH = "api/sql/gerbeur.db";
|
||||
|
||||
// Upload/files
|
||||
@@ -87,15 +91,13 @@ export const VALIDATION = {
|
||||
// SEO/OG
|
||||
export const OG_SITE_NAME = Deno.env.get("GERBEUR_SITE_NAME") || "gerbeur";
|
||||
|
||||
const rawOrigins = Deno.env.get("GERBEUR_ALLOWED_ORIGINS") ??
|
||||
"http://localhost:3000";
|
||||
const rawOrigins = Deno.env.get("GERBEUR_ALLOWED_ORIGINS") ?? "";
|
||||
export const ALLOWED_ORIGINS: string[] = Array.from(
|
||||
new Set([
|
||||
BASE_URL,
|
||||
...(
|
||||
rawOrigins
|
||||
? rawOrigins.split(",").map((o) => o.trim()).filter(Boolean)
|
||||
: []
|
||||
),
|
||||
PUBLIC_URL,
|
||||
// FRONTEND_URL is auto-included so the separate-frontend case only needs
|
||||
// GERBEUR_FRONTEND_URL — no need to repeat it in GERBEUR_ALLOWED_ORIGINS.
|
||||
FRONTEND_URL,
|
||||
...rawOrigins.split(",").map((o) => o.trim()).filter(Boolean),
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -14,6 +14,8 @@ export function routeStaticFilesFrom(staticPaths: string[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// SPA fallback: serve index.html so client-side routes work on direct navigation
|
||||
await send(context, "/index.html", { root: staticPaths[0] });
|
||||
await next();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import notificationsRouter from "./routes/notifications.ts";
|
||||
import invitesRouter from "./routes/invites.ts";
|
||||
import searchRouter from "./routes/search.ts";
|
||||
|
||||
import { ALLOWED_ORIGINS, BASE_URL, LISTEN_HOST, PORT } from "./config.ts";
|
||||
import { ALLOWED_ORIGINS, LISTEN_HOST, PORT, PUBLIC_URL } from "./config.ts";
|
||||
import { errorMiddleware } from "./middleware/error.ts";
|
||||
import { ogMiddleware } from "./middleware/og.ts";
|
||||
import { routeStaticFilesFrom } from "./lib/static.ts";
|
||||
@@ -100,7 +100,7 @@ app.use(routeStaticFilesFrom([
|
||||
|
||||
app.addEventListener(
|
||||
"listen",
|
||||
() => console.log(`Server listening on ${BASE_URL}`),
|
||||
() => console.log(`Server listening on ${PUBLIC_URL}`),
|
||||
);
|
||||
|
||||
app.addEventListener(
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { RichContentProvider } from "../rich-content-service.ts";
|
||||
import { getDump } from "../dump-service.ts";
|
||||
import { getUserByUsername } from "../user-service.ts";
|
||||
import { getPlaylistById } from "../playlist-service.ts";
|
||||
import { BASE_URL } from "../../config.ts";
|
||||
import { PUBLIC_URL } from "../../config.ts";
|
||||
|
||||
const DUMP_RE = /^\/dumps\/([^/]+)$/;
|
||||
const USER_RE = /^\/users\/([^/]+)$/;
|
||||
@@ -31,7 +31,7 @@ export const selfProvider: RichContentProvider = {
|
||||
const dump = getDump(dumpMatch[1]);
|
||||
let thumbnailUrl: string | undefined;
|
||||
if (dump.kind === "file" && dump.fileMime?.startsWith("image/")) {
|
||||
thumbnailUrl = `${BASE_URL}/api/files/${dump.id}`;
|
||||
thumbnailUrl = `${PUBLIC_URL}/api/files/${dump.id}`;
|
||||
} else if (dump.richContent?.thumbnailUrl) {
|
||||
thumbnailUrl = dump.richContent.thumbnailUrl;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export const selfProvider: RichContentProvider = {
|
||||
if (userMatch) {
|
||||
const user = getUserByUsername(userMatch[1]);
|
||||
const thumbnailUrl = user.avatarMime
|
||||
? `${BASE_URL}/api/avatars/${user.id}`
|
||||
? `${PUBLIC_URL}/api/avatars/${user.id}`
|
||||
: undefined;
|
||||
return Promise.resolve({
|
||||
type: "generic",
|
||||
@@ -65,7 +65,7 @@ export const selfProvider: RichContentProvider = {
|
||||
if (playlistMatch) {
|
||||
const playlist = getPlaylistById(playlistMatch[1]);
|
||||
const thumbnailUrl = playlist.imageMime
|
||||
? `${BASE_URL}/api/playlists/${playlist.id}/image`
|
||||
? `${PUBLIC_URL}/api/playlists/${playlist.id}/image`
|
||||
: undefined;
|
||||
return Promise.resolve({
|
||||
type: "generic",
|
||||
|
||||
Reference in New Issue
Block a user