diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..ab01fe4
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,22 @@
+node_modules
+npm-debug.log*
+.git
+.github
+.gitignore
+.env
+
+# Output di compilazione di installazioni precedenti: non servono a runtime.
+backend/compiled
+backend/static
+esempi/*.pdf
+esempi/*.html
+latex-source/*.html
+
+# Non serve a runtime (frontend/ e docs/ invece sì: li serve server.js)
+deploy
+api
+vercel.json
+CHANGELOG.md
+CONTRIBUTING.md
+.vscode
+.idea
diff --git a/.env.example b/.env.example
index 5b5b0de..466fab1 100644
--- a/.env.example
+++ b/.env.example
@@ -1,3 +1,8 @@
-# .env.example
+# Copia questo file in .env e modifica i valori se serve.
+# Nessuna variabile è obbligatoria: i default bastano per lo sviluppo.
+
+# Porta su cui il server Express si mette in ascolto (default: 3000)
PORT=3000
+
+# production disattiva i log di sviluppo di Express
NODE_ENV=development
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bd4dffe..be54dc9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,51 +7,180 @@ on:
branches: [main, master]
jobs:
- test:
+ server:
+ name: Server Node + smoke test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - name: Setup Python
- uses: actions/setup-python@v5
+ - name: Setup Node
+ uses: actions/setup-node@v4
with:
- python-version: "3.11"
+ node-version: "22"
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: Avvia il server
+ run: |
+ node server.js &
+ echo $! > server.pid
+ for i in $(seq 1 30); do
+ if curl -sf -o /dev/null http://localhost:3000/; then
+ echo "server pronto dopo ${i}s"
+ exit 0
+ fi
+ sleep 1
+ done
+ echo "il server non ha risposto entro 30s"
+ exit 1
+
+ - name: Il frontend viene servito
+ run: |
+ curl -sf http://localhost:3000/ | grep -q 'id="dropzone"'
+ curl -sf -o /dev/null http://localhost:3000/app.js
+ curl -sf -o /dev/null http://localhost:3000/app.css
+ curl -sf -o /dev/null http://localhost:3000/manifest.json
+ echo "Frontend OK"
+
+ - name: La documentazione viene servita
+ run: |
+ curl -sf http://localhost:3000/docs/ | grep -q 'id="navLinks"'
+ echo "Docs OK"
+
+ - name: Gli endpoint di download rispondono
+ run: |
+ curl -sf http://localhost:3000/api/template/quiz.tex | grep -q 'documentclass'
+ curl -sf -o /dev/null http://localhost:3000/api/download/file/quizstruct.sty
+ curl -sf -o /dev/null http://localhost:3000/api/download/package.zip
+ echo "Download OK"
+
+ - name: Compilazione di un singolo .tex
+ run: |
+ curl -sf -X POST http://localhost:3000/compile \
+ -F "files=@latex-source/quiz.tex" -o out.json
+ node -e "
+ const fs = require('fs');
+ const d = require('./out.json');
+ if (!d.success) { console.error(d.log); process.exit(1); }
+ if (!d.pdf || !d.html) { console.error('output mancanti', d); process.exit(1); }
+ // gli output arrivano nella risposta, non da un URL sul server
+ fs.writeFileSync('out.pdf', Buffer.from(d.pdf.base64, 'base64'));
+ fs.writeFileSync('out.html', Buffer.from(d.html.base64, 'base64'));
+ console.log('Compile OK');
+ "
+ head -c 4 out.pdf | grep -q '%PDF'
+ grep -qi ' evil.tex <<'TEX'
+ \documentclass{article}
+ \usepackage{enumitem}
+ \usepackage{quizstruct}
+ \begin{document}
+ \begin{quiz}{T}
+ \domanda[1]{D
?}
+ \begin{opzioni}
+ \rispostacorretta{ok}
+ \end{opzioni}
+ \end{quiz}
+ \end{document}
+ TEX
+ curl -sf -X POST http://localhost:3000/compile -F "files=@evil.tex" -o evil.json
+ node -e "
+ const d = require('./evil.json');
+ const html = Buffer.from(d.html.base64, 'base64').toString();
+ if (/
+
+