Skip to content

Commit

Permalink
refact(skymp5-server): replace Axios with fetch-retry in login.ts (#2273
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Pospelove authored Jan 10, 2025
1 parent 7ac8f8b commit 1c587ed
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 27 deletions.
2 changes: 2 additions & 0 deletions skymp5-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
"dependencies": {
"@octokit/rest": "^20.0.2",
"@types/lodash": "^4.14.202",
"@types/node": "^22.10.2",
"argparse": "^2.0.1",
"axios": "^1.7.4",
"chokidar": "^3.5.3",
"crc-32": "^1.2.2",
"discord.js": "^14.13.0",
"fetch-retry": "^6.0.0",
"koa": "^2.14.2",
"koa-body": "^4.2.0",
"koa-proxy": "^1.0.0-alpha.3",
Expand Down
86 changes: 59 additions & 27 deletions skymp5-server/ts/systems/login.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { System, Log, Content, SystemContext } from "./system";
import Axios from "axios";
import { getMyPublicIp } from "../publicIp";
import { Settings } from "../settings";
import * as fetchRetry from "fetch-retry";

const loginFailedNotLoggedViaDiscord = JSON.stringify({ customPacketType: "loginFailedNotLoggedViaDiscord" });
const loginFailedNotInTheDiscordServer = JSON.stringify({ customPacketType: "loginFailedNotInTheDiscordServer" });
Expand Down Expand Up @@ -32,21 +32,40 @@ export class Login implements System {
private offlineMode: boolean
) { }

private getFetchOptions(callerFunctionName: string) {
return {
// retry on any network error, or 5xx status codes
retryOn: (attempt: number, error: Error | null, response: Response) => {
const retry = error !== null || response.status >= 500;
if (retry) {
console.log(`${callerFunctionName}: retrying request ${JSON.stringify({ attempt, error, status: response.status })}`);
}
return retry;
},
retries: 10
};
}

private async getUserProfile(session: string, userId: number, ctx: SystemContext): Promise<UserProfile> {
try {
const response = await Axios.get(
`${this.masterUrl}/api/servers/${this.myAddr}/sessions/${session}`
);
if (!response.data || !response.data.user || !response.data.user.id) {
throw new Error(`getUserProfile: bad master-api response ${JSON.stringify(response.data)}`);
}
return response.data.user as UserProfile;
} catch (error) {
if (Axios.isAxiosError(error) && error.response?.status === 404) {
const response = await this.fetchRetry(
`${this.masterUrl}/api/servers/${this.myAddr}/sessions/${session}`,
this.getFetchOptions('getUserProfile')
);

if (!response.ok) {
if (response.status === 404) {
ctx.svr.sendCustomPacket(userId, loginFailedSessionNotFound);
}
throw error;
throw new Error(`getUserProfile: HTTP error ${response.status}`);
}

const data = await response.json();

if (!data || !data.user || !data.user.id) {
throw new Error(`getUserProfile: bad master-api response ${JSON.stringify(data)}`);
}

return data.user as UserProfile;
}

async initAsync(ctx: SystemContext): Promise<void> {
Expand Down Expand Up @@ -115,15 +134,17 @@ export class Login implements System {
throw new Error("Not logged in via Discord");
}
const guidBeforeAsyncOp = ctx.svr.getUserGuid(userId);
const response = await Axios.get(
const response = await this.fetchRetry(
`https://discord.com/api/guilds/${discordAuth.guildId}/members/${profile.discordId}`,
{
method: 'GET',
headers: {
'Authorization': `${discordAuth.botToken}`,
},
validateStatus: (status) => true,
... this.getFetchOptions('discordAuth1'),
},
);
const responseData = response.ok ? await response.json() : null;
const guidAfterAsyncOp = ctx.svr.isConnected(userId) ? ctx.svr.getUserGuid(userId) : "<disconnected>";

console.log({ guidBeforeAsyncOp, guidAfterAsyncOp, op: "Discord request" });
Expand All @@ -138,11 +159,11 @@ export class Login implements System {
// TODO: what if more characters
const actorId = ctx.svr.getActorsByProfileId(profile.id)[0];

const receivedRoles: string[] | null = (response.data && Array.isArray(response.data.roles)) ? response.data.roles : null;
const receivedRoles: string[] | null = (responseData && Array.isArray(responseData.roles)) ? responseData.roles : null;
const currentRoles: string[] | null = actorId ? mp.get(actorId, "private.discordRoles") : null;
roles = receivedRoles || currentRoles || [];

console.log('Discord request:', JSON.stringify({ status: response.status, data: response.data }));
console.log('Discord request:', JSON.stringify({ status: response.status, data: responseData }));

if (discordAuth.eventLogChannelId) {
let ipToPrint = ip;
Expand All @@ -154,21 +175,31 @@ export class Login implements System {
}

const actorIds = ctx.svr.getActorsByProfileId(profile.id).map(actorId => actorId.toString(16));
Axios.post(
`https://discord.com/api/channels/${discordAuth.eventLogChannelId}/messages`,
{
this.fetchRetry(`https://discord.com/api/channels/${discordAuth.eventLogChannelId}/messages`, {
method: 'POST',
headers: {
'Authorization': `${discordAuth.botToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
content: `Server Login: IP ${ipToPrint}, Actor ID ${actorIds}, Master API ${profile.id}, Discord ID ${profile.discordId} <@${profile.discordId}>`,
allowed_mentions: { parse: [] },
},
{
headers: {
'Authorization': `${discordAuth.botToken}`,
},
},
).catch((err) => console.error("Error sending message to Discord:", err));
}),
... this.getFetchOptions('discordAuth2'),
})
.then((response) => {
if (!response.ok) {
throw new Error(`Error sending message to Discord: ${response.statusText}`);
}
return response.json();
})
.then((_data) => null)
.catch((err) => {
console.error("Error sending message to Discord:", err);
});
}

if (response.status === 404 && response.data?.code === DiscordErrors.unknownMember) {
if (response.status === 404 && responseData?.code === DiscordErrors.unknownMember) {
ctx.svr.sendCustomPacket(userId, loginFailedNotInTheDiscordServer);
throw new Error("Not in the Discord server");
}
Expand Down Expand Up @@ -205,4 +236,5 @@ export class Login implements System {

private myAddr: string;
private settingsObject: Settings;
private fetchRetry = fetchRetry.default(global.fetch);
}
17 changes: 17 additions & 0 deletions skymp5-server/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,13 @@
dependencies:
undici-types "~5.26.4"

"@types/node@^22.10.2":
version "22.10.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.2.tgz#a485426e6d1fdafc7b0d4c7b24e2c78182ddabb9"
integrity sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==
dependencies:
undici-types "~6.20.0"

"@types/ws@8.5.9":
version "8.5.9"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.9.tgz#384c489f99c83225a53f01ebc3eddf3b8e202a8c"
Expand Down Expand Up @@ -818,6 +825,11 @@ fast-json-stable-stringify@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==

fetch-retry@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-6.0.0.tgz#4ffdf92c834d72ae819e42a4ee2a63f1e9454426"
integrity sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==

fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
Expand Down Expand Up @@ -1686,6 +1698,11 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==

undici-types@~6.20.0:
version "6.20.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==

undici@5.27.2:
version "5.27.2"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.27.2.tgz#a270c563aea5b46cc0df2550523638c95c5d4411"
Expand Down

0 comments on commit 1c587ed

Please sign in to comment.