Spaces:
Running
Running
Download src/app.js from AndreaNotes1/auth-app-api: direct link, hf CLI and curl.
- Browser
- Download file 4.44 kB
-
https://huggingface.co/spaces/AndreaNotes1/auth-app-api/resolve/main/src/app.js
- Command line
-
hf download hf://spaces/AndreaNotes1/auth-app-api/src/app.js
-
curl -L -o app.js https://huggingface.co/spaces/AndreaNotes1/auth-app-api/resolve/main/src/app.js
4.44 kB
| const express = require("express"); | |
| const helmet = require("helmet"); | |
| const cors = require("cors"); | |
| const compression = require("compression"); | |
| const morgan = require("morgan"); | |
| const mongoSanitize = require("express-mongo-sanitize"); | |
| const hpp = require("hpp"); | |
| const { env } = require("./config/env"); | |
| const { apiLimiter } = require("./middleware/rateLimiters"); | |
| const { errorHandler, notFound } = require("./middleware/errorHandler"); | |
| const logger = require("./utils/logger"); | |
| const authRoutes = require("./routes/auth.routes"); | |
| const courseRoutes = require("./routes/course.routes"); | |
| const orderRoutes = require("./routes/order.routes"); | |
| const adminRoutes = require("./routes/admin.routes"); | |
| const voucherRoutes = require("./routes/voucher.routes"); | |
| const webhookRoutes = require("./routes/webhook.routes"); | |
| const app = express(); | |
| // Trust the first proxy hop (Render/Heroku/Nginx/etc.) so req.ip and the | |
| // "secure" cookie/HSTS logic reflect the real client over HTTPS. | |
| app.set("trust proxy", 1); | |
| // --- Security headers ------------------------------------------------- | |
| app.use( | |
| helmet({ | |
| contentSecurityPolicy: { | |
| directives: { | |
| defaultSrc: ["'self'"], | |
| scriptSrc: ["'self'"], | |
| styleSrc: ["'self'", "'unsafe-inline'"], | |
| imgSrc: ["'self'", "data:", "https:"], | |
| objectSrc: ["'none'"], | |
| frameAncestors: ["'none'"], | |
| }, | |
| }, | |
| crossOriginResourcePolicy: { policy: "same-site" }, | |
| hsts: { maxAge: 63072000, includeSubDomains: true, preload: true }, | |
| }) | |
| ); | |
| // --- CORS: only the configured frontend origins may call the API. | |
| // `credentials: false` is deliberate β this API is designed to run | |
| // cross-site from its frontend (e.g. a Hugging Face Space backend + | |
| // a separate Vercel-hosted frontend), and some hosting proxies in that | |
| // kind of setup intercept CORS preflight requests before they reach | |
| // this app and strip the Access-Control-Allow-Credentials header, | |
| // which silently breaks anything relying on `credentials: include`. | |
| // Sessions are carried via a bearer access token (Authorization | |
| // header) plus a refresh token returned in the JSON response body | |
| // instead of a cookie, so no cross-site credentialed request is ever | |
| // needed β see controllers/auth.controller.js for the reasoning and | |
| // trade-offs of that approach in more detail. --- | |
| app.use( | |
| cors({ | |
| origin(origin, callback) { | |
| if (!origin || env.clientOrigins.includes(origin)) return callback(null, true); | |
| // Logged (not just thrown) so a mismatched frontend domain shows up | |
| // immediately in the server logs with the exact rejected value β | |
| // compare this directly against the CORS allow-list logged at | |
| // startup to spot typos, protocol mismatches, or a missing entry. | |
| logger.warn(`CORS rejected origin: "${origin}" (allow-list: ${JSON.stringify(env.clientOrigins)})`); | |
| return callback(new Error("Not allowed by CORS")); | |
| }, | |
| credentials: false, | |
| }) | |
| ); | |
| app.use(compression()); | |
| // Payoneer's webhook signature is computed over the exact raw request | |
| // body, so this route MUST be registered with a raw-body parser | |
| // BEFORE the global express.json() below β once express.json() has | |
| // run, the original bytes are gone and any signature check would | |
| // fail. See utils/payoneerCheckout.js -> verifyAndParseWebhook. | |
| app.use("/api/webhooks", express.raw({ type: "*/*", limit: "200kb" }), webhookRoutes); | |
| app.use(express.json({ limit: "100kb" })); // small limit: this API never needs large bodies | |
| app.use(express.urlencoded({ extended: true, limit: "100kb" })); | |
| // Strip any keys starting with "$" or containing "." from user input to | |
| // block MongoDB operator-injection attacks. | |
| app.use(mongoSanitize()); | |
| // Prevent HTTP parameter-pollution (e.g. ?role=user&role=admin). | |
| app.use(hpp()); | |
| app.use( | |
| morgan("combined", { | |
| stream: { write: (message) => logger.info(message.trim()) }, | |
| skip: () => env.nodeEnv === "test", | |
| }) | |
| ); | |
| app.use("/api/", apiLimiter); | |
| app.get("/api/health", (req, res) => res.json({ status: "ok", time: new Date().toISOString() })); | |
| app.use("/api/auth", authRoutes); | |
| app.use("/api/courses", courseRoutes); | |
| app.use("/api/orders", orderRoutes); | |
| app.use("/api/admin", adminRoutes); | |
| app.use("/api/vouchers", voucherRoutes); | |
| app.use(notFound); | |
| app.use(errorHandler); | |
| module.exports = app; | |