mirror of
https://github.com/cstaelen/tidarr.git
synced 2026-10-22 02:52:33 -05:00
166 lines
5.2 KiB
TypeScript
166 lines
5.2 KiB
TypeScript
import cors from "cors";
|
|
import dotenv from "dotenv";
|
|
import express, { Express, Response } from "express";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
|
|
import { setAppInstance } from "./src/helpers/app-instance";
|
|
import { getBasePath } from "./src/helpers/base-path";
|
|
import { checkConfig } from "./src/helpers/check-config";
|
|
import { get_tiddl_config } from "./src/helpers/get_tiddl_config";
|
|
import { gracefulShutdown } from "./src/helpers/gracefull_shutdown";
|
|
import { logExpiresToken } from "./src/helpers/refresh-token";
|
|
import { ProcessingStack } from "./src/processing/core/processing-manager";
|
|
import { cleanFolder } from "./src/processing/utils/jobs";
|
|
import { setupJellyfinProxy } from "./src/proxies/jellyfin";
|
|
import { setupNavidromeProxy } from "./src/proxies/navidrome";
|
|
import { setupPlexProxy } from "./src/proxies/plex";
|
|
import { setupTidalProxy } from "./src/proxies/tidal";
|
|
// Import routers
|
|
import apiKeyRouter from "./src/routes/api-key";
|
|
import authRouter from "./src/routes/auth";
|
|
import configRouter from "./src/routes/config";
|
|
import historyRouter from "./src/routes/history";
|
|
import lidarrRouter from "./src/routes/lidarr";
|
|
import oidcRouter from "./src/routes/oidc";
|
|
import playRouter from "./src/routes/playback";
|
|
import processingRouter from "./src/routes/processing";
|
|
import sseRouter from "./src/routes/sse";
|
|
import syncRouter from "./src/routes/sync";
|
|
import tiddlTomlRouter from "./src/routes/tiddl-toml";
|
|
import { getOrCreateApiKey } from "./src/services/api-key";
|
|
import { configureServer } from "./src/services/config";
|
|
import { loadHistoryFromFile } from "./src/services/history";
|
|
import { createSyncCronJob } from "./src/services/sync";
|
|
|
|
import customCssRouter from "./src/routes/custom-css";
|
|
|
|
dotenv.config({
|
|
path: path.join(__dirname, "../.env"),
|
|
override: false,
|
|
quiet: true,
|
|
});
|
|
|
|
const port = parseInt(process.env.PORT || "8484", 10);
|
|
const hostname = "0.0.0.0";
|
|
const basePath = getBasePath();
|
|
|
|
const app: Express = express();
|
|
|
|
// Make app instance available globally
|
|
setAppInstance(app);
|
|
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: false }));
|
|
app.use(cors());
|
|
|
|
// Strip URL_BASE so every route below stays declared without prefix
|
|
if (basePath) {
|
|
app.use((req, res, next) => {
|
|
if (req.path === basePath) {
|
|
// Keep the query string (e.g. ?token=...)
|
|
return res.redirect(`${basePath}/${req.url.slice(basePath.length)}`);
|
|
}
|
|
if (req.path.startsWith(`${basePath}/`)) {
|
|
req.url = req.url.slice(basePath.length);
|
|
}
|
|
next();
|
|
});
|
|
}
|
|
|
|
// Setup all API proxies (Tidal, Plex, Navidrome)
|
|
|
|
setupTidalProxy(app);
|
|
setupPlexProxy(app);
|
|
setupJellyfinProxy(app);
|
|
setupNavidromeProxy(app);
|
|
|
|
const processingList = ProcessingStack();
|
|
app.locals.processingStack = processingList;
|
|
app.locals.addOutputLog = processingList.actions.addOutputLog;
|
|
app.locals.activeListConnections = [];
|
|
app.locals.activeItemOutputConnections = new Map<string, Response[]>();
|
|
app.locals.history = [];
|
|
app.locals.historySet = new Set<string>();
|
|
|
|
app.all("/{*any}", function (_req, res, next) {
|
|
res.header("Access-Control-Allow-Origin", "*");
|
|
res.header(
|
|
"Access-Control-Allow-Headers",
|
|
"Origin, X-Requested-With, Content-Type, Accept",
|
|
);
|
|
|
|
next();
|
|
});
|
|
|
|
// Register routers
|
|
|
|
app.use("/api", authRouter);
|
|
app.use("/api", oidcRouter);
|
|
app.use("/api", processingRouter);
|
|
app.use("/api", sseRouter);
|
|
app.use("/api", configRouter);
|
|
app.use("/api", syncRouter);
|
|
app.use("/api", customCssRouter);
|
|
app.use("/api", tiddlTomlRouter);
|
|
app.use("/api", historyRouter);
|
|
app.use("/api", playRouter);
|
|
app.use("/api", apiKeyRouter);
|
|
app.use("/api", lidarrRouter);
|
|
|
|
// Run
|
|
|
|
checkConfig();
|
|
|
|
const server = app.listen(port, async () => {
|
|
const config = await configureServer();
|
|
app.locals.config = config;
|
|
|
|
// Load tiddl config on startup
|
|
const { config: tiddlConfig } = get_tiddl_config();
|
|
app.locals.tiddlConfig = tiddlConfig;
|
|
|
|
// Clean up all processing folders
|
|
await cleanFolder();
|
|
|
|
// Log token expiration status
|
|
logExpiresToken(tiddlConfig?.auth?.expires_at);
|
|
|
|
// Generate or load API key on startup
|
|
const apiKey = getOrCreateApiKey();
|
|
console.log(`🔑 [TIDARR] API key ready (${apiKey.substring(0, 8)}...)`);
|
|
|
|
// Load download history
|
|
const history = await loadHistoryFromFile();
|
|
app.locals.history = history;
|
|
app.locals.historySet = new Set(history);
|
|
|
|
// Load queue file on startup
|
|
await app.locals.processingStack.actions.loadDataFromFile();
|
|
console.log(`✅ [QUEUE] Queue file loaded.`);
|
|
|
|
// Initiate processing cron job
|
|
await createSyncCronJob(app);
|
|
|
|
console.log(`⚡️ [SERVER]: Server is running at http://${hostname}:${port}`);
|
|
});
|
|
|
|
// Graceful shutdown
|
|
process.on("SIGTERM", () => gracefulShutdown("SIGTERM", app, server));
|
|
process.on("SIGINT", () => gracefulShutdown("SIGINT", app, server));
|
|
|
|
// fallback load app
|
|
|
|
const frontendFiles = "/tidarr/app/build";
|
|
if (fs.existsSync(frontendFiles)) {
|
|
// The <base> tag lets the prebuilt (relative) assets resolve under URL_BASE
|
|
const indexHtml = fs
|
|
.readFileSync(path.join(frontendFiles, "index.html"), "utf8")
|
|
.replace('<base href="/" />', `<base href="${basePath}/" />`);
|
|
|
|
app.use(express.static(frontendFiles, { index: false }));
|
|
app.get("/{*any}", (_, res) => {
|
|
res.type("html").send(indexHtml);
|
|
});
|
|
}
|