feat: add support for multiple server IPs

This commit is contained in:
mrjvs 2026-01-11 17:15:15 +01:00
parent 2f5a4b95db
commit a924002d0b
5 changed files with 35 additions and 8 deletions

View File

@ -1,10 +1,11 @@
import { Schema, model } from 'mongoose';
import uniqueValidator from 'mongoose-unique-validator';
import type { IServer, IServerMethods, ServerModel } from '@/types/mongoose/server';
import type { IServer, IServerConnectInfo, IServerMethods, ServerModel } from '@/types/mongoose/server';
const ServerSchema = new Schema<IServer, ServerModel, IServerMethods>({
client_id: String,
ip: String,
ipList: [String], // If specified, clients will be given a random IP from this list
port: Number,
service_name: String,
service_type: String,
@ -18,4 +19,17 @@ const ServerSchema = new Schema<IServer, ServerModel, IServerMethods>({
ServerSchema.plugin(uniqueValidator, { message: '{PATH} already in use.' });
ServerSchema.method('getServerConnectInfo', async function (): Promise<IServerConnectInfo> {
const ipList = (this.ipList ?? [this.ip]).filter((v): v is string => !!v);
if (ipList.length === 0) {
throw new Error(`No IP configured for server ${this._id}`);
}
const randomIp = ipList[Math.floor(Math.random() * ipList.length)];
return {
ip: randomIp,
port: this.port
};
});
export const Server = model<IServer, ServerModel>('Server', ServerSchema);

View File

@ -13,7 +13,8 @@ const serverProvisioningSchema = z.object({
servers: z.array(z.object({
id: z.string(),
name: z.string(),
ip: z.string(),
ip: z.string().optional(),
ipList: z.array(z.string()).optional(),
port: z.coerce.number()
}))
});
@ -40,6 +41,7 @@ export async function handleServerProvisioning(): Promise<void> {
$set: {
_id: id,
service_name: server.name,
ipList: server.ipList,
ip: server.ip,
port: server.port
}

View File

@ -51,7 +51,8 @@ router.post('/', async (request: express.Request, response: express.Response): P
return;
}
if (action === 'LOGIN' && server.port <= 0 && server.ip !== '0.0.0.0') {
const connectInfo = await server.getServerConnectInfo();
if (action === 'LOGIN' && connectInfo.port <= 0 && connectInfo.ip !== '0.0.0.0') {
// * Addresses of 0.0.0.0:0 are allowed
// * They are expected for titles with no NEX server
response.status(200).send(nascError('110').toString());
@ -85,8 +86,9 @@ async function processLoginRequest(server: HydratedServerDocument, pid: number,
const nexTokenBuffer = await generateToken(server.aes_key, tokenOptions);
const nexToken = nintendoBase64Encode(nexTokenBuffer || '');
const connectInfo = await server.getServerConnectInfo();
return new URLSearchParams({
locator: nintendoBase64Encode(`${server.ip}:${server.port}`),
locator: nintendoBase64Encode(`${connectInfo.ip}:${connectInfo.port}`),
retry: nintendoBase64Encode('0'),
returncd: nintendoBase64Encode('001'),
token: nexToken,

View File

@ -229,12 +229,13 @@ router.get('/nex_token/@me', async (request: express.Request, response: express.
nexToken = Buffer.from(nexToken || '', 'base64').toString('hex');
}
const connectInfo = await server.getServerConnectInfo();
response.send(xmlbuilder.create({
nex_token: {
host: server.ip,
host: connectInfo.ip,
nex_password: nexAccount.password,
pid: nexAccount.pid,
port: server.port,
port: connectInfo.port,
token: nexToken
}
}).end());

View File

@ -2,7 +2,8 @@ import type { Model, HydratedDocument } from 'mongoose';
export interface IServer {
client_id: string;
ip: string;
ip?: string;
ipList?: string[];
port: number;
service_name: string;
service_type: string;
@ -14,7 +15,14 @@ export interface IServer {
aes_key: string;
}
export interface IServerMethods {}
export interface IServerConnectInfo {
ip: string;
port: number;
}
export interface IServerMethods {
getServerConnectInfo(): Promise<IServerConnectInfo>;
}
interface IServerQueryHelpers {}