feat: make all boss services use database + cdn for datasource

This commit is contained in:
mrjvs
2025-09-03 19:37:04 +02:00
parent 6559693cd1
commit 297ececced
5 changed files with 62 additions and 34 deletions

View File

@@ -7,6 +7,7 @@ const FileSchema = new mongoose.Schema<IFile, FileModel, IFileMethods>({
type: Boolean,
default: false
},
file_key: String,
data_id: Number, // TODO - Wait until https://github.com/typegoose/auto-increment/pull/21 is merged and then change this to BigInt
task_id: String,
boss_app_id: String,

View File

@@ -1,24 +1,31 @@
import path from 'node:path';
import express from 'express';
import { fileErrCallback } from '@/util';
import { __appRoot } from '@/app-root';
import { restrictHostnames } from '@/middleware/host-limit';
import { config } from '@/config-manager';
import { File } from '@/models/file';
import { getCdnFileAsStream, streamFileToResponse } from '@/cdn';
const npdi = express.Router();
npdi.get('/p01/data/1/:titleHash/:dataID/:fileHash', (request, response) => {
const { titleHash, fileHash } = request.params;
const contentPath = path.normalize(`${__appRoot}/../cdn/content/encrypted/${titleHash}/${fileHash}`);
npdi.get('/p01/data/1/:titleHash/:dataID/:fileHash', async (request, response) => {
const { dataID } = request.params;
response.sendFile(contentPath, {
headers: {
// * The misspelling here is intentional, it's what the official server sets
'Content-Type': 'applicatoin/octet-stream',
'Content-Disposition': 'attachment',
'Content-Transfer-Encoding': 'binary'
}
}, fileErrCallback(response));
const file = await File.findOne({
data_id: dataID
});
if (!file) {
return response.sendStatus(404);
}
const fileStream = await getCdnFileAsStream('taskFile', file.file_key);
if (!fileStream) {
throw new Error('File not found in CDN');
}
return streamFileToResponse(response, fileStream, {
// * The misspelling here is intentional, it's what the official server sets
'Content-Type': 'applicatoin/octet-stream',
'Content-Disposition': 'attachment',
'Content-Transfer-Encoding': 'binary'
});
});
const router = express.Router();

View File

@@ -1,10 +1,8 @@
import { Stream } from 'node:stream';
import express from 'express';
import { getTaskFile } from '@/database';
import { getCDNFileStream } from '@/util';
import { logger } from '@/logger';
import { config } from '@/config-manager';
import { restrictHostnames } from '@/middleware/host-limit';
import { getCdnFileAsStream, streamFileToResponse } from '@/cdn';
const npdl = express.Router();
@@ -31,21 +29,14 @@ npdl.get([
return;
}
const key = `${appID}/${taskID}/${file.hash}`;
const readStream = await getCDNFileStream(key);
const readStream = await getCdnFileAsStream('taskFile', file.file_key);
if (!readStream) {
response.sendStatus(404);
return;
}
response.setHeader('Last-Modified', new Date(Number(file.updated)).toUTCString());
Stream.pipeline(readStream, response, (err) => {
if (err) {
logger.error('Error with response stream: ' + err.message);
response.end();
}
return streamFileToResponse(response, readStream, {
'Last-Modified': new Date(Number(file.updated)).toUTCString()
});
});

View File

@@ -1,27 +1,55 @@
import path from 'node:path';
import express from 'express';
import xmlbuilder from 'xmlbuilder';
import { fileErrCallback } from '@/util';
import { __appRoot } from '@/app-root';
import { config } from '@/config-manager';
import { restrictHostnames } from '@/middleware/host-limit';
import { Task } from '@/models/task';
import { File } from '@/models/file';
const npts = express.Router();
npts.get('/p01/tasksheet/:id/:hash/:fileName', (request, response) => {
const { id, hash, fileName } = request.params;
const tasksheetPath = path.normalize(`${__appRoot}/../cdn/tasksheet/${id}/${hash}/${fileName}`);
npts.get('/p01/tasksheet/:id/:hash/:taskId', async (request, response) => {
const { id, taskId } = request.params;
response.sendFile(tasksheetPath, {
headers: {
'Content-Type': 'text/xml'
const task = await Task.findOne({
title_id: id,
id: taskId
});
if (!task) {
return response.sendStatus(404);
}
const files = await File.find({
task_id: taskId
});
const xmlContent = {
TaskSheet: {
TitleId: task.title_id,
TaskId: task.id,
ServiceStatus: task.status,
Files: {
File: files.map(f => ({
Filename: f.name,
DataId: f.data_id,
Type: f.type,
Url: `https://${config.domains.npdi}/p01/data/1/${task.title_id}/${f.data_id}/${f.hash}`
}))
}
}
}, fileErrCallback(response));
};
response.set('Content-Type', 'application/xml; charset=utf-8');
response.send(xmlbuilder.create(xmlContent, { headless: true }).end({ pretty: true }));
});
npts.get('/p01/tasksheet/:id/:hash/:subfolder/:fileName', (request, response) => {
const { id, hash, subfolder, fileName } = request.params;
const tasksheetPath = path.normalize(`${__appRoot}/../cdn/tasksheet/${id}/${hash}/_subfolder/${subfolder}/${fileName}`);
// TODO add subfolder support
response.sendFile(tasksheetPath, {
headers: {
'Content-Type': 'text/xml'

View File

@@ -2,6 +2,7 @@ import type { Model, HydratedDocument } from 'mongoose';
export interface IFile {
deleted: boolean;
file_key: string;
data_id: bigint;
task_id: string;
boss_app_id: string;