Compare commits
No commits in common. "4d9fb518b1cd1bc739ba4b6c76177d1776651ca9" and "0d4aa0413d840fcfc51b148c18e14e7d98e0a293" have entirely different histories.
4d9fb518b1
...
0d4aa0413d
6 changed files with 6 additions and 103 deletions
|
|
@ -18,7 +18,6 @@ import { type AuthVariables, authMiddleware } from './middleware/auth.js';
|
||||||
import { errorHandler } from './middleware/error-handler.js';
|
import { errorHandler } from './middleware/error-handler.js';
|
||||||
import { defaultRateLimitKey, rateLimit } from './middleware/rate-limit.js';
|
import { defaultRateLimitKey, rateLimit } from './middleware/rate-limit.js';
|
||||||
import { eventsRoutes } from './routes/events.js';
|
import { eventsRoutes } from './routes/events.js';
|
||||||
import { adminRoutes } from './routes/admin.js';
|
|
||||||
import { tablesRoutes } from './routes/tables.js';
|
import { tablesRoutes } from './routes/tables.js';
|
||||||
import { viewsRoutes } from './routes/views.js';
|
import { viewsRoutes } from './routes/views.js';
|
||||||
import { webhooksRoutes } from './routes/webhooks.js';
|
import { webhooksRoutes } from './routes/webhooks.js';
|
||||||
|
|
@ -84,8 +83,6 @@ export async function buildApp(): Promise<Hono<{ Variables: AuthVariables }>> {
|
||||||
v1.route('/tables', tablesRoutes);
|
v1.route('/tables', tablesRoutes);
|
||||||
// R3.1.a : routes views — liste vues par table + donnees paginées d'une vue.
|
// R3.1.a : routes views — liste vues par table + donnees paginées d'une vue.
|
||||||
v1.route('/views', viewsRoutes);
|
v1.route('/views', viewsRoutes);
|
||||||
// Phase B (acadenice) : CRUD tables/fields/views via Baserow user JWT.
|
|
||||||
v1.route('/admin', adminRoutes);
|
|
||||||
app.route('/api/v1', v1);
|
app.route('/api/v1', v1);
|
||||||
|
|
||||||
// R3.1.b : SSE realtime stream — monté sur /api/events hors /api/v1 pour
|
// R3.1.b : SSE realtime stream — monté sur /api/events hors /api/v1 pour
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ export class BaserowJwtManagerImpl implements BaserowJwtManager {
|
||||||
|
|
||||||
private async doLogin(): Promise<string> {
|
private async doLogin(): Promise<string> {
|
||||||
this.logger.debug({ email: this.email }, 'baserow jwt login');
|
this.logger.debug({ email: this.email }, 'baserow jwt login');
|
||||||
const url = `${this.baseUrl}/user/token-auth/`;
|
const url = `${this.baseUrl}/api/user/token-auth/`;
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|
@ -202,7 +202,7 @@ export class BaserowJwtManagerImpl implements BaserowJwtManager {
|
||||||
|
|
||||||
private async doRefresh(currentToken: string): Promise<string> {
|
private async doRefresh(currentToken: string): Promise<string> {
|
||||||
this.logger.debug('baserow jwt refresh');
|
this.logger.debug('baserow jwt refresh');
|
||||||
const url = `${this.baseUrl}/user/token-refresh/`;
|
const url = `${this.baseUrl}/api/user/token-refresh/`;
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Logger } from 'pino';
|
import type { Logger } from 'pino';
|
||||||
import { BaserowAdminClient } from '../adapters/baserow-admin-client.js';
|
|
||||||
import { BaserowClient } from '../adapters/baserow-client.js';
|
import { BaserowClient } from '../adapters/baserow-client.js';
|
||||||
import { RedisCache } from '../adapters/redis-cache.js';
|
import { RedisCache } from '../adapters/redis-cache.js';
|
||||||
import type { ApiTokenRecord } from '../middleware/auth.js';
|
import type { ApiTokenRecord } from '../middleware/auth.js';
|
||||||
|
|
@ -48,9 +47,6 @@ export interface Container {
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
/** Gestionnaire JWT user Baserow pour endpoints metadata. Toujours present. */
|
/** Gestionnaire JWT user Baserow pour endpoints metadata. Toujours present. */
|
||||||
baserowJwt: BaserowJwtManager;
|
baserowJwt: BaserowJwtManager;
|
||||||
/** Client admin Baserow (CRUD tables/fields/views). Lève
|
|
||||||
* BASEROW_USER_AUTH_NOT_CONFIGURED si appelé sans creds user. */
|
|
||||||
baserowAdmin: BaserowAdminClient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let _container: Container | null = null;
|
let _container: Container | null = null;
|
||||||
|
|
@ -150,12 +146,6 @@ export async function initContainer(opts: InitOptions): Promise<Container> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const baserowAdmin = new BaserowAdminClient({
|
|
||||||
baseUrl: config.baserowApiUrl,
|
|
||||||
jwtManager: baserowJwt,
|
|
||||||
logger: rootLogger,
|
|
||||||
});
|
|
||||||
|
|
||||||
const container: Container = {
|
const container: Container = {
|
||||||
config,
|
config,
|
||||||
baserow,
|
baserow,
|
||||||
|
|
@ -167,7 +157,6 @@ export async function initContainer(opts: InitOptions): Promise<Container> {
|
||||||
groupsScopesMap,
|
groupsScopesMap,
|
||||||
logger: rootLogger,
|
logger: rootLogger,
|
||||||
baserowJwt,
|
baserowJwt,
|
||||||
baserowAdmin,
|
|
||||||
};
|
};
|
||||||
setContainer(container);
|
setContainer(container);
|
||||||
return container;
|
return container;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ export type ErrorCode =
|
||||||
| 'CONFLICT'
|
| 'CONFLICT'
|
||||||
| 'RATE_LIMITED'
|
| 'RATE_LIMITED'
|
||||||
| 'BASEROW_UNAVAILABLE'
|
| 'BASEROW_UNAVAILABLE'
|
||||||
| 'BASEROW_USER_AUTH_NOT_CONFIGURED'
|
|
||||||
| 'DOCMOST_UNAVAILABLE'
|
| 'DOCMOST_UNAVAILABLE'
|
||||||
| 'INTERNAL';
|
| 'INTERNAL';
|
||||||
|
|
||||||
|
|
@ -58,12 +57,6 @@ export const errors = {
|
||||||
rateLimited: (retryAfter: number) =>
|
rateLimited: (retryAfter: number) =>
|
||||||
new BridgeError('RATE_LIMITED', 429, 'Too many requests', { retry_after: retryAfter }),
|
new BridgeError('RATE_LIMITED', 429, 'Too many requests', { retry_after: retryAfter }),
|
||||||
baserowDown: () => new BridgeError('BASEROW_UNAVAILABLE', 502, 'Baserow API unreachable'),
|
baserowDown: () => new BridgeError('BASEROW_UNAVAILABLE', 502, 'Baserow API unreachable'),
|
||||||
baserowUserAuthNotConfigured: () =>
|
|
||||||
new BridgeError(
|
|
||||||
'BASEROW_USER_AUTH_NOT_CONFIGURED',
|
|
||||||
503,
|
|
||||||
'Baserow user JWT not configured (BASEROW_USER_EMAIL/PASSWORD missing)',
|
|
||||||
),
|
|
||||||
docmostDown: () => new BridgeError('DOCMOST_UNAVAILABLE', 502, 'Docmost API unreachable'),
|
docmostDown: () => new BridgeError('DOCMOST_UNAVAILABLE', 502, 'Docmost API unreachable'),
|
||||||
internal: (message: string) => new BridgeError('INTERNAL', 500, message),
|
internal: (message: string) => new BridgeError('INTERNAL', 500, message),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,15 @@
|
||||||
# compose.prod.yml — overrides pour env production
|
# compose.prod.yml — overrides pour env production
|
||||||
# Usage : docker compose -f compose.yml -f compose.prod.yml up -d
|
# Usage : docker compose -f compose.yml -f compose.prod.yml up -d
|
||||||
# Reseau externe : admin_proxy (Traefik)
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
docmost:
|
docmost:
|
||||||
image: acadedoc:${ACADEDOC_VERSION:-local}
|
|
||||||
pull_policy: never
|
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
APP_URL: ${DOCMOST_URL:?DOCMOST_URL requis sur prod}
|
APP_URL: ${DOCMOST_URL:?DOCMOST_URL requis sur prod}
|
||||||
LOG_LEVEL: warn
|
LOG_LEVEL: warn
|
||||||
MAIL_DRIVER: smtp
|
|
||||||
SMTP_HOST: ${SMTP_HOST}
|
|
||||||
SMTP_PORT: ${SMTP_PORT}
|
|
||||||
SMTP_USERNAME: ${SMTP_USERNAME}
|
|
||||||
SMTP_PASSWORD: ${SMTP_PASSWORD}
|
|
||||||
SMTP_SECURE: "false"
|
|
||||||
MAIL_FROM_ADDRESS: ${MAIL_FROM_ADDRESS}
|
|
||||||
MAIL_FROM_NAME: ${MAIL_FROM_NAME}
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=admin_proxy"
|
- "traefik.http.routers.docmost-prod.rule=Host(`wiki.acadenice.fr`)"
|
||||||
- "traefik.http.routers.docmost-prod.rule=Host(`doc.stark.a3n.fr`)"
|
|
||||||
- "traefik.http.routers.docmost-prod.entrypoints=websecure"
|
- "traefik.http.routers.docmost-prod.entrypoints=websecure"
|
||||||
- "traefik.http.routers.docmost-prod.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.docmost-prod.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.services.docmost-prod.loadbalancer.server.port=3000"
|
- "traefik.http.services.docmost-prod.loadbalancer.server.port=3000"
|
||||||
|
|
@ -30,11 +18,10 @@ services:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
memory: 2G
|
memory: 2G
|
||||||
cpus: "1.5"
|
|
||||||
reservations:
|
reservations:
|
||||||
memory: 512M
|
memory: 512M
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|
@ -44,11 +31,9 @@ services:
|
||||||
restart: always
|
restart: always
|
||||||
environment:
|
environment:
|
||||||
BASEROW_PUBLIC_URL: ${BASEROW_URL:?BASEROW_URL requis sur prod}
|
BASEROW_PUBLIC_URL: ${BASEROW_URL:?BASEROW_URL requis sur prod}
|
||||||
BASEROW_EXTRA_ALLOWED_HOSTS: baserow
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.docker.network=admin_proxy"
|
- "traefik.http.routers.baserow-prod.rule=Host(`baserow.acadenice.fr`)"
|
||||||
- "traefik.http.routers.baserow-prod.rule=Host(`baserow.stark.a3n.fr`)"
|
|
||||||
- "traefik.http.routers.baserow-prod.entrypoints=websecure"
|
- "traefik.http.routers.baserow-prod.entrypoints=websecure"
|
||||||
- "traefik.http.routers.baserow-prod.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.baserow-prod.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.services.baserow-prod.loadbalancer.server.port=80"
|
- "traefik.http.services.baserow-prod.loadbalancer.server.port=80"
|
||||||
|
|
@ -57,54 +42,8 @@ services:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
memory: 3G
|
memory: 3G
|
||||||
cpus: "2.0"
|
|
||||||
reservations:
|
reservations:
|
||||||
memory: 1G
|
memory: 1G
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "curl -fsS http://localhost/_health/ || exit 1"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 60s
|
|
||||||
|
|
||||||
bridge:
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
BASEROW_WEBHOOK_SECRET: ${BASEROW_WEBHOOK_SECRET:?BASEROW_WEBHOOK_SECRET requis (>= 16 chars)}
|
|
||||||
LOG_LEVEL: warn
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.docker.network=admin_proxy"
|
|
||||||
# Router public : webhooks Baserow + appels machine-to-machine (token brg_*).
|
|
||||||
- "traefik.http.routers.bridge-prod.rule=Host(`bridge.stark.a3n.fr`)"
|
|
||||||
- "traefik.http.routers.bridge-prod.entrypoints=websecure"
|
|
||||||
- "traefik.http.routers.bridge-prod.tls.certresolver=letsencrypt"
|
|
||||||
- "traefik.http.routers.bridge-prod.service=bridge-prod"
|
|
||||||
# Router same-origin sur le doc : appels SPA front. Le cookie authToken
|
|
||||||
# de Docmost est host-only sur doc.stark.a3n.fr donc on ne peut PAS
|
|
||||||
# router cross-subdomain. Le strip /bridge laisse passer /api/v1/* tel
|
|
||||||
# que le bridge l'attend.
|
|
||||||
- "traefik.http.routers.bridge-on-doc.rule=Host(`doc.stark.a3n.fr`) && PathPrefix(`/bridge`)"
|
|
||||||
- "traefik.http.routers.bridge-on-doc.entrypoints=websecure"
|
|
||||||
- "traefik.http.routers.bridge-on-doc.tls.certresolver=letsencrypt"
|
|
||||||
- "traefik.http.routers.bridge-on-doc.middlewares=bridge-strip"
|
|
||||||
- "traefik.http.routers.bridge-on-doc.service=bridge-prod"
|
|
||||||
- "traefik.http.middlewares.bridge-strip.stripprefix.prefixes=/bridge"
|
|
||||||
- "traefik.http.services.bridge-prod.loadbalancer.server.port=4000"
|
|
||||||
ports: !reset []
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 512M
|
|
||||||
cpus: "0.5"
|
|
||||||
reservations:
|
|
||||||
memory: 128M
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:4000/api/health | grep -q '\"status\":\"ok\"' || exit 1"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 3
|
|
||||||
start_period: 20s
|
|
||||||
|
|
||||||
docmost-db:
|
docmost-db:
|
||||||
restart: always
|
restart: always
|
||||||
|
|
@ -112,9 +51,6 @@ services:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
memory: 1G
|
memory: 1G
|
||||||
cpus: "1.0"
|
|
||||||
reservations:
|
|
||||||
memory: 256M
|
|
||||||
|
|
||||||
docmost-redis:
|
docmost-redis:
|
||||||
restart: always
|
restart: always
|
||||||
|
|
@ -122,16 +58,8 @@ services:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
memory: 256M
|
memory: 256M
|
||||||
cpus: "0.5"
|
|
||||||
reservations:
|
|
||||||
memory: 64M
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "redis-cli", "ping"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
external: true
|
external: true
|
||||||
name: admin_proxy
|
name: traefik
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,8 @@ services:
|
||||||
environment:
|
environment:
|
||||||
BASEROW_API_URL: http://baserow:80/api
|
BASEROW_API_URL: http://baserow:80/api
|
||||||
BASEROW_API_TOKEN: ${BASEROW_API_TOKEN}
|
BASEROW_API_TOKEN: ${BASEROW_API_TOKEN}
|
||||||
BASEROW_USER_EMAIL: ${BASEROW_USER_EMAIL}
|
|
||||||
BASEROW_USER_PASSWORD: ${BASEROW_USER_PASSWORD}
|
|
||||||
DOCMOST_API_URL: http://docmost:3000/api
|
DOCMOST_API_URL: http://docmost:3000/api
|
||||||
DOCMOST_API_TOKEN: ${DOCMOST_API_TOKEN}
|
DOCMOST_API_TOKEN: ${DOCMOST_API_TOKEN}
|
||||||
DOCMOST_APP_SECRET: ${DOCMOST_APP_SECRET}
|
|
||||||
DOCMOST_JWT_ISSUER: Docmost
|
|
||||||
REDIS_URL: redis://docmost-redis:6379
|
REDIS_URL: redis://docmost-redis:6379
|
||||||
ports:
|
ports:
|
||||||
- "4000:4000"
|
- "4000:4000"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue