Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ build

# Environment
.env
.env.test
**/.env
**/.env.test
.env*.local
**/.env*.local

# Misc
.DS_Store
Expand Down
18 changes: 18 additions & 0 deletions .env.prod.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Production Docker Compose environment.
# Copy to .env.prod and replace every example secret/domain before deploying.

# PostgreSQL
POSTGRES_USER=loreo
POSTGRES_PASSWORD=replace-with-a-random-database-password
POSTGRES_DB=loreo

# Published host ports. The server still listens on 3000 inside the Docker network.
SERVER_PUBLIC_PORT=3002
WEB_PUBLIC_PORT=3001

# Server
CORS_ORIGINS=https://loreo.example.com
JWT_SECRET=replace-with-a-random-secret-at-least-32-characters

# Public web URL used by the server when generating local storage URLs.
PUBLIC_URL=https://loreo.example.com
3 changes: 1 addition & 2 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ jobs:
include:
- image: loreo-web
dockerfile: apps/web/Dockerfile.prod
build-args: |
VITE_API_URL=${{ vars.VITE_API_URL || 'http://localhost:3001' }}
build-args: ''
- image: loreo-server
dockerfile: apps/server/Dockerfile.prod
build-args: ''
Expand Down
1 change: 1 addition & 0 deletions apps/web/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# Optional. Leave unset for same-origin API requests through the web nginx proxy.
VITE_API_URL=http://localhost:3000
1 change: 1 addition & 0 deletions apps/web/.env.test.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# Optional. Leave unset for same-origin API requests through the web nginx proxy.
VITE_API_URL=http://localhost:3000
7 changes: 3 additions & 4 deletions apps/web/Dockerfile.prod
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ COPY apps/web ./apps/web

WORKDIR /app/apps/web

ARG VITE_API_URL
ENV VITE_API_URL=$VITE_API_URL

RUN pnpm build

FROM nginx:alpine AS runner

COPY --from=builder /app/apps/web/dist /usr/share/nginx/html
COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf
COPY apps/web/nginx.conf.template /etc/nginx/templates/default.conf.template

ENV API_UPSTREAM=http://loreo-server:3000

EXPOSE 80

Expand Down
2 changes: 1 addition & 1 deletion apps/web/nginx.conf → apps/web/nginx.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ server {
gzip_static on;

location ~ ^/(health|auth|home|links|highlights|tags|files|imports|doc|reference|openapi\.json) {
proxy_pass http://loreo-server:3000;
proxy_pass ${API_UPSTREAM};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/lib/api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const buildPayload = (body?: RequestBody, headers: object = {}): Options => {

export const instance = ky.create({
credentials: 'include',
prefix: env.API_URL,
prefix: env.API_URL || globalThis.location.origin,
retry: {
limit: 1,
statusCodes: [401, 403, 429, 500, 502, 503, 504]
Expand Down
27 changes: 22 additions & 5 deletions apps/web/src/lib/env.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { afterEach, describe, expect, it, vi } from 'vitest';

async function importEnv(demoMode?: 'true' | 'false') {
type ImportEnvOptions = {
apiUrl?: string;
demoMode?: 'true' | 'false';
};

async function importEnv(options: ImportEnvOptions = {}) {
vi.resetModules();
vi.unstubAllEnvs();
vi.stubEnv('VITE_API_URL', 'http://localhost:3000');
if (demoMode) {
vi.stubEnv('VITE_DEMO_MODE', demoMode);
vi.stubEnv('VITE_API_URL', options.apiUrl ?? '');
if (options.demoMode) {
vi.stubEnv('VITE_DEMO_MODE', options.demoMode);
}

return import('./env');
Expand All @@ -24,8 +29,20 @@ describe('web env', () => {
});

it('parses demo mode on', async () => {
const { env } = await importEnv('true');
const { env } = await importEnv({ demoMode: 'true' });

expect(env.isDemo).toBe(true);
});

it('defaults API URL to same-origin', async () => {
const { env } = await importEnv();

expect(env.API_URL).toBe('');
});

it('allows API URL override', async () => {
const { env } = await importEnv({ apiUrl: 'https://api.example.com' });

expect(env.API_URL).toBe('https://api.example.com');
});
});
2 changes: 1 addition & 1 deletion apps/web/src/lib/env.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as z from 'zod';

const envSchema = z.object({
API_URL: z.string(),
API_URL: z.string().default(''),
DEMO_MODE: z
.enum(['true', 'false'])
.default('false')
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// <reference types="vite/client" />

interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_API_URL?: string;
// more env variables...
}

Expand Down
13 changes: 5 additions & 8 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
loreo-postgres:
image: postgres:16-alpine
image: postgres:17-alpine
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
Expand Down Expand Up @@ -42,7 +42,7 @@ services:
image: ghcr.io/technowizard/loreo-server:latest
restart: always
ports:
- '3000:3000'
- '${SERVER_PUBLIC_PORT:-3000}:3000'
environment:
NODE_ENV: production
DATABASE_HOST: loreo-postgres
Expand Down Expand Up @@ -73,15 +73,12 @@ services:
condition: service_started

loreo-web:
build:
context: .
dockerfile: apps/web/Dockerfile.prod
args:
VITE_API_URL: ${VITE_API_URL:-http://localhost:3001}
image: ghcr.io/technowizard/loreo-web:latest
restart: always
ports:
- '3001:80'
- '${WEB_PUBLIC_PORT:-3001}:80'
environment:
API_UPSTREAM: http://loreo-server:3000
networks:
- frontend
depends_on:
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
postgres:
image: postgres:16-alpine
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
Expand Down
Loading