mirror of
https://github.com/PretendoNetwork/BOSS.git
synced 2026-04-17 10:36:49 -05:00
feat: make SPR use the CDN for data
This commit is contained in:
parent
297ececced
commit
fa2b048015
12
src/cdn.ts
12
src/cdn.ts
|
|
@ -1,5 +1,6 @@
|
|||
import path from 'node:path';
|
||||
import { Stream } from 'node:stream';
|
||||
import { buffer as bufferConsumer } from 'node:stream/consumers';
|
||||
import fs from 'fs-extra';
|
||||
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3 } from '@aws-sdk/client-s3';
|
||||
import { config, disabledFeatures } from '@/config-manager';
|
||||
|
|
@ -24,7 +25,8 @@ if (!disabledFeatures.s3) {
|
|||
}
|
||||
|
||||
export const cdnNamespace = {
|
||||
spr: 'spr'
|
||||
spr: 'spr',
|
||||
taskFile: 'taskFile'
|
||||
} as const;
|
||||
export type CdnNamespace = keyof typeof cdnNamespace;
|
||||
|
||||
|
|
@ -62,6 +64,14 @@ export async function getCdnFileAsStream(namespace: CdnNamespace, key: string):
|
|||
return response.Body;
|
||||
}
|
||||
|
||||
export async function getCdnFileAsBuffer(namespace: CdnNamespace, key: string): Promise<Buffer | null> {
|
||||
const stream = await getCdnFileAsStream(namespace, key);
|
||||
if (!stream) {
|
||||
return null;
|
||||
}
|
||||
return bufferConsumer(stream);
|
||||
}
|
||||
|
||||
export async function uploadCdnFile(namespace: CdnNamespace, key: string, data: Buffer): Promise<void> {
|
||||
const fullKey = buildKey(namespace, key);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const CECDataSchema = new mongoose.Schema<ICECData, CECDataModel, ICECDataMethod
|
|||
creator_pid: Number,
|
||||
game_id: Number,
|
||||
data: String,
|
||||
file_key: String,
|
||||
data_hash: String,
|
||||
size: Number,
|
||||
created: { type: BigInt, index: true }
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import RequestException from '@/request-exception';
|
|||
import { config } from '@/config-manager';
|
||||
import { restrictHostnames } from '@/middleware/host-limit';
|
||||
import { logger } from '@/logger';
|
||||
import { getCdnFileAsBuffer, uploadCdnFile } from '@/cdn';
|
||||
import type { SPRSlot } from '@/types/common/spr-slot';
|
||||
|
||||
const spr = express.Router();
|
||||
|
|
@ -109,7 +110,7 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
const metadataHeaders = sprMetadata.split('\r\n'); // * Split header lines
|
||||
|
||||
if (metadataHeaders.length < 1) {
|
||||
logger.warn(`{request.pid}: spr-meta file is too short / empty`);
|
||||
logger.warn(`${request.pid}: spr-meta file is too short / empty`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
|
@ -118,7 +119,7 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
const metadataHeader = metadataHeaders[i];
|
||||
const [header, value] = metadataHeader.split(': '); // * Split header and value
|
||||
if (!header || !value) {
|
||||
logger.warn(`{request.pid}: Bad spr-meta entry`);
|
||||
logger.warn(`${request.pid}: Bad spr-meta entry`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
|
@ -127,7 +128,7 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
// * we can guarantee that i must match with the slot we are looking at except for 0, which will be the slotsize
|
||||
if (i === 0) {
|
||||
if (header !== 'slotsize') {
|
||||
logger.warn(`{request.pid}: spr-meta missing slotsize`);
|
||||
logger.warn(`${request.pid}: spr-meta missing slotsize`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
|
@ -137,14 +138,14 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
try {
|
||||
slotsize = parseInt(value);
|
||||
} catch {
|
||||
logger.warn(`{request.pid}: Invalid spr-meta slotsize`);
|
||||
logger.warn(`${request.pid}: Invalid spr-meta slotsize`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
// * We don't count the slotsize header itself in the slot count
|
||||
if (slotsize !== (metadataHeaders.length - 1)) {
|
||||
logger.warn(`{request.pid}: Bad spr-meta slotsize`);
|
||||
logger.warn(`${request.pid}: Bad spr-meta slotsize`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
|
@ -155,7 +156,7 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
const metadata = value.split(','); // * Split the value to get the metadata
|
||||
|
||||
if (metadata.length !== 3) {
|
||||
logger.warn(`{request.pid}: Bad spr-meta entry param count`);
|
||||
logger.warn(`${request.pid}: Bad spr-meta entry param count`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
|
@ -168,7 +169,7 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
gameID = parseInt(metadata[1], 16);
|
||||
size = parseInt(metadata[2]);
|
||||
} catch {
|
||||
logger.warn(`{request.pid}: Invalid spr-meta entry params`);
|
||||
logger.warn(`${request.pid}: Invalid spr-meta entry params`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
|
@ -179,13 +180,13 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
const slotData: Buffer | undefined = request.files['spr-slot' + slot];
|
||||
|
||||
if (!slotData) {
|
||||
logger.warn(`{request.pid}: Missing slot data file`);
|
||||
logger.warn(`${request.pid}: Missing slot data file`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (slotData.length !== size) {
|
||||
logger.warn(`{request.pid}: Invalid slot data size`);
|
||||
logger.warn(`${request.pid}: Invalid slot data size`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
|
@ -200,25 +201,25 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
|
||||
// * Check that we at least have enough size for the StreetPass header
|
||||
if (slotData.length < 0x12) {
|
||||
logger.warn(`{request.pid}: Slot is too short`);
|
||||
logger.warn(`${request.pid}: Slot is too short`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (slotData.readUInt32LE() !== 0x6161) {
|
||||
logger.warn(`{request.pid}: Slot header missmatch`);
|
||||
logger.warn(`${request.pid}: Slot header missmatch`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (slotData.readUInt32LE(4) !== size) {
|
||||
logger.warn(`{request.pid}: Slot bad size`);
|
||||
logger.warn(`${request.pid}: Slot bad size`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (slotData.readUInt32LE(8) !== gameID) {
|
||||
logger.warn(`{request.pid}: Slot bad gameID`);
|
||||
logger.warn(`${request.pid}: Slot bad gameID`);
|
||||
response.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
|
@ -248,10 +249,12 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
let slotData = await getDuplicateCECData(request.pid, sprSlot.gameID);
|
||||
|
||||
if (!slotData || slotData.data_hash !== dataHash) {
|
||||
const fileKey = `${request.pid}-${dataHash}`;
|
||||
await uploadCdnFile('spr', fileKey, sprSlot.data);
|
||||
slotData = await CECData.create({
|
||||
creator_pid: request.pid,
|
||||
game_id: sprSlot.gameID,
|
||||
data: sprSlot.data.toString('base64'),
|
||||
file_key: fileKey,
|
||||
data_hash: dataHash,
|
||||
size: sprSlot.size,
|
||||
created: BigInt(Date.now())
|
||||
|
|
@ -271,17 +274,17 @@ spr.post('/relay/0', multipartParser, async (request, response) => {
|
|||
}
|
||||
|
||||
// * Receive slot data
|
||||
sprSlot.size = 0;
|
||||
if (sprSlot.sendMode !== SendMode.SendOnly) {
|
||||
const slotData = await getRandomCECData(userFriends.pids, sprSlot.gameID);
|
||||
|
||||
if (slotData) {
|
||||
sprData = Buffer.concat([sprData, Buffer.from(slotData.data, 'base64')]);
|
||||
sprSlot.size = slotData.size;
|
||||
} else {
|
||||
sprSlot.size = 0;
|
||||
const fileData = await getCdnFileAsBuffer('spr', slotData.file_key);
|
||||
if (fileData) {
|
||||
sprData = Buffer.concat([sprData, fileData]);
|
||||
sprSlot.size = slotData.size;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sprSlot.size = 0;
|
||||
}
|
||||
|
||||
response.setHeader(`X-Spr-Slot${slot}-Result`, `${sprSlot.gameID.toString(16).toUpperCase().padStart(8, '0')},${sprSlot.sendMode},${sprSlot.size}`);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export interface ICECData {
|
|||
data: string;
|
||||
data_hash: string;
|
||||
size: number;
|
||||
file_key: string;
|
||||
created: bigint;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user