Files
musicdownload/server/src/server.js
T
luzadevandClaude Opus 4.7 bcd4f22b1e Asset brand + pagina download + 1-click download endpoint
- assets/: script generate_logo.py + generate_ls_images.py per produrre
  loghi (mark/horizontal light+dark/stacked light+dark) e immagini
  prodotto Lemon Squeezy (thumbnail 1024x1024, cover 1920x1080,
  OG 1200x630). Riproducibili: rilanciare gli script per rigenerare.
- landing/download.html: pagina con form email+chiave o autofill da
  query string ?key=&email= (link 1-click dall'email). Bottoni
  Mac/Windows + istruzioni installazione per OS.
- landing/index.html: link Download in nav, hero ("Hai gia
  acquistato?"), pricing card ("Hai gia una licenza?"), footer.
- landing/js/main.js: rimosso warning placeholder LS (URL ora reale).
- server/src/downloads.js: endpoint GET /api/download/:platform
  che verifica (key, email) contro DB e redirect a URL firmato del
  binario. Errori chiari (400/401/403/503) leggibili dall'utente.
- server/src/email.js: template Resend con 2 bottoni 1-click
  (link con key+email gia compilati).
- server/src/server.js: route aggiunta.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 01:29:48 +02:00

67 lines
2.1 KiB
JavaScript

import "dotenv/config";
import express from "express";
import * as license from "./license.js";
import * as updates from "./updates.js";
import * as ls from "./lemonsqueezy.js";
import * as dl from "./downloads.js";
const app = express();
// L'app sta dietro Apache reverse proxy: fidati di X-Forwarded-* dal localhost.
app.set("trust proxy", "loopback");
app.disable("x-powered-by");
// CORS minimale (solo per /api/*)
app.use("/api", (req, res, next) => {
res.set("Access-Control-Allow-Origin", "*");
res.set("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
res.set("Access-Control-Allow-Headers", "Content-Type,Authorization");
if (req.method === "OPTIONS") return res.status(204).end();
next();
});
// Health check (no body parser necessario)
app.get("/api/health", (_req, res) => {
res.json({ ok: true, version: process.env.LATEST_VERSION || "" });
});
// WEBHOOK Lemon Squeezy: deve ricevere il body RAW per verificare la firma.
// Va registrato PRIMA del json parser globale.
app.post(
"/api/webhook/lemonsqueezy",
express.raw({ type: "application/json", limit: "1mb" }),
ls.webhook,
);
// JSON parser per tutti gli altri endpoint
app.use(express.json({ limit: "128kb" }));
// Licenze
app.post("/api/license/activate", license.activate);
app.post("/api/license/validate", license.validate);
app.post("/api/license/deactivate", license.deactivate);
// Aggiornamenti + download firmato
app.get("/api/latest", updates.latest);
app.get("/api/download", updates.download);
// 1-click download dall'email (key+email come query -> redirect signed)
app.get("/api/download/:platform", dl.downloadByKey);
// 404 JSON solo per /api/*
app.use("/api", (_req, res) => res.status(404).json({ error: "Not found" }));
// Error handler
app.use((err, _req, res, _next) => {
console.error("[unhandled]", err);
if (res.headersSent) return;
res.status(500).json({ error: "Internal error" });
});
const PORT = Number(process.env.PORT || 4002);
const HOST = process.env.HOST || "127.0.0.1";
app.listen(PORT, HOST, () => {
console.log(`[musictools-api] listening on ${HOST}:${PORT}`);
});