Docker deployment

This commit is contained in:
Wayne
2025-07-24 23:43:38 +03:00
parent 7646f39721
commit 946da7925b
14 changed files with 163 additions and 108 deletions

46
.dockerignore Normal file
View File

@@ -0,0 +1,46 @@
# Git
.git
.gitignore
# Node
node_modules
.pnpm-store
# Env
.env
.env.*
!/.env.example
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# IDEs
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Docker
docker-compose.yml
.dockerignore
Dockerfile
# Local data
meili_data

View File

@@ -3,6 +3,7 @@ NODE_ENV=development
PORT_BACKEND=4000 PORT_BACKEND=4000
PORT_FRONTEND=3000 PORT_FRONTEND=3000
# PostgreSQL # PostgreSQL
DATABASE_URL="postgresql://admin:password@postgres:5432/open_archive?schema=public" DATABASE_URL="postgresql://admin:password@postgres:5432/open_archive?schema=public"

View File

@@ -1,88 +0,0 @@
version: '3.8'
services:
frontend:
build:
context: ./packages/frontend
dockerfile: Dockerfile
ports:
- '3000:3000'
depends_on:
- backend-api
env_file:
- ./.env
backend-api:
build:
context: ./packages/backend
dockerfile: Dockerfile
ports:
- '4000:4000'
depends_on:
- postgres
- redis
env_file:
- ./.env
ingestion-worker:
build:
context: ./packages/backend
dockerfile: Dockerfile
command: 'pnpm ts-node-dev --respawn --transpile-only src/workers/ingestion.worker.ts'
depends_on:
- postgres
- redis
env_file:
- ./.env
indexing-worker:
build:
context: ./packages/backend
dockerfile: Dockerfile
command: 'pnpm ts-node-dev --respawn --transpile-only src/workers/indexing.worker.ts'
depends_on:
- postgres
- redis
env_file:
- ./.env
sync-scheduler:
build:
context: ./packages/backend
dockerfile: Dockerfile
command: 'pnpm ts-node-dev --respawn --transpile-only src/jobs/schedulers/sync-scheduler.ts'
depends_on:
- postgres
- redis
env_file:
- ./.env
postgres:
image: postgres:15
ports:
- '5432:5432'
volumes:
- postgres_data:/var/lib/postgresql/data
env_file:
- ./.env
redis:
image: redis:7
ports:
- '6379:6379'
volumes:
- redis_data:/data
meilisearch:
image: getmeili/meilisearch:v1.3
ports:
- '7700:7700'
volumes:
- meili_data:/meili_data
env_file:
- ./.env
volumes:
postgres_data:
redis_data:
meili_data:

53
docker/Dockerfile Normal file
View File

@@ -0,0 +1,53 @@
# Dockerfile for Open Archiver
# 1. Build Stage: Install all dependencies and build the project
FROM node:22-alpine AS build
WORKDIR /app
# Install pnpm
RUN npm install -g pnpm
# Copy manifests and lockfile
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./
COPY packages/backend/package.json ./packages/backend/
COPY packages/frontend/package.json ./packages/frontend/
COPY packages/types/package.json ./packages/types/
# Install all dependencies
RUN pnpm install --frozen-lockfile --prod=false
# Copy the rest of the source code
COPY . .
# Build all packages
RUN pnpm build
# 2. Production Stage: Install only production dependencies and copy built artifacts
FROM node:22-alpine AS production
WORKDIR /app
# Install pnpm
RUN npm install -g pnpm
# Copy manifests and lockfile
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./
COPY packages/backend/package.json ./packages/backend/
COPY packages/frontend/package.json ./packages/frontend/
COPY packages/types/package.json ./packages/types/
# Install only production dependencies
RUN pnpm install --frozen-lockfile --prod
# Copy built application from build stage
COPY --from=build /app/packages/backend/dist ./packages/backend/dist
COPY --from=build /app/packages/frontend/build ./packages/frontend/build
COPY --from=build /app/packages/types/dist ./packages/types/dist
COPY --from=build /app/packages/backend/drizzle.config.ts ./packages/backend/drizzle.config.ts
COPY --from=build /app/packages/backend/src/database/migrations ./packages/backend/src/database/migrations
# Expose the port the app runs on
EXPOSE 4000
EXPOSE 3000
# Start the application
CMD ["pnpm", "docker-start"]

View File

@@ -4,11 +4,19 @@
"scripts": { "scripts": {
"dev": "dotenv -- pnpm --filter \"./packages/*\" --parallel dev", "dev": "dotenv -- pnpm --filter \"./packages/*\" --parallel dev",
"build": "pnpm --filter \"./packages/*\" --parallel build", "build": "pnpm --filter \"./packages/*\" --parallel build",
"start:workers": "dotenv -- concurrently \"pnpm --filter @open-archiver/backend start:ingestion-worker\" \"pnpm --filter @open-archiver/backend start:indexing-worker\" \"pnpm --filter @open-archiver/backend start:sync-scheduler\"" "start": "dotenv -- pnpm --filter \"./packages/*\" --parallel start",
"start:workers": "dotenv -- concurrently \"pnpm --filter @open-archiver/backend start:ingestion-worker\" \"pnpm --filter @open-archiver/backend start:indexing-worker\" \"pnpm --filter @open-archiver/backend start:sync-scheduler\"",
"start:workers:dev": "dotenv -- concurrently \"pnpm --filter @open-archiver/backend start:ingestion-worker:dev\" \"pnpm --filter @open-archiver/backend start:indexing-worker:dev\" \"pnpm --filter @open-archiver/backend start:sync-scheduler:dev\"",
"db:generate": "dotenv -- pnpm --filter @open-archiver/backend db:generate",
"db:migrate": "dotenv -- pnpm --filter @open-archiver/backend db:migrate",
"db:migrate:dev": "dotenv -- pnpm --filter @open-archiver/backend db:migrate:dev",
"docker-start": "pnpm db:migrate && concurrently \"pnpm start:workers\" \"pnpm start\""
},
"dependencies": {
"concurrently": "^9.2.0",
"dotenv-cli": "8.0.0"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^9.2.0",
"dotenv-cli": "8.0.0",
"typescript": "5.8.3" "typescript": "5.8.3"
}, },
"packageManager": "pnpm@10.13.1", "packageManager": "pnpm@10.13.1",

View File

@@ -1,7 +1,7 @@
import { defineConfig } from 'drizzle-kit'; import { defineConfig } from 'drizzle-kit';
import { config } from 'dotenv'; import { config } from 'dotenv';
config({ path: '../../.env' }); config();
if (!process.env.DATABASE_URL) { if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is not set in the .env file'); throw new Error('DATABASE_URL is not set in the .env file');

View File

@@ -6,16 +6,20 @@
"scripts": { "scripts": {
"dev": "ts-node-dev --respawn --transpile-only src/index.ts ", "dev": "ts-node-dev --respawn --transpile-only src/index.ts ",
"build": "tsc", "build": "tsc",
"prestart": "npm run build",
"start": "node dist/index.js", "start": "node dist/index.js",
"start:ingestion-worker": "ts-node-dev --respawn --transpile-only src/workers/ingestion.worker.ts", "start:ingestion-worker": "node dist/workers/ingestion.worker.js",
"start:indexing-worker": "ts-node-dev --respawn --transpile-only src/workers/indexing.worker.ts", "start:indexing-worker": "node dist/workers/indexing.worker.js",
"start:sync-scheduler": "ts-node-dev --respawn --transpile-only src/jobs/schedulers/sync-scheduler.ts", "start:sync-scheduler": "node dist/jobs/schedulers/sync-scheduler.js",
"start:ingestion-worker:dev": "ts-node-dev --respawn --transpile-only src/workers/ingestion.worker.ts",
"start:indexing-worker:dev": "ts-node-dev --respawn --transpile-only src/workers/indexing.worker.ts",
"start:sync-scheduler:dev": "ts-node-dev --respawn --transpile-only src/jobs/schedulers/sync-scheduler.ts",
"db:generate": "drizzle-kit generate --config=drizzle.config.ts", "db:generate": "drizzle-kit generate --config=drizzle.config.ts",
"db:push": "drizzle-kit push --config=drizzle.config.ts", "db:push": "drizzle-kit push --config=drizzle.config.ts",
"db:migrate": "ts-node-dev src/database/migrate.ts" "db:migrate": "node dist/database/migrate.js",
"db:migrate:dev": "ts-node-dev src/database/migrate.ts"
}, },
"dependencies": { "dependencies": {
"drizzle-kit": "^0.31.4",
"@aws-sdk/client-s3": "^3.844.0", "@aws-sdk/client-s3": "^3.844.0",
"@aws-sdk/lib-storage": "^3.844.0", "@aws-sdk/lib-storage": "^3.844.0",
"@azure/msal-node": "^3.6.3", "@azure/msal-node": "^3.6.3",
@@ -55,7 +59,6 @@
"@types/microsoft-graph": "^2.40.1", "@types/microsoft-graph": "^2.40.1",
"@types/node": "^24.0.12", "@types/node": "^24.0.12",
"bull-board": "^2.1.3", "bull-board": "^2.1.3",
"drizzle-kit": "^0.31.4",
"ts-node-dev": "^2.0.0", "ts-node-dev": "^2.0.0",
"typescript": "^5.8.3" "typescript": "^5.8.3"
} }

View File

@@ -3,7 +3,7 @@ import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres'; import postgres from 'postgres';
import { config } from 'dotenv'; import { config } from 'dotenv';
config({ path: '../../.env' }); config();
const runMigrate = async () => { const runMigrate = async () => {
if (!process.env.DATABASE_URL) { if (!process.env.DATABASE_URL) {

File diff suppressed because one or more lines are too long

View File

@@ -6,6 +6,7 @@
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
"build": "vite build", "build": "vite build",
"start": "node build/index.js",
"preview": "vite preview", "preview": "vite preview",
"prepare": "svelte-kit sync || echo ''", "prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
@@ -19,18 +20,21 @@
"jose": "^6.0.1", "jose": "^6.0.1",
"lucide-svelte": "^0.525.0", "lucide-svelte": "^0.525.0",
"postal-mime": "^2.4.4", "postal-mime": "^2.4.4",
"svelte-persisted-store": "^0.12.0" "svelte-persisted-store": "^0.12.0",
"@sveltejs/kit": "^2.16.0",
"bits-ui": "^2.8.10",
"clsx": "^2.1.1",
"tailwind-merge": "^3.3.1",
"tailwind-variants": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@internationalized/date": "^3.8.2", "@internationalized/date": "^3.8.2",
"@lucide/svelte": "^0.515.0", "@lucide/svelte": "^0.515.0",
"@sveltejs/adapter-auto": "^6.0.0", "@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/kit": "^2.16.0", "@sveltejs/adapter-node": "^5.2.13",
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/vite": "^4.0.0", "@tailwindcss/vite": "^4.0.0",
"@types/d3-shape": "^3.1.7", "@types/d3-shape": "^3.1.7",
"bits-ui": "^2.8.10",
"clsx": "^2.1.1",
"dotenv": "^17.2.0", "dotenv": "^17.2.0",
"layerchart": "2.0.0-next.27", "layerchart": "2.0.0-next.27",
"prettier": "^3.4.2", "prettier": "^3.4.2",
@@ -38,8 +42,6 @@
"prettier-plugin-tailwindcss": "^0.6.11", "prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^5.0.0", "svelte": "^5.0.0",
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
"tailwind-merge": "^3.3.1",
"tailwind-variants": "^1.0.0",
"tailwindcss": "^4.0.0", "tailwindcss": "^4.0.0",
"tw-animate-css": "^1.3.5", "tw-animate-css": "^1.3.5",
"typescript": "^5.0.0", "typescript": "^5.0.0",

View File

@@ -1,5 +1,4 @@
import { authStore } from '$lib/stores/auth.store'; import { authStore } from '$lib/stores/auth.store';
import type { User } from '@open-archiver/types';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
const BASE_URL = '/api/v1'; // Using a relative URL for proxying const BASE_URL = '/api/v1'; // Using a relative URL for proxying

View File

@@ -2,6 +2,7 @@ import type { RequestEvent } from '@sveltejs/kit';
const BASE_URL = '/api/v1'; // Using a relative URL for proxying const BASE_URL = '/api/v1'; // Using a relative URL for proxying
/** /**
* A custom fetch wrapper for the server-side to automatically handle authentication headers. * A custom fetch wrapper for the server-side to automatically handle authentication headers.
* @param url The URL to fetch, relative to the API base. * @param url The URL to fetch, relative to the API base.

View File

@@ -0,0 +1,30 @@
import { env } from '$env/dynamic/private';
import type { RequestHandler } from '@sveltejs/kit';
const BACKEND_URL = `http://localhost:${env.PORT_BACKEND || 4000}`;
const handleRequest: RequestHandler = async ({ request, params }) => {
const url = new URL(request.url);
const slug = params.slug || '';
const targetUrl = `${BACKEND_URL}/${slug}${url.search}`;
// Create a new request with the same method, headers, and body
const proxyRequest = new Request(targetUrl, {
method: request.method,
headers: request.headers,
body: request.body,
duplex: 'half' // Required for streaming request bodies
} as RequestInit);
// Forward the request to the backend
const response = await fetch(proxyRequest);
// Return the response from the backend
return response;
};
export const GET = handleRequest;
export const POST = handleRequest;
export const PUT = handleRequest;
export const PATCH = handleRequest;
export const DELETE = handleRequest;

View File

@@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto'; import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */