mirror of
https://github.com/readest/readest.git
synced 2026-05-19 07:53:58 +00:00
feat(docker/podman): self-hosting with docker/podman compose (#3312)
Some checks failed
Deploy to vercel on merge / build_and_deploy (push) Has been cancelled
Some checks failed
Deploy to vercel on merge / build_and_deploy (push) Has been cancelled
* initial files * added testing files * removed unused files * cleaned additional mounts * fixed sql init * removed more unused files * moved to docker folder * revert package.json * gitignore update * env example comments and compose necessary healthcheck * ghcr package impl * updated dockerfile steps for layer caching * added development-stage to dockerfile to dev environment * added documentation on how to use dockerfile and compose.yml * fixed prettier issues * fixed image tag * removed workflow for later
This commit is contained in:
parent
c6ae85484e
commit
eec2c39f19
10 changed files with 580 additions and 12 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
docker/.env
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
|
|
|||
44
Dockerfile
44
Dockerfile
|
|
@ -1,20 +1,40 @@
|
|||
FROM node:22-slim
|
||||
|
||||
ENV PNPM_HOME="/root/.local/share/pnpm"
|
||||
ENV PATH="${PATH}:${PNPM_HOME}"
|
||||
|
||||
RUN npm install --global pnpm
|
||||
|
||||
COPY . /app
|
||||
|
||||
FROM docker.io/node:22-slim AS base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
RUN corepack prepare pnpm@10.29.3 --activate
|
||||
WORKDIR /app
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
COPY apps/readest-app/package.json ./apps/readest-app/
|
||||
COPY patches/ ./patches/
|
||||
COPY packages/ ./packages/
|
||||
|
||||
RUN pnpm install
|
||||
|
||||
FROM base AS dependencies
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
||||
RUN pnpm --filter @readest/readest-app setup-vendors
|
||||
|
||||
FROM dependencies AS development-stage
|
||||
COPY . .
|
||||
WORKDIR /app/apps/readest-app
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["pnpm", "dev-web", "-H", "0.0.0.0"]
|
||||
|
||||
FROM base AS build
|
||||
ARG NEXT_PUBLIC_SUPABASE_URL
|
||||
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
ARG NEXT_PUBLIC_APP_PLATFORM
|
||||
ARG NEXT_PUBLIC_API_BASE_URL
|
||||
ARG NEXT_PUBLIC_OBJECT_STORAGE_TYPE
|
||||
ARG NEXT_PUBLIC_STORAGE_FIXED_QUOTA
|
||||
ARG NEXT_PUBLIC_TRANSLATION_FIXED_QUOTA
|
||||
COPY --from=dependencies /app/node_modules /app/node_modules
|
||||
COPY --from=dependencies /app/apps/readest-app/node_modules /app/apps/readest-app/node_modules
|
||||
COPY --from=dependencies /app/apps/readest-app/public/vendor /app/apps/readest-app/public/vendor
|
||||
COPY --from=dependencies /app/packages/foliate-js/node_modules /app/packages/foliate-js/node_modules
|
||||
COPY . .
|
||||
WORKDIR /app/apps/readest-app
|
||||
RUN pnpm build-web
|
||||
|
||||
ENTRYPOINT ["pnpm", "start-web"]
|
||||
FROM build as production-stage
|
||||
ENTRYPOINT ["pnpm", "start-web", "-H", "0.0.0.0"]
|
||||
EXPOSE 3000
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { createClient } from '@supabase/supabase-js';
|
||||
|
||||
const supabaseUrl =
|
||||
process.env['SUPABASE_URL'] ||
|
||||
process.env['NEXT_PUBLIC_SUPABASE_URL'] ||
|
||||
atob(process.env['NEXT_PUBLIC_DEFAULT_SUPABASE_URL_BASE64']!);
|
||||
const supabaseAnonKey =
|
||||
process.env['SUPABASE_ANON_KEY'] ||
|
||||
process.env['NEXT_PUBLIC_SUPABASE_ANON_KEY'] ||
|
||||
atob(process.env['NEXT_PUBLIC_DEFAULT_SUPABASE_KEY_BASE64']!);
|
||||
|
||||
|
|
|
|||
51
docker/.env.example
Normal file
51
docker/.env.example
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# ip used by frontend/client-side urls to access the backend
|
||||
HOST_IP=localhost
|
||||
|
||||
# db(psql) config
|
||||
# change to strong password, min 32 chars
|
||||
POSTGRES_PASSWORD=your-super-secret-postgres-password
|
||||
POSTGRES_HOST=db
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_DB=postgres
|
||||
|
||||
# jwt config
|
||||
JWT_EXPIRY=3600
|
||||
JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters
|
||||
# both ANON_KEY and SERVICE_ROLE_KEY should be generated using the above set secret
|
||||
# sign this payload -> {"role": "anon"} <- for ANON_KEY with JWT_SECRET
|
||||
ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiJ9.HmbvN-NgnsK1Lbk5e8Dti9SS6SFB384DsIT8QVLwUo8
|
||||
# sign this payload -> {"role": "service_role"} <- for SERVICE_ROLE_KEY with JWT_SECRET
|
||||
SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIn0.ym708-_0SlTjvc0pAK56bXQK2srKAo8RbJfA3czgkFo
|
||||
|
||||
KONG_HTTP_PORT=8000
|
||||
|
||||
# auth (gotrue)
|
||||
API_EXTERNAL_URL=http://localhost:8000
|
||||
SITE_URL=http://localhost:3000
|
||||
ADDITIONAL_REDIRECT_URLS=http://localhost:3000/**,http://localhost:8000/**
|
||||
|
||||
# readest auth stuff
|
||||
DISABLE_SIGNUP=false
|
||||
ENABLE_EMAIL_SIGNUP=true
|
||||
ENABLE_EMAIL_AUTOCONFIRM=true
|
||||
ENABLE_ANONYMOUS_USERS=false
|
||||
|
||||
# only needed if ENABLE_EMAIL_AUTOCONFIRM is false
|
||||
SMTP_HOST=
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=
|
||||
SMTP_PASS=
|
||||
SMTP_ADMIN_EMAIL=admin@example.com
|
||||
SMTP_SENDER_NAME=Readest
|
||||
|
||||
# PostgREST shiz
|
||||
PGRST_DB_SCHEMAS=public,graphql_public
|
||||
|
||||
# s3 storage through MinIO
|
||||
MINIO_ROOT_USER=minioadmin
|
||||
MINIO_ROOT_PASSWORD=minioadmin-secret-password
|
||||
S3_BUCKET_NAME=readest-files
|
||||
|
||||
# 1GB fixed quota for storage and 50k for translations
|
||||
STORAGE_FIXED_QUOTA=1073741824
|
||||
TRANSLATION_FIXED_QUOTA=50000
|
||||
122
docker/README.md
Normal file
122
docker/README.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# Self-Hosting with Docker/Podman with Compose
|
||||
|
||||
## Stack
|
||||
|
||||
| service | Image | Description |
|
||||
| --------------- | --------------------------- | ------------------------------------------------- |
|
||||
| **client** | from `../Dockerfile` | readest frontend |
|
||||
| **db** | `supabase/postgres` | psql db with supabase extensions |
|
||||
| **kong** | `kong:2.8.1` | api gateway routing requests to supabase services |
|
||||
| **auth** | `supabase/gotrue:v2.185.0` | auth service (email, JWT) |
|
||||
| **rest** | `postgrest/postgrest:v14.3` | psql rest api |
|
||||
| **minio** | `minio/minio` | s3 storage |
|
||||
| **minio-setup** | `minio/mc` | helper container to create s3 buckets |
|
||||
|
||||
### Exposed ports
|
||||
|
||||
| Port | Service |
|
||||
| ------ | ---------------- |
|
||||
| `3000` | readest |
|
||||
| `7000` | kong API gateway |
|
||||
| `9000` | MinIO S3 API |
|
||||
| `9001` | MinIO console UI |
|
||||
|
||||
---
|
||||
|
||||
## Running with Docker/Podman Compose
|
||||
|
||||
### 1. setup .env
|
||||
|
||||
```bash
|
||||
cp docker/.env.example docker/.env
|
||||
```
|
||||
|
||||
update `docker/.env`:
|
||||
|
||||
- update `POSTGRES_PASSWORD` to a strong password (32+ chars)
|
||||
- update `JWT_SECRET` to a random secret (32+ chars)
|
||||
- regenerate `ANON_KEY` and `SERVICE_ROLE_KEY` as HS256 JWTs signed with your `JWT_SECRET` (use [jwt.io](https://jwt.io/) or a similar tool):
|
||||
- `ANON_KEY` payload: `{"role": "anon"}`
|
||||
- `SERVICE_ROLE_KEY` payload: `{"role": "service_role"}`
|
||||
- set `MINIO_ROOT_PASSWORD` to a strong password
|
||||
|
||||
### 2. Start the Stack
|
||||
|
||||
run from the `docker/` directory:
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
docker compose up --build -d
|
||||
```
|
||||
|
||||
the client image is built locally on first run. subsequent starts reuse the cached image.
|
||||
|
||||
### 3. Access
|
||||
|
||||
- Readest app: `http://localhost:3000`
|
||||
- MinIO console: `http://localhost:9001` (login with `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD`)
|
||||
|
||||
### Hot Reload (development)
|
||||
|
||||
to develop using the compose stack, set the build target on `client` to `development-stage`, which'll runs the next.js dev server. to enable hot reload, uncomment the `volumes` block in the `client` service in `compose.yaml`:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ../:/app
|
||||
- /app/node_modules
|
||||
- /app/apps/readest-app/node_modules
|
||||
- /app/apps/readest-app/public/vendor
|
||||
- /app/apps/readest-app/.next
|
||||
- /app/packages/foliate-js/node_modules
|
||||
```
|
||||
|
||||
the first mount overlays your local repo into the container. the remaining anonymous volumes shadow the directories that were pre-built inside the image, so the container's installed deps and vendor assets are used instead of what's on your host.
|
||||
|
||||
### Stop the Stack
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
docker compose down
|
||||
```
|
||||
|
||||
to also remove volumes (database and storage data):
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
docker compose down -v
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building the Dockerfile standalone
|
||||
|
||||
the `Dockerfile` requires Build args for the next.js public env vars (they are inlined at build time)
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--target production-stage \
|
||||
--build-arg NEXT_PUBLIC_SUPABASE_URL=http://localhost:7000 \
|
||||
--build-arg NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon-key> \
|
||||
--build-arg NEXT_PUBLIC_APP_PLATFORM=web \
|
||||
--build-arg NEXT_PUBLIC_API_BASE_URL=http://localhost:3000 \
|
||||
--build-arg NEXT_PUBLIC_OBJECT_STORAGE_TYPE=s3 \
|
||||
--build-arg NEXT_PUBLIC_STORAGE_FIXED_QUOTA=1073741824 \
|
||||
--build-arg NEXT_PUBLIC_TRANSLATION_FIXED_QUOTA=50000 \
|
||||
-t readest-client \
|
||||
.
|
||||
```
|
||||
|
||||
run the built image:
|
||||
|
||||
```bash
|
||||
docker run -p 3000:3000 \
|
||||
-e SUPABASE_URL=http://kong:8000 \
|
||||
-e SUPABASE_ANON_KEY=<anon-key> \
|
||||
-e SUPABASE_ADMIN_KEY=<service-role-key> \
|
||||
-e S3_ENDPOINT=http://localhost:9000 \
|
||||
-e S3_REGION=us-east-1 \
|
||||
-e S3_BUCKET_NAME=readest-files \
|
||||
-e S3_ACCESS_KEY_ID=<minio-user> \
|
||||
-e S3_SECRET_ACCESS_KEY=<minio-password> \
|
||||
readest-client
|
||||
```
|
||||
160
docker/compose.yaml
Normal file
160
docker/compose.yaml
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
name: readest
|
||||
|
||||
services:
|
||||
db:
|
||||
container_name: supabase-db
|
||||
image: supabase/postgres:15.8.1.085
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z
|
||||
- ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z
|
||||
- ./volumes/db/init/schema.sql:/docker-entrypoint-initdb.d/init-scripts/100-schema.sql:Z
|
||||
- db-data:/var/lib/postgresql/data:Z
|
||||
- db-config:/etc/postgresql-custom
|
||||
environment:
|
||||
POSTGRES_HOST: /var/run/postgresql
|
||||
PGPORT: ${POSTGRES_PORT}
|
||||
POSTGRES_PORT: ${POSTGRES_PORT}
|
||||
PGPASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
PGDATABASE: ${POSTGRES_DB}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
JWT_EXP: ${JWT_EXPIRY}
|
||||
command:
|
||||
- postgres
|
||||
- -c
|
||||
- config_file=/etc/postgresql/postgresql.conf
|
||||
- -c
|
||||
- log_min_messages=fatal
|
||||
|
||||
kong:
|
||||
container_name: supabase-kong
|
||||
image: kong:2.8.1
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${KONG_HTTP_PORT}:8000/tcp"
|
||||
volumes:
|
||||
- ./volumes/api/kong.yml:/home/kong/temp.yml:ro,z
|
||||
environment:
|
||||
KONG_DATABASE: "off"
|
||||
KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml
|
||||
KONG_DNS_ORDER: LAST,A,CNAME
|
||||
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
|
||||
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||
entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start'
|
||||
|
||||
auth:
|
||||
container_name: supabase-auth
|
||||
image: supabase/gotrue:v2.185.0
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
GOTRUE_API_HOST: 0.0.0.0
|
||||
GOTRUE_API_PORT: 9999
|
||||
API_EXTERNAL_URL: ${API_EXTERNAL_URL}
|
||||
GOTRUE_DB_DRIVER: postgres
|
||||
GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||
GOTRUE_SITE_URL: ${SITE_URL}
|
||||
GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS}
|
||||
GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP}
|
||||
GOTRUE_JWT_ADMIN_ROLES: service_role
|
||||
GOTRUE_JWT_AUD: authenticated
|
||||
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
|
||||
GOTRUE_JWT_EXP: ${JWT_EXPIRY}
|
||||
GOTRUE_JWT_SECRET: ${JWT_SECRET}
|
||||
GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP}
|
||||
GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS}
|
||||
GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM}
|
||||
GOTRUE_SMTP_HOST: ${SMTP_HOST}
|
||||
GOTRUE_SMTP_PORT: ${SMTP_PORT}
|
||||
GOTRUE_SMTP_USER: ${SMTP_USER}
|
||||
GOTRUE_SMTP_PASS: ${SMTP_PASS}
|
||||
GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL}
|
||||
GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME}
|
||||
|
||||
rest:
|
||||
container_name: supabase-rest
|
||||
image: postgrest/postgrest:v14.3
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/${POSTGRES_DB}
|
||||
PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}
|
||||
PGRST_DB_ANON_ROLE: anon
|
||||
PGRST_JWT_SECRET: ${JWT_SECRET}
|
||||
PGRST_DB_USE_LEGACY_GUCS: "false"
|
||||
PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET}
|
||||
PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY}
|
||||
command: ["postgrest"]
|
||||
|
||||
minio:
|
||||
container_name: readest-minio
|
||||
image: minio/minio
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||
command: server --console-address ":9001" /data
|
||||
volumes:
|
||||
- minio-data:/data:z
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
minio-setup:
|
||||
image: minio/mc
|
||||
depends_on:
|
||||
minio:
|
||||
condition: service_healthy
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
mc alias set myminio http://minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD} &&
|
||||
mc mb --ignore-existing myminio/${S3_BUCKET_NAME}
|
||||
"
|
||||
|
||||
client:
|
||||
container_name: readest-client
|
||||
build:
|
||||
context: ..
|
||||
target: production-stage
|
||||
args:
|
||||
NEXT_PUBLIC_SUPABASE_URL: http://${HOST_IP}:7000
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
NEXT_PUBLIC_APP_PLATFORM: web
|
||||
NEXT_PUBLIC_API_BASE_URL: http://${HOST_IP}:3000
|
||||
NEXT_PUBLIC_OBJECT_STORAGE_TYPE: s3
|
||||
NEXT_PUBLIC_STORAGE_FIXED_QUOTA: ${STORAGE_FIXED_QUOTA}
|
||||
NEXT_PUBLIC_TRANSLATION_FIXED_QUOTA: ${TRANSLATION_FIXED_QUOTA}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
# to enable hot reload, mount the source code as a volume, and artifacts as anon volumes
|
||||
# volumes:
|
||||
# - ../:/app
|
||||
# - /app/node_modules
|
||||
# - /app/apps/readest-app/node_modules
|
||||
# - /app/apps/readest-app/public/vendor
|
||||
# - /app/apps/readest-app/.next
|
||||
# - /app/packages/foliate-js/node_modules
|
||||
environment:
|
||||
SUPABASE_URL: http://kong:8000
|
||||
SUPABASE_ANON_KEY: ${ANON_KEY}
|
||||
SUPABASE_ADMIN_KEY: ${SERVICE_ROLE_KEY}
|
||||
S3_ENDPOINT: http://${HOST_IP}:9000
|
||||
S3_REGION: us-east-1
|
||||
S3_BUCKET_NAME: ${S3_BUCKET_NAME}
|
||||
S3_ACCESS_KEY_ID: ${MINIO_ROOT_USER}
|
||||
S3_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD}
|
||||
depends_on:
|
||||
- kong
|
||||
- minio
|
||||
|
||||
volumes:
|
||||
db-config:
|
||||
db-data:
|
||||
minio-data:
|
||||
95
docker/volumes/api/kong.yml
Normal file
95
docker/volumes/api/kong.yml
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
_format_version: '2.1'
|
||||
_transform: true
|
||||
|
||||
consumers:
|
||||
- username: anon
|
||||
keyauth_credentials:
|
||||
- key: $SUPABASE_ANON_KEY
|
||||
- username: service_role
|
||||
keyauth_credentials:
|
||||
- key: $SUPABASE_SERVICE_KEY
|
||||
|
||||
acls:
|
||||
- consumer: anon
|
||||
group: anon
|
||||
- consumer: service_role
|
||||
group: admin
|
||||
|
||||
services:
|
||||
- name: auth-v1-health
|
||||
url: http://auth:9999/health
|
||||
routes:
|
||||
- name: auth-v1-health
|
||||
strip_path: true
|
||||
paths:
|
||||
- /auth/v1/health
|
||||
plugins:
|
||||
- name: cors
|
||||
|
||||
- name: auth-v1-open
|
||||
url: http://auth:9999/verify
|
||||
routes:
|
||||
- name: auth-v1-open
|
||||
strip_path: true
|
||||
paths:
|
||||
- /auth/v1/verify
|
||||
plugins:
|
||||
- name: cors
|
||||
|
||||
- name: auth-v1-open-callback
|
||||
url: http://auth:9999/callback
|
||||
routes:
|
||||
- name: auth-v1-open-callback
|
||||
strip_path: true
|
||||
paths:
|
||||
- /auth/v1/callback
|
||||
plugins:
|
||||
- name: cors
|
||||
|
||||
- name: auth-v1-open-authorize
|
||||
url: http://auth:9999/authorize
|
||||
routes:
|
||||
- name: auth-v1-open-authorize
|
||||
strip_path: true
|
||||
paths:
|
||||
- /auth/v1/authorize
|
||||
plugins:
|
||||
- name: cors
|
||||
|
||||
- name: auth-v1
|
||||
url: http://auth:9999/
|
||||
routes:
|
||||
- name: auth-v1-all
|
||||
strip_path: true
|
||||
paths:
|
||||
- /auth/v1/
|
||||
plugins:
|
||||
- name: cors
|
||||
- name: key-auth
|
||||
config:
|
||||
hide_credentials: false
|
||||
- name: acl
|
||||
config:
|
||||
hide_groups_header: true
|
||||
allow:
|
||||
- admin
|
||||
- anon
|
||||
|
||||
- name: rest-v1
|
||||
url: http://rest:3000/
|
||||
routes:
|
||||
- name: rest-v1-all
|
||||
strip_path: true
|
||||
paths:
|
||||
- /rest/v1/
|
||||
plugins:
|
||||
- name: cors
|
||||
- name: key-auth
|
||||
config:
|
||||
hide_credentials: true
|
||||
- name: acl
|
||||
config:
|
||||
hide_groups_header: true
|
||||
allow:
|
||||
- admin
|
||||
- anon
|
||||
105
docker/volumes/db/init/schema.sql
Normal file
105
docker/volumes/db/init/schema.sql
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
ALTER FUNCTION auth.uid() OWNER TO supabase_auth_admin;
|
||||
ALTER FUNCTION auth.role() OWNER TO supabase_auth_admin;
|
||||
|
||||
CREATE TABLE public.books (
|
||||
user_id uuid NOT NULL,
|
||||
book_hash text NOT NULL,
|
||||
meta_hash text NULL,
|
||||
format text NULL,
|
||||
title text NULL,
|
||||
source_title text NULL,
|
||||
author text NULL,
|
||||
"group" text NULL,
|
||||
tags text[] NULL,
|
||||
created_at timestamp with time zone NULL DEFAULT now(),
|
||||
updated_at timestamp with time zone NULL DEFAULT now(),
|
||||
deleted_at timestamp with time zone NULL,
|
||||
uploaded_at timestamp with time zone NULL,
|
||||
progress integer[] NULL,
|
||||
reading_status text NULL,
|
||||
group_id text NULL,
|
||||
group_name text NULL,
|
||||
metadata json NULL,
|
||||
CONSTRAINT books_pkey PRIMARY KEY (user_id, book_hash),
|
||||
CONSTRAINT books_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE public.books ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY select_books ON public.books FOR SELECT TO authenticated USING ((SELECT auth.uid()) = user_id);
|
||||
CREATE POLICY insert_books ON public.books FOR INSERT TO authenticated WITH CHECK ((SELECT auth.uid()) = user_id);
|
||||
CREATE POLICY update_books ON public.books FOR UPDATE TO authenticated USING ((SELECT auth.uid()) = user_id);
|
||||
CREATE POLICY delete_books ON public.books FOR DELETE TO authenticated USING ((SELECT auth.uid()) = user_id);
|
||||
|
||||
CREATE TABLE public.book_configs (
|
||||
user_id uuid NOT NULL,
|
||||
book_hash text NOT NULL,
|
||||
meta_hash text NULL,
|
||||
location text NULL,
|
||||
xpointer text NULL,
|
||||
progress jsonb NULL,
|
||||
search_config jsonb NULL,
|
||||
view_settings jsonb NULL,
|
||||
created_at timestamp with time zone NULL DEFAULT now(),
|
||||
updated_at timestamp with time zone NULL DEFAULT now(),
|
||||
deleted_at timestamp with time zone NULL,
|
||||
CONSTRAINT book_configs_pkey PRIMARY KEY (user_id, book_hash),
|
||||
CONSTRAINT book_configs_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE public.book_configs ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY select_book_configs ON public.book_configs FOR SELECT TO authenticated USING ((SELECT auth.uid()) = user_id);
|
||||
CREATE POLICY insert_book_configs ON public.book_configs FOR INSERT TO authenticated WITH CHECK ((SELECT auth.uid()) = user_id);
|
||||
CREATE POLICY update_book_configs ON public.book_configs FOR UPDATE TO authenticated USING ((SELECT auth.uid()) = user_id);
|
||||
CREATE POLICY delete_book_configs ON public.book_configs FOR DELETE TO authenticated USING ((SELECT auth.uid()) = user_id);
|
||||
|
||||
CREATE TABLE public.book_notes (
|
||||
user_id uuid NOT NULL,
|
||||
book_hash text NOT NULL,
|
||||
meta_hash text NULL,
|
||||
id text NOT NULL,
|
||||
type text NULL,
|
||||
cfi text NULL,
|
||||
text text NULL,
|
||||
style text NULL,
|
||||
color text NULL,
|
||||
note text NULL,
|
||||
created_at timestamp with time zone NULL DEFAULT now(),
|
||||
updated_at timestamp with time zone NULL DEFAULT now(),
|
||||
deleted_at timestamp with time zone NULL,
|
||||
CONSTRAINT book_notes_pkey PRIMARY KEY (user_id, book_hash, id),
|
||||
CONSTRAINT book_notes_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE public.book_notes ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY select_book_notes ON public.book_notes FOR SELECT TO authenticated USING ((SELECT auth.uid()) = user_id);
|
||||
CREATE POLICY insert_book_notes ON public.book_notes FOR INSERT TO authenticated WITH CHECK ((SELECT auth.uid()) = user_id);
|
||||
CREATE POLICY update_book_notes ON public.book_notes FOR UPDATE TO authenticated USING ((SELECT auth.uid()) = user_id);
|
||||
CREATE POLICY delete_book_notes ON public.book_notes FOR DELETE TO authenticated USING ((SELECT auth.uid()) = user_id);
|
||||
|
||||
CREATE TABLE public.files (
|
||||
id uuid NOT NULL DEFAULT gen_random_uuid(),
|
||||
user_id uuid NOT NULL,
|
||||
book_hash text NULL,
|
||||
file_key text NOT NULL,
|
||||
file_size bigint NOT NULL,
|
||||
created_at timestamp with time zone NULL DEFAULT now(),
|
||||
deleted_at timestamp with time zone NULL,
|
||||
CONSTRAINT files_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT files_file_key_key UNIQUE (file_key),
|
||||
CONSTRAINT files_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_files_user_id_deleted_at ON public.files (user_id, deleted_at);
|
||||
CREATE INDEX idx_files_file_key ON public.files (file_key);
|
||||
CREATE INDEX idx_files_file_key_deleted_at ON public.files (file_key, deleted_at);
|
||||
|
||||
ALTER TABLE public.files ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY files_insert ON public.files FOR INSERT WITH CHECK (auth.uid() = user_id);
|
||||
CREATE POLICY files_select ON public.files FOR SELECT USING (auth.uid() = user_id AND deleted_at IS NULL);
|
||||
CREATE POLICY files_update ON public.files FOR UPDATE USING (auth.uid() = user_id) WITH CHECK (deleted_at IS NULL OR deleted_at > now());
|
||||
CREATE POLICY files_delete ON public.files FOR DELETE USING (auth.uid() = user_id);
|
||||
|
||||
GRANT ALL ON public.books TO authenticated;
|
||||
GRANT ALL ON public.book_configs TO authenticated;
|
||||
GRANT ALL ON public.book_notes TO authenticated;
|
||||
GRANT ALL ON public.files TO authenticated;
|
||||
5
docker/volumes/db/jwt.sql
Normal file
5
docker/volumes/db/jwt.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
\set jwt_secret `echo "$JWT_SECRET"`
|
||||
\set jwt_exp `echo "$JWT_EXP"`
|
||||
|
||||
ALTER DATABASE postgres SET "app.settings.jwt_secret" TO :'jwt_secret';
|
||||
ALTER DATABASE postgres SET "app.settings.jwt_exp" TO :'jwt_exp';
|
||||
7
docker/volumes/db/roles.sql
Normal file
7
docker/volumes/db/roles.sql
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
-- NOTE: change to your own passwords for production environments
|
||||
\set pgpass `echo "$POSTGRES_PASSWORD"`
|
||||
|
||||
ALTER USER authenticator WITH PASSWORD :'pgpass';
|
||||
ALTER USER pgbouncer WITH PASSWORD :'pgpass';
|
||||
ALTER USER supabase_auth_admin WITH PASSWORD :'pgpass';
|
||||
ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass';
|
||||
Loading…
Add table
Add a link
Reference in a new issue