node-csfd-api
Version:
ČSFD API in JavaScript. Amazing NPM library for scrapping csfd.cz :)
268 lines (266 loc) • 9.08 kB
JavaScript
import { csfd } from "../index.js";
import { homepage, name, version } from "../package.js";
import "dotenv/config";
import express from "express";
//#region src/bin/server.ts
const LOG_COLORS = {
info: "\x1B[36m",
warn: "\x1B[33m",
error: "\x1B[31m",
success: "\x1B[32m",
reset: "\x1B[0m"
};
const LOG_SYMBOLS = {
info: "ℹ️",
warn: "⚠️",
error: "❌",
success: "✅"
};
const LOG_PADDED_SEVERITY = {
info: "INFO ",
warn: "WARN ",
error: "ERROR ",
success: "SUCCESS"
};
/**
* Optimized logging function.
* Uses global constants to avoid memory reallocation on every request.
*/
function logMessage(severity, log, req) {
const time = (/* @__PURE__ */ new Date()).toISOString();
const reqInfo = req ? `${req.method}: ${req.originalUrl}` : "";
const reqIp = req ? req.headers["x-forwarded-for"] || req.socket.remoteAddress || req.ip || req.ips : "";
const msg = `${LOG_COLORS[severity]}[${LOG_PADDED_SEVERITY[severity]}]${LOG_COLORS.reset} ${time} | IP: ${reqIp} ${LOG_SYMBOLS[severity]} ${log.error ? log.error + ":" : ""} ${log.message} 🔗 ${reqInfo}`;
const logSuccessEnabled = process.env.VERBOSE === "true";
if (severity === "success") {
if (logSuccessEnabled) console.log(msg);
} else if (severity === "error") console.error(msg);
else if (severity === "warn") console.warn(msg);
else console.log(msg);
}
var Endpoint = /* @__PURE__ */ function(Endpoint) {
Endpoint["MOVIE"] = "/movie/:id";
Endpoint["CREATOR"] = "/creator/:id";
Endpoint["SEARCH"] = "/search/:query";
Endpoint["USER_RATINGS"] = "/user-ratings/:id";
Endpoint["USER_REVIEWS"] = "/user-reviews/:id";
Endpoint["CINEMAS"] = "/cinemas";
return Endpoint;
}(Endpoint || {});
const app = express();
const port = process.env.PORT || 3e3;
const API_KEY_NAME = process.env.API_KEY_NAME || "x-api-key";
const API_KEY = process.env.API_KEY;
const RAW_LANGUAGE = process.env.LANGUAGE;
const isSupportedLanguage = (value) => value === "cs" || value === "en" || value === "sk";
const BASE_LANGUAGE = isSupportedLanguage(RAW_LANGUAGE) ? RAW_LANGUAGE : void 0;
const API_KEYS_LIST = API_KEY ? API_KEY.split(/[,;\s]+/).map((k) => k.trim()).filter(Boolean) : [];
if (BASE_LANGUAGE) csfd.setOptions({ language: BASE_LANGUAGE });
app.use((req, res, next) => {
if (API_KEY) {
const apiKey = req.get(API_KEY_NAME)?.trim();
if (!apiKey) {
const log = {
error: "API_KEY_MISSING",
message: `Missing API key in request header: ${API_KEY_NAME}`
};
logMessage("error", log, req);
res.status(401).json(log);
return;
}
if (!API_KEYS_LIST.includes(apiKey)) {
const log = {
error: "API_KEY_INVALID",
message: `Invalid API key in request header: ${API_KEY_NAME}`
};
logMessage("error", log, req);
res.status(401).json(log);
return;
}
}
next();
});
app.get("/", (_, res) => {
logMessage("info", {
error: null,
message: "/"
});
res.json({
name,
version,
docs: homepage,
links: Object.values(Endpoint)
});
});
app.get([
"/movie/",
"/creator/",
"/search/",
"/user-ratings/",
"/user-reviews/"
], (req, res) => {
const log = {
error: "ID_MISSING",
message: `ID is missing. Provide ID like this: ${req.url}${req.url.endsWith("/") ? "" : "/"}1234`
};
logMessage("warn", log, req);
res.status(404).json(log);
});
app.get("/movie/:id", async (req, res) => {
const rawLanguage = req.query.language;
const language = isSupportedLanguage(rawLanguage) ? rawLanguage : void 0;
try {
const movie = await csfd.movie(+req.params.id, { language });
res.json(movie);
logMessage("success", {
error: null,
message: `/movie/:id: ${req.params.id}${language ? ` [${language}]` : ""}`
}, req);
} catch (error) {
const log = {
error: "MOVIE_FETCH_FAILED",
message: "Failed to fetch movie data: " + error
};
logMessage("error", log, req);
res.status(500).json(log);
}
});
app.get("/creator/:id", async (req, res) => {
const rawLanguage = req.query.language;
const language = isSupportedLanguage(rawLanguage) ? rawLanguage : void 0;
try {
const result = await csfd.creator(+req.params.id, { language });
res.json(result);
logMessage("success", {
error: null,
message: `/creator/:id: ${req.params.id}${language ? ` [${language}]` : ""}`
}, req);
} catch (error) {
const log = {
error: "CREATOR_FETCH_FAILED",
message: "Failed to fetch creator data: " + error
};
logMessage("error", log, req);
res.status(500).json(log);
}
});
app.get("/search/:query", async (req, res) => {
const rawLanguage = req.query.language;
const language = isSupportedLanguage(rawLanguage) ? rawLanguage : void 0;
try {
const result = await csfd.search(req.params.query, { language });
res.json(result);
logMessage("success", {
error: null,
message: `/search/:query: ${req.params.query}${language ? ` [${language}]` : ""}`
}, req);
} catch (error) {
const log = {
error: "SEARCH_FETCH_FAILED",
message: "Failed to fetch search data: " + error
};
logMessage("error", log, req);
res.status(500).json(log);
}
});
app.get("/user-ratings/:id", async (req, res) => {
const { allPages, allPagesDelay, excludes, includesOnly, page } = req.query;
const rawLanguage = req.query.language;
const language = isSupportedLanguage(rawLanguage) ? rawLanguage : void 0;
try {
const result = await csfd.userRatings(req.params.id, {
allPages: allPages === "true",
allPagesDelay: allPagesDelay ? +allPagesDelay : void 0,
excludes: excludes ? excludes.split(",") : void 0,
includesOnly: includesOnly ? includesOnly.split(",") : void 0,
page: page ? +page : void 0
}, { language });
res.json(result);
logMessage("success", {
error: null,
message: `/user-ratings/:id: ${req.params.id}${language ? ` [${language}]` : ""}`
}, req);
} catch (error) {
const log = {
error: "USER_RATINGS_FETCH_FAILED",
message: "Failed to fetch user-ratings data: " + error
};
logMessage("error", log, req);
res.status(500).json(log);
}
});
app.get("/user-reviews/:id", async (req, res) => {
const { allPages, allPagesDelay, excludes, includesOnly, page } = req.query;
const rawLanguage = req.query.language;
const language = isSupportedLanguage(rawLanguage) ? rawLanguage : void 0;
try {
const result = await csfd.userReviews(req.params.id, {
allPages: allPages === "true",
allPagesDelay: allPagesDelay ? +allPagesDelay : void 0,
excludes: excludes ? excludes.split(",") : void 0,
includesOnly: includesOnly ? includesOnly.split(",") : void 0,
page: page ? +page : void 0
}, { language });
res.json(result);
logMessage("success", {
error: null,
message: `/user-reviews/:id: ${req.params.id}${language ? ` [${language}]` : ""}`
}, req);
} catch (error) {
const log = {
error: "USER_REVIEWS_FETCH_FAILED",
message: "Failed to fetch user-reviews data: " + error
};
logMessage("error", log, req);
res.status(500).json(log);
}
});
app.get("/cinemas", async (req, res) => {
const rawLanguage = req.query.language;
const language = isSupportedLanguage(rawLanguage) ? rawLanguage : void 0;
try {
const result = await csfd.cinema(1, "today", { language });
logMessage("success", {
error: null,
message: `/cinemas${language ? ` [${language}]` : ""}`
}, req);
res.json(result);
} catch (error) {
const log = {
error: "CINEMAS_FETCH_FAILED",
message: "Failed to fetch cinemas data: " + error
};
logMessage("error", log, req);
res.status(500).json(log);
}
});
app.use((req, res) => {
const log = {
error: "PAGE_NOT_FOUND",
message: "The requested endpoint could not be found."
};
logMessage("warn", log, req);
res.status(404).json(log);
});
app.listen(port, () => {
console.log(`
_ __ _ _
| | / _| | | (_)
_ __ ___ __| | ___ ___ ___| |_ __| | __ _ _ __ _
| '_ \\ / _ \\ / _\` |/ _ \\ / __/ __| _/ _\` | / _\` | '_ \\| |
| | | | (_) | (_| | __/ | (__\\__ \\ || (_| | | (_| | |_) | |
|_| |_|\\___/ \\__,_|\\___| \\___|___/_| \\__,_| \\__,_| .__/|_|
| |
|_|
`);
console.log(`node-csfd-api@${version}\n`);
console.log(`Docs: ${homepage}`);
console.log(`Endpoints: ${Object.values(Endpoint).join(", ")}\n`);
console.log(`API is running on: http://localhost:${port}`);
if (BASE_LANGUAGE) console.log(`Base language configured: ${BASE_LANGUAGE}\n`);
if (API_KEYS_LIST.length === 0) console.log("\x1B[31m%s\x1B[0m", "⚠️ Server is OPEN!\n- Your server will be open to the world and potentially everyone can use it without any restriction.\n- To enable some basic protection, set API_KEY environment variable (single value or comma-separated list) and provide the same value in request header: " + API_KEY_NAME);
else console.log("\x1B[32m%s\x1B[0m", `✔️ Server is protected (somehow).\n- ${API_KEYS_LIST.length} API key(s) are configured and will be checked for each request header: ${API_KEY_NAME}`);
});
//#endregion
export { };