This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
node_modules
|
||||
.next
|
||||
.git
|
||||
.env*
|
||||
!.env.local.example
|
||||
!.env.docker.example
|
||||
docker
|
||||
supabase
|
||||
*.md
|
||||
@@ -0,0 +1,11 @@
|
||||
# Le Tribunal — Docker deployment
|
||||
|
||||
# --- Domain ---
|
||||
APP_DOMAIN=acropole.alxczl.fr
|
||||
|
||||
# --- Traefik ---
|
||||
TRAEFIK_NETWORK=proxy
|
||||
|
||||
# --- Webhook (auto-deploy on push) ---
|
||||
# openssl rand -hex 16
|
||||
WEBHOOK_SECRET=
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Build and deploy
|
||||
run-name: ${{ gitea.actor }} is deploying Le Tribunal
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
npm run build
|
||||
cp -r .next/static .next/standalone/.next/static
|
||||
cp -r public .next/standalone/public
|
||||
env:
|
||||
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
|
||||
|
||||
- name: Deploy
|
||||
run: |
|
||||
tar -czf - -C .next/standalone . | \
|
||||
curl -sf \
|
||||
-H "X-Webhook-Secret: ${{ secrets.WEBHOOK_SECRET }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @- \
|
||||
"${{ secrets.DEPLOY_WEBHOOK }}"
|
||||
@@ -33,6 +33,7 @@ yarn-error.log*
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
!.env.local.example
|
||||
!.env.docker.example
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
FROM node:22-alpine AS base
|
||||
WORKDIR /app
|
||||
|
||||
FROM base AS deps
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
FROM base AS build
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
ARG NEXT_PUBLIC_SUPABASE_URL
|
||||
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL
|
||||
ENV NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||
RUN npm run build
|
||||
|
||||
FROM base AS runner
|
||||
ENV NODE_ENV=production
|
||||
RUN addgroup --system --gid 1001 nodejs && \
|
||||
adduser --system --uid 1001 nextjs
|
||||
COPY --from=build /app/public ./public
|
||||
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
USER nextjs
|
||||
EXPOSE 3000
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME=0.0.0.0
|
||||
CMD ["node", "server.js"]
|
||||
@@ -0,0 +1,48 @@
|
||||
services:
|
||||
app:
|
||||
image: node:22-alpine
|
||||
restart: unless-stopped
|
||||
working_dir: /app
|
||||
command: ["/entrypoint.sh"]
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
HOSTNAME: 0.0.0.0
|
||||
volumes:
|
||||
- app-data:/app
|
||||
- ./docker/entrypoint.sh:/entrypoint.sh:ro
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.tribunal.rule=Host(`${APP_DOMAIN}`)"
|
||||
- "traefik.http.routers.tribunal.entrypoints=https"
|
||||
- "traefik.http.routers.tribunal.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.tribunal.middlewares=compression@file,hsts-headers@file"
|
||||
- "traefik.http.services.tribunal.loadbalancer.server.port=3000"
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
webhook:
|
||||
build: ./docker/webhook
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
WEBHOOK_SECRET: ${WEBHOOK_SECRET}
|
||||
volumes:
|
||||
- app-data:/data
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.tribunal-webhook.rule=Host(`containers01.lan.alxczl.fr`) && PathPrefix(`/tribunal-webhook`)"
|
||||
- "traefik.http.routers.tribunal-webhook.tls.certresolver=voidca"
|
||||
- "traefik.http.routers.tribunal-webhook.entrypoints=https"
|
||||
- "traefik.http.routers.tribunal-webhook.middlewares=hsts-headers@file,tribunal-webhook-strip"
|
||||
- "traefik.http.middlewares.tribunal-webhook-strip.stripprefix.prefixes=/tribunal-webhook"
|
||||
- "traefik.http.services.tribunal-webhook.loadbalancer.server.port=9000"
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
volumes:
|
||||
app-data:
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
name: ${TRAEFIK_NETWORK}
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
while [ ! -f /app/server.js ]; do sleep 2; done
|
||||
|
||||
while true; do
|
||||
STAMP=$(cat /app/.deploy-timestamp 2>/dev/null)
|
||||
node /app/server.js &
|
||||
PID=$!
|
||||
while [ "$(cat /app/.deploy-timestamp 2>/dev/null)" = "$STAMP" ]; do
|
||||
sleep 2
|
||||
done
|
||||
kill $PID
|
||||
wait $PID 2>/dev/null
|
||||
done
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM python:3-alpine
|
||||
COPY receive.py /receive.py
|
||||
CMD ["python", "/receive.py"]
|
||||
@@ -0,0 +1,34 @@
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
import os, tarfile, io, shutil, time
|
||||
|
||||
SECRET = os.environ["WEBHOOK_SECRET"]
|
||||
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
if self.headers.get("X-Webhook-Secret") != SECRET:
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
return
|
||||
|
||||
body = self.rfile.read(int(self.headers["Content-Length"]))
|
||||
|
||||
for item in os.listdir("/data"):
|
||||
path = os.path.join("/data", item)
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
os.remove(path)
|
||||
|
||||
with tarfile.open(fileobj=io.BytesIO(body), mode="r:gz") as tar:
|
||||
tar.extractall("/data", filter="data")
|
||||
|
||||
with open("/data/.deploy-timestamp", "w") as f:
|
||||
f.write(str(time.time()))
|
||||
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write(b"OK")
|
||||
|
||||
|
||||
HTTPServer(("0.0.0.0", 9000), Handler).serve_forever()
|
||||
+6
-1
@@ -1,11 +1,16 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const supabaseHostname = process.env.NEXT_PUBLIC_SUPABASE_URL
|
||||
? new URL(process.env.NEXT_PUBLIC_SUPABASE_URL).hostname
|
||||
: "localhost";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: "standalone",
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "kuichrzrbdwydixbdfon.supabase.co",
|
||||
hostname: supabaseHostname,
|
||||
pathname: "/storage/v1/object/public/**",
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user