Skip to content

Commit 8aad15d

Browse files
committed
Reapply "Merge pull request #686 from AnishSarkar22/feat/replace-logs"
This reverts commit 3418c0e.
1 parent 3418c0e commit 8aad15d

File tree

84 files changed

+8317
-4664
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+8317
-4664
lines changed

.env.example

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ FRONTEND_PORT=3000
99
NEXT_PUBLIC_FASTAPI_BACKEND_URL=http://localhost:8000 (Default: http://localhost:8000)
1010
NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=LOCAL or GOOGLE (Default: LOCAL)
1111
NEXT_PUBLIC_ETL_SERVICE=UNSTRUCTURED or LLAMACLOUD or DOCLING (Default: DOCLING)
12-
1312
# Backend Configuration
1413
BACKEND_PORT=8000
1514

@@ -19,6 +18,17 @@ POSTGRES_PASSWORD=postgres
1918
POSTGRES_DB=surfsense
2019
POSTGRES_PORT=5432
2120

21+
# Electric-SQL Configuration
22+
ELECTRIC_PORT=5133
23+
# PostgreSQL host for Electric connection
24+
# - 'db' for Docker PostgreSQL (service name in docker-compose)
25+
# - 'host.docker.internal' for local PostgreSQL (recommended when Electric runs in Docker)
26+
# Note: host.docker.internal works on Docker Desktop (Mac/Windows) and can be enabled on Linux
27+
POSTGRES_HOST=db
28+
ELECTRIC_DB_USER=electric
29+
ELECTRIC_DB_PASSWORD=electric_password
30+
NEXT_PUBLIC_ELECTRIC_URL=http://localhost:5133
31+
2232
# pgAdmin Configuration
2333
PGADMIN_PORT=5050
2434
PGADMIN_DEFAULT_EMAIL=admin@surfsense.com

Dockerfile.allinone

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# SurfSense All-in-One Docker Image
2-
# This image bundles PostgreSQL+pgvector, Redis, Backend, and Frontend
3-
# Usage: docker run -d -p 3000:3000 -p 8000:8000 -v surfsense-data:/data --name surfsense ghcr.io/modsetter/surfsense:latest
2+
# This image bundles PostgreSQL+pgvector, Redis, Electric SQL, Backend, and Frontend
3+
# Usage: docker run -d -p 3000:3000 -p 8000:8000 -p 5133:5133 -v surfsense-data:/data --name surfsense ghcr.io/modsetter/surfsense:latest
44
#
55
# Included Services (all run locally by default):
66
# - PostgreSQL 14 + pgvector (vector database)
77
# - Redis (task queue)
8+
# - Electric SQL (real-time sync)
89
# - Docling (document processing, CPU-only, OCR disabled)
910
# - Kokoro TTS (local text-to-speech for podcasts)
1011
# - Faster-Whisper (local speech-to-text for audio files)
@@ -14,7 +15,12 @@
1415
# will be available in the future for faster AI inference.
1516

1617
# ====================
17-
# Stage 1: Build Frontend
18+
# Stage 1: Get Electric SQL Binary
19+
# ====================
20+
FROM electricsql/electric:latest AS electric-builder
21+
22+
# ====================
23+
# Stage 2: Build Frontend
1824
# ====================
1925
FROM node:20-alpine AS frontend-builder
2026

@@ -42,12 +48,14 @@ RUN pnpm fumadocs-mdx
4248
ENV NEXT_PUBLIC_FASTAPI_BACKEND_URL=__NEXT_PUBLIC_FASTAPI_BACKEND_URL__
4349
ENV NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=__NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE__
4450
ENV NEXT_PUBLIC_ETL_SERVICE=__NEXT_PUBLIC_ETL_SERVICE__
51+
ENV NEXT_PUBLIC_ELECTRIC_URL=__NEXT_PUBLIC_ELECTRIC_URL__
52+
ENV NEXT_PUBLIC_ELECTRIC_AUTH_MODE=__NEXT_PUBLIC_ELECTRIC_AUTH_MODE__
4553

4654
# Build
4755
RUN pnpm run build
4856

4957
# ====================
50-
# Stage 2: Runtime Image
58+
# Stage 3: Runtime Image
5159
# ====================
5260
FROM ubuntu:22.04 AS runtime
5361

@@ -167,6 +175,11 @@ COPY --from=frontend-builder /app/public ./public
167175

168176
COPY surfsense_web/content/docs /app/surfsense_web/content/docs
169177

178+
# ====================
179+
# Copy Electric SQL Release
180+
# ====================
181+
COPY --from=electric-builder /app /app/electric-release
182+
170183
# ====================
171184
# Setup Backend
172185
# ====================
@@ -238,11 +251,22 @@ ENV NEXT_PUBLIC_FASTAPI_BACKEND_URL=http://localhost:8000
238251
ENV NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=LOCAL
239252
ENV NEXT_PUBLIC_ETL_SERVICE=DOCLING
240253

254+
# Electric SQL configuration (ELECTRIC_DATABASE_URL is built dynamically by entrypoint from these values)
255+
ENV ELECTRIC_DB_USER=electric
256+
ENV ELECTRIC_DB_PASSWORD=electric_password
257+
# Note: ELECTRIC_DATABASE_URL is NOT set here - entrypoint builds it dynamically from ELECTRIC_DB_USER/PASSWORD
258+
ENV ELECTRIC_INSECURE=true
259+
ENV ELECTRIC_WRITE_TO_PG_MODE=direct
260+
ENV ELECTRIC_PORT=5133
261+
ENV PORT=5133
262+
ENV NEXT_PUBLIC_ELECTRIC_URL=http://localhost:5133
263+
ENV NEXT_PUBLIC_ELECTRIC_AUTH_MODE=insecure
264+
241265
# Data volume
242266
VOLUME ["/data"]
243267

244-
# Expose ports
245-
EXPOSE 3000 8000
268+
# Expose ports (Frontend: 3000, Backend: 8000, Electric: 5133)
269+
EXPOSE 3000 8000 5133
246270

247271
# Health check
248272
HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \

docker-compose.yml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@ services:
77
- "${POSTGRES_PORT:-5432}:5432"
88
volumes:
99
- postgres_data:/var/lib/postgresql/data
10+
- ./scripts/docker/postgresql.conf:/etc/postgresql/postgresql.conf:ro
11+
- ./scripts/docker/init-electric-user.sh:/docker-entrypoint-initdb.d/init-electric-user.sh:ro
1012
environment:
1113
- POSTGRES_USER=${POSTGRES_USER:-postgres}
1214
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
1315
- POSTGRES_DB=${POSTGRES_DB:-surfsense}
16+
- ELECTRIC_DB_USER=${ELECTRIC_DB_USER:-electric}
17+
- ELECTRIC_DB_PASSWORD=${ELECTRIC_DB_PASSWORD:-electric_password}
18+
command: postgres -c config_file=/etc/postgresql/postgresql.conf
1419

1520
pgadmin:
1621
image: dpage/pgadmin4
@@ -51,11 +56,14 @@ services:
5156
- UNSTRUCTURED_HAS_PATCHED_LOOP=1
5257
- LANGCHAIN_TRACING_V2=false
5358
- LANGSMITH_TRACING=false
59+
- ELECTRIC_DB_USER=${ELECTRIC_DB_USER:-electric}
60+
- ELECTRIC_DB_PASSWORD=${ELECTRIC_DB_PASSWORD:-electric_password}
61+
- NEXT_FRONTEND_URL=http://frontend:3000
5462
depends_on:
5563
- db
5664
- redis
5765

58-
# Run these services seperately in production
66+
# Run these services separately in production
5967
# celery_worker:
6068
# build: ./surfsense_backend
6169
# # image: ghcr.io/modsetter/surfsense_backend:latest
@@ -110,6 +118,23 @@ services:
110118
# - redis
111119
# - celery_worker
112120

121+
electric:
122+
image: electricsql/electric:latest
123+
ports:
124+
- "${ELECTRIC_PORT:-5133}:3000"
125+
environment:
126+
- DATABASE_URL=${ELECTRIC_DATABASE_URL:-postgresql://${ELECTRIC_DB_USER:-electric}:${ELECTRIC_DB_PASSWORD:-electric_password}@${POSTGRES_HOST:-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-surfsense}?sslmode=disable}
127+
- ELECTRIC_INSECURE=true
128+
- ELECTRIC_WRITE_TO_PG_MODE=direct
129+
restart: unless-stopped
130+
# depends_on:
131+
# - db
132+
healthcheck:
133+
test: ["CMD", "curl", "-f", "http://localhost:3000/v1/health"]
134+
interval: 10s
135+
timeout: 5s
136+
retries: 5
137+
113138
frontend:
114139
build:
115140
context: ./surfsense_web
@@ -122,8 +147,12 @@ services:
122147
- "${FRONTEND_PORT:-3000}:3000"
123148
env_file:
124149
- ./surfsense_web/.env
150+
environment:
151+
- NEXT_PUBLIC_ELECTRIC_URL=${NEXT_PUBLIC_ELECTRIC_URL:-http://localhost:5133}
152+
- NEXT_PUBLIC_ELECTRIC_AUTH_MODE=insecure
125153
depends_on:
126154
- backend
155+
- electric
127156

128157
volumes:
129158
postgres_data:

scripts/docker/entrypoint-allinone.sh

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,31 @@ if [ -z "$STT_SERVICE" ]; then
4242
echo "✅ Using default STT_SERVICE: local/base"
4343
fi
4444

45+
# ================================================
46+
# Set Electric SQL configuration
47+
# ================================================
48+
export ELECTRIC_DB_USER="${ELECTRIC_DB_USER:-electric}"
49+
export ELECTRIC_DB_PASSWORD="${ELECTRIC_DB_PASSWORD:-electric_password}"
50+
if [ -z "$ELECTRIC_DATABASE_URL" ]; then
51+
export ELECTRIC_DATABASE_URL="postgresql://${ELECTRIC_DB_USER}:${ELECTRIC_DB_PASSWORD}@localhost:5432/${POSTGRES_DB:-surfsense}?sslmode=disable"
52+
echo "✅ Electric SQL URL configured dynamically"
53+
else
54+
# Ensure sslmode=disable is in the URL if not already present
55+
if [[ "$ELECTRIC_DATABASE_URL" != *"sslmode="* ]]; then
56+
# Add sslmode=disable (handle both cases: with or without existing query params)
57+
if [[ "$ELECTRIC_DATABASE_URL" == *"?"* ]]; then
58+
export ELECTRIC_DATABASE_URL="${ELECTRIC_DATABASE_URL}&sslmode=disable"
59+
else
60+
export ELECTRIC_DATABASE_URL="${ELECTRIC_DATABASE_URL}?sslmode=disable"
61+
fi
62+
fi
63+
echo "✅ Electric SQL URL configured from environment"
64+
fi
65+
66+
# Set Electric SQL port
67+
export ELECTRIC_PORT="${ELECTRIC_PORT:-5133}"
68+
export PORT="${ELECTRIC_PORT}"
69+
4570
# ================================================
4671
# Initialize PostgreSQL if needed
4772
# ================================================
@@ -60,6 +85,11 @@ if [ ! -f /data/postgres/PG_VERSION ]; then
6085
echo "local all all trust" >> /data/postgres/pg_hba.conf
6186
echo "listen_addresses='*'" >> /data/postgres/postgresql.conf
6287

88+
# Enable logical replication for Electric SQL
89+
echo "wal_level = logical" >> /data/postgres/postgresql.conf
90+
echo "max_replication_slots = 10" >> /data/postgres/postgresql.conf
91+
echo "max_wal_senders = 10" >> /data/postgres/postgresql.conf
92+
6393
# Start PostgreSQL temporarily to create database and user
6494
su - postgres -c "/usr/lib/postgresql/14/bin/pg_ctl -D /data/postgres -l /tmp/postgres_init.log start"
6595

@@ -73,6 +103,35 @@ if [ ! -f /data/postgres/PG_VERSION ]; then
73103
# Enable pgvector extension
74104
su - postgres -c "psql -d ${POSTGRES_DB:-surfsense} -c 'CREATE EXTENSION IF NOT EXISTS vector;'"
75105

106+
# Create Electric SQL replication user (idempotent - uses IF NOT EXISTS)
107+
echo "📡 Creating Electric SQL replication user..."
108+
su - postgres -c "psql -d ${POSTGRES_DB:-surfsense} <<-EOSQL
109+
DO \\\$\\\$
110+
BEGIN
111+
IF NOT EXISTS (SELECT FROM pg_user WHERE usename = '${ELECTRIC_DB_USER}') THEN
112+
CREATE USER ${ELECTRIC_DB_USER} WITH REPLICATION PASSWORD '${ELECTRIC_DB_PASSWORD}';
113+
END IF;
114+
END
115+
\\\$\\\$;
116+
117+
GRANT CONNECT ON DATABASE ${POSTGRES_DB:-surfsense} TO ${ELECTRIC_DB_USER};
118+
GRANT USAGE ON SCHEMA public TO ${ELECTRIC_DB_USER};
119+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ${ELECTRIC_DB_USER};
120+
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO ${ELECTRIC_DB_USER};
121+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO ${ELECTRIC_DB_USER};
122+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON SEQUENCES TO ${ELECTRIC_DB_USER};
123+
124+
-- Create the publication for Electric SQL (if not exists)
125+
DO \\\$\\\$
126+
BEGIN
127+
IF NOT EXISTS (SELECT FROM pg_publication WHERE pubname = 'electric_publication_default') THEN
128+
CREATE PUBLICATION electric_publication_default;
129+
END IF;
130+
END
131+
\\\$\\\$;
132+
EOSQL"
133+
echo "✅ Electric SQL user '${ELECTRIC_DB_USER}' created"
134+
76135
# Stop temporary PostgreSQL
77136
su - postgres -c "/usr/lib/postgresql/14/bin/pg_ctl -D /data/postgres stop"
78137

@@ -107,18 +166,23 @@ echo "🔧 Applying runtime environment configuration..."
107166
NEXT_PUBLIC_FASTAPI_BACKEND_URL="${NEXT_PUBLIC_FASTAPI_BACKEND_URL:-http://localhost:8000}"
108167
NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE="${NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE:-LOCAL}"
109168
NEXT_PUBLIC_ETL_SERVICE="${NEXT_PUBLIC_ETL_SERVICE:-DOCLING}"
169+
NEXT_PUBLIC_ELECTRIC_URL="${NEXT_PUBLIC_ELECTRIC_URL:-http://localhost:5133}"
170+
NEXT_PUBLIC_ELECTRIC_AUTH_MODE="${NEXT_PUBLIC_ELECTRIC_AUTH_MODE:-insecure}"
110171

111172
# Replace placeholders in all JS files
112173
find /app/frontend -type f \( -name "*.js" -o -name "*.json" \) -exec sed -i \
113174
-e "s|__NEXT_PUBLIC_FASTAPI_BACKEND_URL__|${NEXT_PUBLIC_FASTAPI_BACKEND_URL}|g" \
114175
-e "s|__NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE__|${NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE}|g" \
115176
-e "s|__NEXT_PUBLIC_ETL_SERVICE__|${NEXT_PUBLIC_ETL_SERVICE}|g" \
177+
-e "s|__NEXT_PUBLIC_ELECTRIC_URL__|${NEXT_PUBLIC_ELECTRIC_URL}|g" \
178+
-e "s|__NEXT_PUBLIC_ELECTRIC_AUTH_MODE__|${NEXT_PUBLIC_ELECTRIC_AUTH_MODE}|g" \
116179
{} +
117180

118181
echo "✅ Environment configuration applied"
119-
echo " Backend URL: ${NEXT_PUBLIC_FASTAPI_BACKEND_URL}"
120-
echo " Auth Type: ${NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE}"
121-
echo " ETL Service: ${NEXT_PUBLIC_ETL_SERVICE}"
182+
echo " Backend URL: ${NEXT_PUBLIC_FASTAPI_BACKEND_URL}"
183+
echo " Auth Type: ${NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE}"
184+
echo " ETL Service: ${NEXT_PUBLIC_ETL_SERVICE}"
185+
echo " Electric URL: ${NEXT_PUBLIC_ELECTRIC_URL}"
122186

123187
# ================================================
124188
# Run database migrations
@@ -161,6 +225,7 @@ echo "==========================================="
161225
echo " Frontend URL: http://localhost:3000"
162226
echo " Backend API: ${NEXT_PUBLIC_FASTAPI_BACKEND_URL}"
163227
echo " API Docs: ${NEXT_PUBLIC_FASTAPI_BACKEND_URL}/docs"
228+
echo " Electric URL: ${NEXT_PUBLIC_ELECTRIC_URL:-http://localhost:5133}"
164229
echo " Auth Type: ${NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE}"
165230
echo " ETL Service: ${NEXT_PUBLIC_ETL_SERVICE}"
166231
echo " TTS Service: ${TTS_SERVICE}"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/bin/sh
2+
# ============================================================================
3+
# Electric SQL User Initialization Script (docker-compose only)
4+
# ============================================================================
5+
# This script is ONLY used when running via docker-compose.
6+
#
7+
# How it works:
8+
# - docker-compose.yml mounts this script into the PostgreSQL container's
9+
# /docker-entrypoint-initdb.d/ directory
10+
# - PostgreSQL automatically executes scripts in that directory on first
11+
# container initialization
12+
#
13+
# For local PostgreSQL users (non-Docker), this script is NOT used.
14+
# Instead, the Electric user is created by Alembic migration 66
15+
# (66_add_notifications_table_and_electric_replication.py).
16+
#
17+
# Both approaches are idempotent (use IF NOT EXISTS), so running both
18+
# will not cause conflicts.
19+
# ============================================================================
20+
21+
set -e
22+
23+
# Use environment variables with defaults
24+
ELECTRIC_DB_USER="${ELECTRIC_DB_USER:-electric}"
25+
ELECTRIC_DB_PASSWORD="${ELECTRIC_DB_PASSWORD:-electric_password}"
26+
27+
echo "Creating Electric SQL replication user: $ELECTRIC_DB_USER"
28+
29+
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
30+
DO \$\$
31+
BEGIN
32+
IF NOT EXISTS (SELECT FROM pg_user WHERE usename = '$ELECTRIC_DB_USER') THEN
33+
CREATE USER $ELECTRIC_DB_USER WITH REPLICATION PASSWORD '$ELECTRIC_DB_PASSWORD';
34+
END IF;
35+
END
36+
\$\$;
37+
38+
GRANT CONNECT ON DATABASE $POSTGRES_DB TO $ELECTRIC_DB_USER;
39+
GRANT CREATE ON DATABASE $POSTGRES_DB TO $ELECTRIC_DB_USER;
40+
GRANT USAGE ON SCHEMA public TO $ELECTRIC_DB_USER;
41+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO $ELECTRIC_DB_USER;
42+
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO $ELECTRIC_DB_USER;
43+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO $ELECTRIC_DB_USER;
44+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON SEQUENCES TO $ELECTRIC_DB_USER;
45+
46+
-- Create the publication for Electric SQL (if not exists)
47+
DO \$\$
48+
BEGIN
49+
IF NOT EXISTS (SELECT FROM pg_publication WHERE pubname = 'electric_publication_default') THEN
50+
CREATE PUBLICATION electric_publication_default;
51+
END IF;
52+
END
53+
\$\$;
54+
EOSQL
55+
56+
echo "Electric SQL user '$ELECTRIC_DB_USER' and publication created successfully"

scripts/docker/init-postgres.sh

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ POSTGRES_USER=${POSTGRES_USER:-surfsense}
99
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-surfsense}
1010
POSTGRES_DB=${POSTGRES_DB:-surfsense}
1111

12+
# Electric SQL user credentials (configurable)
13+
ELECTRIC_DB_USER=${ELECTRIC_DB_USER:-electric}
14+
ELECTRIC_DB_PASSWORD=${ELECTRIC_DB_PASSWORD:-electric_password}
15+
1216
echo "Initializing PostgreSQL..."
1317

1418
# Check if PostgreSQL is already initialized
@@ -23,8 +27,18 @@ fi
2327
# Configure PostgreSQL
2428
cat >> "$PGDATA/postgresql.conf" << EOF
2529
listen_addresses = '*'
26-
max_connections = 100
27-
shared_buffers = 128MB
30+
max_connections = 200
31+
shared_buffers = 256MB
32+
33+
# Enable logical replication (required for Electric SQL)
34+
wal_level = logical
35+
max_replication_slots = 10
36+
max_wal_senders = 10
37+
38+
# Performance settings
39+
checkpoint_timeout = 10min
40+
max_wal_size = 1GB
41+
min_wal_size = 80MB
2842
EOF
2943

3044
cat >> "$PGDATA/pg_hba.conf" << EOF
@@ -45,6 +59,15 @@ CREATE USER $POSTGRES_USER WITH PASSWORD '$POSTGRES_PASSWORD' SUPERUSER;
4559
CREATE DATABASE $POSTGRES_DB OWNER $POSTGRES_USER;
4660
\c $POSTGRES_DB
4761
CREATE EXTENSION IF NOT EXISTS vector;
62+
63+
-- Create Electric SQL replication user
64+
CREATE USER $ELECTRIC_DB_USER WITH REPLICATION PASSWORD '$ELECTRIC_DB_PASSWORD';
65+
GRANT CONNECT ON DATABASE $POSTGRES_DB TO $ELECTRIC_DB_USER;
66+
GRANT USAGE ON SCHEMA public TO $ELECTRIC_DB_USER;
67+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO $ELECTRIC_DB_USER;
68+
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO $ELECTRIC_DB_USER;
69+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO $ELECTRIC_DB_USER;
70+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON SEQUENCES TO $ELECTRIC_DB_USER;
4871
EOF
4972

5073
echo "PostgreSQL initialized successfully."

0 commit comments

Comments
 (0)