mirror of
https://github.com/PretendoNetwork/BOSS.git
synced 2026-04-04 16:25:41 -05:00
feat: add/move utils for handling CDN files
This commit is contained in:
parent
1112c135e5
commit
6559693cd1
112
src/cdn.ts
Normal file
112
src/cdn.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import path from 'node:path';
|
||||
import { Stream } from 'node:stream';
|
||||
import fs from 'fs-extra';
|
||||
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3 } from '@aws-sdk/client-s3';
|
||||
import { config, disabledFeatures } from '@/config-manager';
|
||||
import { fileStatOrNull } from './util';
|
||||
import { logger } from './logger';
|
||||
import type { Response } from 'express';
|
||||
import type { Readable } from 'node:stream';
|
||||
import type { S3Client } from '@aws-sdk/client-s3';
|
||||
import type { NodeJsClient } from '@smithy/types';
|
||||
|
||||
let s3: NodeJsClient<S3Client> | null = null;
|
||||
if (!disabledFeatures.s3) {
|
||||
s3 = new S3({
|
||||
forcePathStyle: true,
|
||||
endpoint: config.cdn.s3.endpoint,
|
||||
region: config.cdn.s3.region,
|
||||
credentials: {
|
||||
accessKeyId: config.cdn.s3.key,
|
||||
secretAccessKey: config.cdn.s3.secret
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const cdnNamespace = {
|
||||
spr: 'spr'
|
||||
} as const;
|
||||
export type CdnNamespace = keyof typeof cdnNamespace;
|
||||
|
||||
function buildKey(namespace: CdnNamespace, key: string): string {
|
||||
return `${namespace}/${key}`;
|
||||
}
|
||||
|
||||
function buildLocalCdnPath(fullKey: string): string {
|
||||
return path.join(config.cdn.disk_path, fullKey);
|
||||
}
|
||||
|
||||
export async function getCdnFileAsStream(namespace: CdnNamespace, key: string): Promise<Readable | null> {
|
||||
const fullKey = buildKey(namespace, key);
|
||||
|
||||
if (!s3) {
|
||||
const filePath = buildLocalCdnPath(fullKey);
|
||||
const fileInfo = await fileStatOrNull(filePath);
|
||||
|
||||
if (!fileInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fs.createReadStream(filePath);
|
||||
}
|
||||
|
||||
const response = await s3.send(new GetObjectCommand({
|
||||
Key: fullKey,
|
||||
Bucket: config.cdn.s3.bucket
|
||||
}));
|
||||
|
||||
if (!response.Body) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.Body;
|
||||
}
|
||||
|
||||
export async function uploadCdnFile(namespace: CdnNamespace, key: string, data: Buffer): Promise<void> {
|
||||
const fullKey = buildKey(namespace, key);
|
||||
|
||||
if (!s3) {
|
||||
const filePath = buildLocalCdnPath(fullKey);
|
||||
const folder = path.dirname(filePath);
|
||||
await fs.ensureDir(folder);
|
||||
await fs.writeFile(filePath, data);
|
||||
return;
|
||||
}
|
||||
|
||||
await s3.send(new PutObjectCommand({
|
||||
Key: fullKey,
|
||||
Bucket: config.cdn.s3.bucket,
|
||||
Body: data,
|
||||
ACL: 'private'
|
||||
}));
|
||||
}
|
||||
|
||||
export async function deleteCdnFile(namespace: CdnNamespace, key: string): Promise<void> {
|
||||
const fullKey = buildKey(namespace, key);
|
||||
|
||||
if (!s3) {
|
||||
const filePath = buildLocalCdnPath(fullKey);
|
||||
const fileInfo = await fileStatOrNull(filePath);
|
||||
if (!fileInfo || !fileInfo.isFile()) {
|
||||
return; // Not found or not a file
|
||||
}
|
||||
|
||||
await fs.unlink(filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
await s3.send(new DeleteObjectCommand({
|
||||
Key: fullKey,
|
||||
Bucket: config.cdn.s3.bucket
|
||||
}));
|
||||
}
|
||||
|
||||
export function streamFileToResponse(response: Response, stream: Readable, headers: Record<string, string> = {}): void {
|
||||
response.setHeaders(new Headers(headers));
|
||||
Stream.pipeline(stream, response, (err) => {
|
||||
if (err) {
|
||||
logger.error('Error with response stream: ' + err.message);
|
||||
response.end();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import type { GetUserFriendPIDsResponse } from '@pretendonetwork/grpc/friends/ge
|
|||
import type { NodeJsClient } from '@smithy/types';
|
||||
import type { Response } from 'express';
|
||||
import type { Readable } from 'node:stream';
|
||||
import type { Stats } from 'node:fs';
|
||||
|
||||
let s3: NodeJsClient<S3Client>;
|
||||
|
||||
|
|
@ -98,6 +99,14 @@ export function isValidFileNotifyCondition(condition: string): boolean {
|
|||
return VALID_FILE_NOTIFY_CONDITIONS.includes(condition);
|
||||
}
|
||||
|
||||
export async function fileStatOrNull(filePath: string): Promise<Stats | null> {
|
||||
try {
|
||||
return await fs.stat(filePath);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUserDataByPID(pid: number): Promise<GetUserDataResponse | null> {
|
||||
try {
|
||||
return await gRPCAccountClient.getUserData({
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user