Merge branch 'dev' into feat/pnids-fetch-api

This commit is contained in:
Jemma Poffinbarger
2026-04-22 17:27:00 -05:00
committed by GitHub
12 changed files with 884 additions and 714 deletions

1412
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@
"@aws-sdk/client-s3": "^3.657.0",
"@aws-sdk/client-ses": "^3.515.0",
"@inquirer/prompts": "^7.2.0",
"@pretendonetwork/grpc": "^2.4.4",
"@pretendonetwork/grpc": "^2.5.2",
"bcrypt": "^5.0.0",
"buffer-crc32": "^0.2.13",
"colors": "^1.4.0",
@@ -69,7 +69,7 @@
},
"devDependencies": {
"@hcaptcha/types": "^1.0.3",
"@pretendonetwork/eslint-config": "^0.0.8",
"@pretendonetwork/eslint-config": "^0.1.4",
"@types/bcrypt": "^5.0.0",
"@types/buffer-crc32": "^0.2.2",
"@types/cors": "^2.8.13",

View File

@@ -130,6 +130,11 @@ const PNIDSchema = new Schema<IPNID, PNIDModel, IPNIDMethods>({
}
}, { id: false });
PNIDSchema.index({ pid: 1 });
PNIDSchema.index({ usernameLower: 1 });
// Used for the admin panel querying
PNIDSchema.index({ 'pid': 1, 'username': 1, 'connections.discord.id': 1 });
PNIDSchema.plugin(uniqueValidator, { message: '{PATH} already in use.' });
/*

View File

@@ -1,9 +1,9 @@
import fs from 'node:fs/promises';
import { z } from 'zod';
import mongoose from 'mongoose';
import { config, disabledFeatures } from './config-manager';
import { LOG_INFO, LOG_WARN } from './logger';
import { Server } from './models/server';
import { config, disabledFeatures } from '@/config-manager';
import { LOG_INFO, LOG_WARN } from '@/logger';
import { Server } from '@/models/server';
// Provisioning has a couple edgecases:
// - It will only update existing entries, will not add new one

View File

@@ -16,7 +16,7 @@ import api from '@/services/api';
import localcdn from '@/services/local-cdn';
import assets from '@/services/assets';
import { config, disabledFeatures } from '@/config-manager';
import { startProvisioner } from './provisioning';
import { startProvisioner } from '@/provisioning';
process.title = 'Pretendo - Account';
process.on('uncaughtException', (err, origin) => {

View File

@@ -1,7 +1,7 @@
import { Status, ServerError } from 'nice-grpc';
import { getPNIDByPID } from '@/database';
import { sendPNIDDeletedEmail } from '@/util';
import { LOG_ERROR } from '@/logger';
import { LOG_ERROR, LOG_INFO } from '@/logger';
import type { DeleteAccountRequest, DeleteAccountResponse } from '@pretendonetwork/grpc/account/v2/delete_account_rpc';
export async function deleteAccount(request: DeleteAccountRequest): Promise<DeleteAccountResponse> {
@@ -15,14 +15,26 @@ export async function deleteAccount(request: DeleteAccountRequest): Promise<Dele
}
try {
LOG_INFO(`Deleting PNID ${pnid.pid} (bypass grace period=${request.bypassGracePeriod})`);
const email = pnid.email.address;
pnid.markForDeletion();
if (request.bypassGracePeriod) {
await pnid.scrub();
} else {
pnid.markForDeletion();
}
await pnid.save();
await sendPNIDDeletedEmail(email, pnid.username);
if (request.bypassGracePeriod) {
LOG_INFO(`PNID ${pnid.pid} deleted immediately (bypassed grace period)`);
} else {
LOG_INFO(`PNID ${pnid.pid} marked for deletion (will be deleted after grace period)`);
}
} catch (error) {
LOG_ERROR(`Deleting PNID ${error}`);
LOG_ERROR(`Error deleting PNID ${pnid.pid}: ${error}`);
}
return {

View File

@@ -2,11 +2,9 @@ import { Status, ServerError } from 'nice-grpc';
import { getPNIDByAPIAccessToken } from '@/database';
import { PNID_PERMISSION_FLAGS } from '@/types/common/permission-flags';
import { config } from '@/config-manager';
import { Device } from '@/models/device';
import type { GetUserDataResponse } from '@pretendonetwork/grpc/account/v2/get_user_data_rpc';
import type { ExchangeTokenForUserDataRequest } from '@pretendonetwork/grpc/account/v2/exchange_token_for_user_data_rpc';
import type { ExchangeTokenForUserDataRequest, ExchangeTokenForUserDataResponse } from '@pretendonetwork/grpc/account/v2/exchange_token_for_user_data_rpc';
export async function exchangeTokenForUserData(request: ExchangeTokenForUserDataRequest): Promise<GetUserDataResponse> {
export async function exchangeTokenForUserData(request: ExchangeTokenForUserDataRequest): Promise<ExchangeTokenForUserDataResponse> {
if (!request.token.trim()) {
throw new ServerError(Status.INVALID_ARGUMENT, 'Invalid token');
}
@@ -17,19 +15,6 @@ export async function exchangeTokenForUserData(request: ExchangeTokenForUserData
throw new ServerError(Status.INVALID_ARGUMENT, 'Invalid token');
}
const devices = (await Device.find({
linked_pids: pnid.pid
})).map((device) => {
return {
model: device.get('model'), // ".model" gives the Mongoose model...
serial: device.serial,
linkedPids: device.linked_pids,
accessLevel: device.access_level,
serverAccessLevel: device.server_access_level,
deviceId: device.device_id
};
});
return {
deleted: pnid.deleted || pnid.marked_for_deletion,
pid: pnid.pid,
@@ -71,7 +56,6 @@ export async function exchangeTokenForUserData(request: ExchangeTokenForUserData
updateBossFiles: pnid.hasPermission(PNID_PERMISSION_FLAGS.UPDATE_BOSS_FILES),
deleteBossFiles: pnid.hasPermission(PNID_PERMISSION_FLAGS.DELETE_BOSS_FILES),
updatePnidPermissions: pnid.hasPermission(PNID_PERMISSION_FLAGS.UPDATE_PNID_PERMISSIONS)
},
linkedDevices: devices
}
};
}

View File

@@ -19,6 +19,7 @@ export async function getUserData(request: GetUserDataRequest): Promise<GetUserD
linked_pids: pnid.pid
})).map((device) => {
return {
deviceId: device.device_id,
model: device.get('model'), // ".model" gives the Mongoose model...
serial: device.serial,
linkedPids: device.linked_pids,

View File

@@ -1,3 +1,4 @@
import { ServerError, Status } from 'nice-grpc';
import { getUserData } from '@/services/grpc/account/v2/get-user-data';
import { getNEXPassword } from '@/services/grpc/account/v2/get-nex-password';
import { getNEXData } from '@/services/grpc/account/v2/get-nex-data';
@@ -9,70 +10,52 @@ import { exchangeIndependentServiceTokenForUserData } from '@/services/grpc/acco
import { exchangePasswordResetTokenForUserData } from '@/services/grpc/account/v2/exchange-password-reset-token-for-user-data';
import { validateIndependentServiceToken } from '@/services/grpc/account/v2/validate-independent-service-token';
import { deleteAccount } from '@/services/grpc/account/v2/delete-account';
import { listDevices } from '@/services/grpc/account/v2/list-devices';
import { getDevice } from '@/services/grpc/account/v2/get-device';
import { updateDevice } from '@/services/grpc/account/v2/update-device';
import { listNEXAccounts } from '@/services/grpc/account/v2/list-nex-accounts';
import { getNEXAccount } from '@/services/grpc/account/v2/get-nex-account';
import { updateNEXAccount } from '@/services/grpc/account/v2/update-nex-account';
import { listServers } from '@/services/grpc/account/v2/list-servers';
import { createServer } from '@/services/grpc/account/v2/create-server';
import { getServer } from '@/services/grpc/account/v2/get-server';
import { updateServer } from '@/services/grpc/account/v2/update-server';
import { deleteServer } from '@/services/grpc/account/v2/delete-server';
import { listPNIDs } from '@/services/grpc/account/v2/list-pnids';
import { getPNID } from '@/services/grpc/account/v2/get-pnid';
import { updatePNID } from '@/services/grpc/account/v2/update-pnid';
import { deletePNID } from '@/services/grpc/account/v2/delete-pnid';
import { listAuditLogs } from '@/services/grpc/account/v2/list-audit-logs';
import { listAuditLogComments } from '@/services/grpc/account/v2/list-audit-log-comments';
import { createAuditLogComment } from '@/services/grpc/account/v2/create-audit-log-comment';
import { listBans } from '@/services/grpc/account/v2/list-bans';
import { issueBan } from '@/services/grpc/account/v2/issue-ban';
import { getBan } from '@/services/grpc/account/v2/get-ban';
import { updateBan } from '@/services/grpc/account/v2/update-ban';
import { pardonBan } from '@/services/grpc/account/v2/pardon-ban';
import { listBanComments } from '@/services/grpc/account/v2/list-ban-comments';
import { createBanComment } from '@/services/grpc/account/v2/create-ban-comment';
import { getPNIDs } from '@/services/grpc/account/v2/get-pnids';
import type { AccountServiceImplementation } from '@pretendonetwork/grpc/account/v2/account_service';
import type { ServiceImplementation } from 'nice-grpc';
import type { AccountServiceDefinition } from '@pretendonetwork/grpc/account/v2/account_service';
export const accountServiceImplementationV2: AccountServiceImplementation = {
const notImplemented = (): never => {
throw new ServerError(Status.UNIMPLEMENTED, 'Not implemented');
};
export const accountServiceImplementationV2: ServiceImplementation<AccountServiceDefinition> = {
getUserData,
getNEXPassword,
getNEXData,
updatePNIDPermissions,
exchangeTokenForUserData,
exchangeOAuthTokenForUserData,
exchangeNEXTokenForUserData,
exchangeIndependentServiceTokenForUserData,
exchangePasswordResetTokenForUserData,
validateIndependentServiceToken,
deleteAccount,
listDevices,
getDevice,
updateDevice,
listNEXAccounts,
getNEXAccount,
updateNEXAccount,
listServers,
createServer,
getServer,
updateServer,
deleteServer,
listPNIDs,
getPNID,
updatePNID,
deletePNID,
listAuditLogs,
listAuditLogComments,
createAuditLogComment,
listBans,
issueBan,
getBan,
updateBan,
pardonBan,
listBanComments,
createBanComment,
getPNIDs
getPNIDs,
// The following methods are not yet implemented
createAuditLogComment: notImplemented,
createBanComment: notImplemented,
createServer: notImplemented,
deleteServer: notImplemented,
deletePNID: notImplemented,
exchangeIndependentServiceTokenForUserData: notImplemented,
exchangeNEXTokenForUserData: notImplemented,
exchangeOAuthTokenForUserData: notImplemented,
exchangePasswordResetTokenForUserData: notImplemented,
getBan: notImplemented,
getDevice: notImplemented,
getNEXAccount: notImplemented,
getPNID: notImplemented,
getServer: notImplemented,
issueBan: notImplemented,
listAuditLogComments: notImplemented,
listBans: notImplemented,
listDevices: notImplemented,
listNEXAccounts: notImplemented,
listPNIDs: notImplemented,
listServers: notImplemented,
listAuditLogs: notImplemented,
listBanComments: notImplemented,
pardonBan: notImplemented,
updateBan: notImplemented,
updateDevice: notImplemented,
updateNEXAccount: notImplemented,
updateServer: notImplemented,
updatePNID: notImplemented,
validateIndependentServiceToken: notImplemented
};

View File

@@ -1,10 +1,9 @@
import { Status, ServerError } from 'nice-grpc';
import { getPNIDByPID } from '@/database';
import { PNID_PERMISSION_FLAGS } from '@/types/common/permission-flags';
import type { UpdatePNIDPermissionsRequest } from '@pretendonetwork/grpc/account/v2/update_pnid_permissions_rpc';
import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty';
import type { UpdatePNIDPermissionsRequest, UpdatePNIDPermissionsResponse } from '@pretendonetwork/grpc/account/v2/update_pnid_permissions_rpc';
export async function updatePNIDPermissions(request: UpdatePNIDPermissionsRequest): Promise<Empty> {
export async function updatePNIDPermissions(request: UpdatePNIDPermissionsRequest): Promise<UpdatePNIDPermissionsResponse> {
const pnid = await getPNIDByPID(request.pid);
if (!pnid) {

View File

@@ -1983,7 +1983,7 @@
{
"area": "America/Nassau",
"language": "en",
"name": "Eastern Time (US &amp; Canada)",
"name": "Eastern Time (US & Canada)",
"utc_offset": "-18000",
"order": "0"
}
@@ -2171,14 +2171,14 @@
{
"area": "America/Dawson",
"language": "en",
"name": "Pacific Time (US &amp; Canada)",
"name": "Pacific Time (US & Canada)",
"utc_offset": "-28800",
"order": "1"
},
{
"area": "America/Cambridge_Bay",
"language": "en",
"name": "Mountain Time (US &amp; Canada)",
"name": "Mountain Time (US & Canada)",
"utc_offset": "-25200",
"order": "2"
},
@@ -2192,7 +2192,7 @@
{
"area": "America/Rainy_River",
"language": "en",
"name": "Central Time (US &amp; Canada)",
"name": "Central Time (US & Canada)",
"utc_offset": "-21600",
"order": "4"
},
@@ -2213,7 +2213,7 @@
{
"area": "America/Iqaluit",
"language": "en",
"name": "Eastern Time (US &amp; Canada)",
"name": "Eastern Time (US & Canada)",
"utc_offset": "-18000",
"order": "7"
},
@@ -5038,7 +5038,7 @@
{
"area": "America/Grand_Turk",
"language": "en",
"name": "Eastern Time (US &amp; Canada)",
"name": "Eastern Time (US & Canada)",
"utc_offset": "-14400",
"order": "3"
},
@@ -12519,7 +12519,7 @@
{
"area": "America/Grand_Turk",
"language": "en",
"name": "Eastern Time (US &amp; Canada)",
"name": "Eastern Time (US & Canada)",
"utc_offset": "-14400",
"order": "0"
}
@@ -13049,14 +13049,14 @@
{
"area": "America/Los_Angeles",
"language": "en",
"name": "Pacific Time (US &amp; Canada)",
"name": "Pacific Time (US & Canada)",
"utc_offset": "-28800",
"order": "5"
},
{
"area": "America/Boise",
"language": "en",
"name": "Mountain Time (US &amp; Canada)",
"name": "Mountain Time (US & Canada)",
"utc_offset": "-25200",
"order": "6"
},
@@ -13070,7 +13070,7 @@
{
"area": "America/Chicago",
"language": "en",
"name": "Central Time (US &amp; Canada)",
"name": "Central Time (US & Canada)",
"utc_offset": "-21600",
"order": "8"
},
@@ -13091,7 +13091,7 @@
{
"area": "America/New_York",
"language": "en",
"name": "Eastern Time (US &amp; Canada)",
"name": "Eastern Time (US & Canada)",
"utc_offset": "-18000",
"order": "11"
},

View File

@@ -11,11 +11,11 @@ import { SystemType } from '@/types/common/system-types';
import { TokenType } from '@/types/common/token-types';
import { config, disabledFeatures } from '@/config-manager';
import { LOG_ERROR } from '@/logger';
import type { IncomingHttpHeaders } from 'node:http';
import type { ParsedQs } from 'qs';
import type mongoose from 'mongoose';
import type express from 'express';
import type { ObjectCannedACL } from '@aws-sdk/client-s3';
import type { IncomingHttpHeaders } from 'node:http';
import type { TokenOptions } from '@/types/common/token-options';
import type { Token } from '@/types/common/token';
import type { IPNID, IPNIDMethods } from '@/types/mongoose/pnid';