chore: add missing diagrams/scripts + archicrat-ia routes
This commit is contained in:
7
bridge/Dockerfile
Normal file
7
bridge/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM node:22-bookworm-slim
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm install --omit=dev
|
||||
COPY server.mjs ./
|
||||
EXPOSE 8787
|
||||
CMD ["node","server.mjs"]
|
||||
15
bridge/docker-compose-bridge.yml
Normal file
15
bridge/docker-compose-bridge.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
services:
|
||||
issue_bridge:
|
||||
build: ./bridge
|
||||
environment:
|
||||
GITEA_API_BASE: "http://gitea:3000"
|
||||
GITEA_TOKEN: "${GITEA_TOKEN}"
|
||||
GITEA_OWNER: "Archicratia"
|
||||
GITEA_REPO: "archicratie-edition"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- internal
|
||||
|
||||
networks:
|
||||
internal:
|
||||
external: true
|
||||
10
bridge/package.json
Normal file
10
bridge/package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "issue-bridge",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"express": "^4.19.2",
|
||||
"multer": "^1.4.5-lts.1"
|
||||
}
|
||||
}
|
||||
|
||||
89
bridge/server.mjs
Normal file
89
bridge/server.mjs
Normal file
@@ -0,0 +1,89 @@
|
||||
import express from "express";
|
||||
import multer from "multer";
|
||||
|
||||
const app = express();
|
||||
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 25 * 1024 * 1024 } }); // 25 MB
|
||||
|
||||
const {
|
||||
GITEA_API_BASE, // ex: http://gitea:3000 (ou https://forge.tld)
|
||||
GITEA_TOKEN, // PAT du bot
|
||||
GITEA_OWNER, // owner/org
|
||||
GITEA_REPO // repo
|
||||
} = process.env;
|
||||
|
||||
function mustEnv(name) {
|
||||
if (!process.env[name]) throw new Error(`Missing env ${name}`);
|
||||
}
|
||||
["GITEA_API_BASE","GITEA_TOKEN","GITEA_OWNER","GITEA_REPO"].forEach(mustEnv);
|
||||
|
||||
function isEditor(req) {
|
||||
// Adapte selon tes headers Authelia. Souvent Remote-Groups / Remote-User.
|
||||
const groups = String(req.header("Remote-Groups") || req.header("X-Remote-Groups") || "");
|
||||
return groups.split(/[,\s]+/).includes("editors");
|
||||
}
|
||||
|
||||
async function giteaFetch(path, init = {}) {
|
||||
const url = String(GITEA_API_BASE).replace(/\/+$/, "") + path;
|
||||
const headers = new Headers(init.headers || {});
|
||||
headers.set("Authorization", `token ${GITEA_TOKEN}`);
|
||||
return fetch(url, { ...init, headers });
|
||||
}
|
||||
|
||||
app.get("/health", (_req, res) => res.json({ ok: true }));
|
||||
|
||||
app.post("/media", upload.single("file"), async (req, res) => {
|
||||
try {
|
||||
if (!isEditor(req)) return res.status(403).json({ ok: false, error: "forbidden" });
|
||||
|
||||
const file = req.file;
|
||||
const title = String(req.body.title || "").trim();
|
||||
const body = String(req.body.body || "").trim();
|
||||
const suggestedName = String(req.body.suggestedName || "").trim();
|
||||
|
||||
if (!file) return res.status(400).json({ ok: false, error: "missing_file" });
|
||||
if (!title) return res.status(400).json({ ok: false, error: "missing_title" });
|
||||
if (!body) return res.status(400).json({ ok: false, error: "missing_body" });
|
||||
|
||||
// 1) Create issue
|
||||
const r1 = await giteaFetch(`/api/v1/repos/${encodeURIComponent(GITEA_OWNER)}/${encodeURIComponent(GITEA_REPO)}/issues`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ title, body })
|
||||
});
|
||||
|
||||
if (!r1.ok) {
|
||||
const t = await r1.text().catch(() => "");
|
||||
return res.status(502).json({ ok: false, step: "create_issue", status: r1.status, detail: t.slice(0, 2000) });
|
||||
}
|
||||
|
||||
const issue = await r1.json();
|
||||
const index = issue?.number ?? issue?.index;
|
||||
const issueUrl = issue?.html_url;
|
||||
|
||||
if (!index) return res.status(502).json({ ok: false, step: "create_issue", error: "missing_issue_index" });
|
||||
|
||||
// 2) Upload attachment (multipart field name = "attachment") :contentReference[oaicite:1]{index=1}
|
||||
const fd = new FormData();
|
||||
fd.append("attachment", new Blob([file.buffer], { type: file.mimetype || "application/octet-stream" }), file.originalname);
|
||||
|
||||
const q = suggestedName ? `?name=${encodeURIComponent(suggestedName)}` : "";
|
||||
const r2 = await giteaFetch(`/api/v1/repos/${encodeURIComponent(GITEA_OWNER)}/${encodeURIComponent(GITEA_REPO)}/issues/${encodeURIComponent(String(index))}/assets${q}`, {
|
||||
method: "POST",
|
||||
body: fd
|
||||
});
|
||||
|
||||
if (!r2.ok) {
|
||||
const t = await r2.text().catch(() => "");
|
||||
return res.status(502).json({ ok: false, step: "upload_asset", status: r2.status, detail: t.slice(0, 2000), issueUrl });
|
||||
}
|
||||
|
||||
const asset = await r2.json().catch(() => ({}));
|
||||
return res.json({ ok: true, issueUrl, issueIndex: index, asset });
|
||||
} catch (e) {
|
||||
return res.status(500).json({ ok: false, error: String(e?.message || e) });
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(8787, "0.0.0.0", () => {
|
||||
console.log("issue-bridge listening on :8787");
|
||||
});
|
||||
Reference in New Issue
Block a user