Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Fix Memory Leaks & other minor fixes #15

Merged
merged 22 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
96d2ccf
chore: Add @smithy/types for correct type for s3 client
binaryoverload Jan 7, 2025
1fd43ce
fix: Use Stream.pipeline for response stream to properly close s3 stream
binaryoverload Jan 7, 2025
068a635
perf: Remove sync IO from npdi and npts
binaryoverload Jan 7, 2025
956852f
chore: Add lint:fix script and fix auto-fixable linting issues
binaryoverload Jan 7, 2025
f4b8f44
chore: Update dependencies in package.json
binaryoverload Jan 7, 2025
9971f8f
fix: Only response if not sentHeaders and log error message
binaryoverload Jan 7, 2025
c3c1dd2
refactor: Update logger to use console.error for error logging
binaryoverload Jan 7, 2025
d77c15d
refactor: Use LOG_ERROR for error logging for timestamps
binaryoverload Jan 7, 2025
69609c3
fix: Add properly padded timestamp to logger with date added
binaryoverload Jan 7, 2025
9fff2a4
fix: Correct typo in Content-Type header value
binaryoverload Jan 7, 2025
6f292b6
Revert "fix: Correct typo in Content-Type header value" and add comment
binaryoverload Jan 7, 2025
aae2435
Apply suggestions from code review
binaryoverload Jan 8, 2025
7e37300
chore: add .editorconfig and move scripts into folder, remove logger …
binaryoverload Jan 8, 2025
ab0a6cc
refactor: remove authentication middleware - unneeded and caused esli…
binaryoverload Jan 8, 2025
a5dbb27
chore: add JSON and YML exceptions to editorconfig
binaryoverload Jan 8, 2025
8689988
feat: Add linting GitHub actions workflow
binaryoverload Jan 8, 2025
065f57c
fix: add getNEXDataByPID import to spr
binaryoverload Jan 8, 2025
60df36a
chore: update eslint + deps, add new rules, update tsconfig
binaryoverload Jan 8, 2025
45d2be8
style: run new eslint on PR affected files
binaryoverload Jan 8, 2025
9bebbe6
style: run new eslint config on EVERYTHING ELSE
binaryoverload Jan 8, 2025
eb2585d
Revert "refactor: remove authentication middleware - unneeded and cau…
binaryoverload Jan 8, 2025
546ae04
chore: update eslint to disable atomic-updates checks
binaryoverload Jan 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,445 changes: 3,052 additions & 2,393 deletions package-lock.json

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"main": "dist/server.js",
"scripts": {
"lint": "npx eslint .",
"lint:fix": "npx eslint . --fix",
"build": "npm run lint && npm run clean && npx tsc && npx tsc-alias",
"clean": "rimraf ./dist",
"start": "node --enable-source-maps ."
Expand All @@ -13,36 +14,37 @@
"author": "",
"license": "ISC",
"dependencies": {
"@aws-sdk/client-s3": "^3.395.0",
"@aws-sdk/client-s3": "^3.723.0",
"@pretendonetwork/boss-crypto": "^1.0.0",
"@pretendonetwork/grpc": "^1.0.6",
"@typegoose/auto-increment": "^3.6.1",
"boss-js": "github:PretendoNetwork/boss-js",
"cacache": "^18.0.0",
"cacache": "^18.0.4",
"colors": "^1.4.0",
"dicer": "^0.3.1",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"express-subdomain": "^1.0.5",
"express": "^4.21.2",
"express-subdomain": "^1.0.6",
"fs-extra": "^11.2.0",
"moment": "^2.29.4",
"mongoose": "^7.4.3",
"moment": "^2.30.1",
"mongoose": "~7.6.1",
"morgan": "^1.10.0",
"nice-grpc": "^2.1.5",
"nice-grpc": "^2.1.10",
"xmlbuilder": "^15.1.1"
},
"devDependencies": {
"@smithy/types": "^4.0.0",
"@types/dicer": "^0.2.4",
"@types/express": "^4.17.17",
"@types/fs-extra": "^11.0.1",
"@types/morgan": "^1.9.4",
"@types/node": "^20.5.0",
"@typescript-eslint/eslint-plugin": "^6.4.0",
"@typescript-eslint/parser": "^6.4.0",
"axios": "^1.6.2",
"eslint": "^8.47.0",
"tsc-alias": "^1.8.7",
"typescript": "^5.1.6",
"xmlbuilder2": "^3.0.2"
"@types/express": "^4.17.21",
"@types/fs-extra": "^11.0.4",
"@types/morgan": "^1.9.9",
"@types/node": "^20.17.12",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"axios": "^1.7.9",
"eslint": "^8.57.1",
"tsc-alias": "^1.8.10",
"typescript": "~5.3.0",
"xmlbuilder2": "^3.1.1"
}
}
2 changes: 1 addition & 1 deletion src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function LOG_ERROR(input: string): void {
input = `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}] [ERROR]: ${input}`;
streams.error.write(`${input}\n`);

console.log(`${input}`.red.bold);
console.error(`${input}`.red.bold);
}

export function LOG_WARN(input: string): void {
Expand Down
18 changes: 8 additions & 10 deletions src/services/npdi.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import path from 'node:path';
import fs from 'fs-extra';
import express from 'express';
import subdomain from 'express-subdomain';
import { fileErrCallback } from '@/util';

const npdi = express.Router();

npdi.get('/p01/data/1/:titleHash/:dataID/:fileHash', (request, response) => {
const { titleHash, fileHash } = request.params;
const contentPath = path.normalize(`${__dirname}/../../cdn/content/encrypted/${titleHash}/${fileHash}`);

if (fs.existsSync(contentPath)) {
response.set('Content-Type', 'applicatoin/octet-stream');
response.set('Content-Disposition', 'attachment');
response.set('Content-Transfer-Encoding', 'binary');
response.set('Content-Type', 'applicatoin/octet-stream');
response.sendFile(contentPath);
} else {
response.sendStatus(404);
}
response.sendFile(contentPath, {
headers: {
'Content-Type': 'applicatoin/octet-stream',
'Content-Disposition': 'attachment',
'Content-Transfer-Encoding': 'binary',
}
}, fileErrCallback(response));
});

const router = express.Router();
Expand Down
10 changes: 9 additions & 1 deletion src/services/npdl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import express from 'express';
import subdomain from 'express-subdomain';
import { getTaskFile } from '@/database';
import { getCDNFileStream } from '@/util';
import { Stream } from 'node:stream';
import { LOG_ERROR } from '@/logger';

const npdl = express.Router();

Expand Down Expand Up @@ -37,7 +39,13 @@ npdl.get([
}

response.setHeader('Last-Modified', new Date(Number(file.updated)).toUTCString());
readStream.pipe(response);

Stream.pipeline(readStream, response, (err) => {
if (err) {
LOG_ERROR('Error with response stream: ' + err.message);
response.end();
}
});
});

const router = express.Router();
Expand Down
24 changes: 11 additions & 13 deletions src/services/npts.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import path from 'node:path';
import fs from 'fs-extra';
import express from 'express';
import subdomain from 'express-subdomain';
import { fileErrCallback } from '@/util';

const npts = express.Router();

npts.get('/p01/tasksheet/:id/:hash/:fileName', (request, response) => {
const { id, hash, fileName } = request.params;
const tasksheetPath = path.normalize(`${__dirname}/../../cdn/tasksheet/${id}/${hash}/${fileName}`);

if (fs.existsSync(tasksheetPath)) {
response.set('Content-Type', 'text/xml');
response.sendFile(tasksheetPath);
} else {
response.sendStatus(404);
}
response.sendFile(tasksheetPath, {
headers: {
'Content-Type': 'text/xml'
}
}, fileErrCallback(response));
});

npts.get('/p01/tasksheet/:id/:hash/:subfolder/:fileName', (request, response) => {
const { id, hash, subfolder, fileName } = request.params;
const tasksheetPath = path.normalize(`${__dirname}/../../cdn/tasksheet/${id}/${hash}/_subfolder/${subfolder}/${fileName}`);

if (fs.existsSync(tasksheetPath)) {
response.set('Content-Type', 'text/xml');
response.sendFile(tasksheetPath);
} else {
response.sendStatus(404);
}
response.sendFile(tasksheetPath, {
headers: {
'Content-Type': 'text/xml'
}
}, fileErrCallback(response));
});

const router = express.Router();
Expand Down
2 changes: 1 addition & 1 deletion src/types/mongoose/cec-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ export interface ICECDataMethods {}

interface ICECDataQueryHelpers {}

export interface CECDataModel extends Model<ICECData, ICECDataQueryHelpers, ICECDataMethods> {}
export type CECDataModel = Model<ICECData, ICECDataQueryHelpers, ICECDataMethods>

export type HydratedCECDataDocument = HydratedDocument<ICECData, ICECDataMethods>
2 changes: 1 addition & 1 deletion src/types/mongoose/cec-slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ export interface ICECSlotMethods {}

interface ICECSlotQueryHelpers {}

export interface CECSlotModel extends Model<ICECSlot, ICECSlotQueryHelpers, ICECSlotMethods> {}
export type CECSlotModel = Model<ICECSlot, ICECSlotQueryHelpers, ICECSlotMethods>

export type HydratedCECSlotDocument = HydratedDocument<ICECSlot, ICECSlotMethods>
2 changes: 1 addition & 1 deletion src/types/mongoose/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ export interface IFileMethods {}

interface IFileQueryHelpers {}

export interface FileModel extends Model<IFile, IFileQueryHelpers, IFileMethods> {}
export type FileModel = Model<IFile, IFileQueryHelpers, IFileMethods>

export type HydratedFileDocument = HydratedDocument<IFile, IFileMethods>
2 changes: 1 addition & 1 deletion src/types/mongoose/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ export interface ITaskMethods {}

interface ITaskQueryHelpers {}

export interface TaskModel extends Model<ITask, ITaskQueryHelpers, ITaskMethods> {}
export type TaskModel = Model<ITask, ITaskQueryHelpers, ITaskMethods>

export type HydratedTaskDocument = HydratedDocument<ITask, ITaskMethods>
26 changes: 22 additions & 4 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import path from 'node:path';
import { Readable } from 'node:stream';
import fs from 'fs-extra';
import { createChannel, createClient, Metadata } from 'nice-grpc';
import { GetObjectCommand, PutObjectCommand, S3 } from '@aws-sdk/client-s3';
import { GetObjectCommand, PutObjectCommand, S3, S3Client } from '@aws-sdk/client-s3';
import { AccountClient, AccountDefinition } from '@pretendonetwork/grpc/account/account_service';
import { FriendsClient, FriendsDefinition } from '@pretendonetwork/grpc/friends/friends_service';
import { GetNEXDataResponse } from '@pretendonetwork/grpc/account/get_nex_data_rpc';
import { GetUserDataResponse } from '@pretendonetwork/grpc/account/get_user_data_rpc';
import { GetUserFriendPIDsResponse } from '@pretendonetwork/grpc/friends/get_user_friend_pids_rpc';
import { config, disabledFeatures } from '@/config-manager';
import { NodeJsClient } from '@smithy/types';
import { Response } from 'express';
import { LOG_ERROR } from '@/logger';

let s3: S3;
let s3: NodeJsClient<S3Client>;

if (!disabledFeatures.s3) {
s3 = new S3({
Expand Down Expand Up @@ -57,6 +60,21 @@ const VALID_FILE_NOTIFY_CONDITIONS = [
'app', 'account'
];

export function fileErrCallback(response: Response) {
return (err: NodeJS.ErrnoException): void => {
if (err) {
if (err.code === 'ENOENT') {
response.sendStatus(404);
} else {
if (!response.headersSent) {
response.status(500).send('Server Error');
}
LOG_ERROR('Error in sending file: ' + err.message);
}
}
};
}

export function md5(input: crypto.BinaryLike): string {
return crypto.createHash('md5').update(input).digest('hex');
}
Expand Down Expand Up @@ -138,7 +156,7 @@ export async function getFriends(pid: number): Promise<GetUserFriendPIDsResponse
}
}

export async function getCDNFileStream(key: string): Promise<Readable | fs.ReadStream | null> {
export async function getCDNFileStream(key: string): Promise<Readable | null> {
try {
if (disabledFeatures.s3) {
return await getLocalCDNFile(key);
Expand All @@ -152,7 +170,7 @@ export async function getCDNFileStream(key: string): Promise<Readable | fs.ReadS
return null;
}

return response.Body as Readable;
return response.Body;
}
} catch (error) {
return null;
Expand Down
Loading