summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgumi <git@gumi.ca>2020-03-04 21:22:46 -0500
committergumi <git@gumi.ca>2020-03-04 21:22:46 -0500
commitecb8bd66d17592346c8855bb021dae802552dabf (patch)
tree47463d662934a09da88daf4dbd55008627f2a1c1
parent349053954d45e4625ab35e6b2383608e5132eba3 (diff)
downloadapi-ecb8bd66d17592346c8855bb021dae802552dabf.tar.gz
api-ecb8bd66d17592346c8855bb021dae802552dabf.tar.bz2
api-ecb8bd66d17592346c8855bb021dae802552dabf.tar.xz
api-ecb8bd66d17592346c8855bb021dae802552dabf.zip
pre-cache the game accounts on login
-rw-r--r--src/routers/vault/middlewares/evol/account.js64
-rw-r--r--src/routers/vault/middlewares/legacy/account.js126
-rw-r--r--src/routers/vault/middlewares/session.js46
-rw-r--r--src/routers/vault/types/Char.js34
-rw-r--r--src/routers/vault/types/EvolAccount.js21
-rw-r--r--src/routers/vault/types/EvolChar.js21
-rw-r--r--src/routers/vault/types/GameAccount.js38
-rw-r--r--src/routers/vault/types/LegacyAccount.js21
-rw-r--r--src/routers/vault/types/LegacyChar.js21
-rw-r--r--src/routers/vault/types/Session.js44
-rw-r--r--src/routers/vault/utils/claim.js27
-rw-r--r--src/routers/vault/utils/game_accounts.js123
12 files changed, 397 insertions, 189 deletions
diff --git a/src/routers/vault/middlewares/evol/account.js b/src/routers/vault/middlewares/evol/account.js
index 80f741d..440367c 100644
--- a/src/routers/vault/middlewares/evol/account.js
+++ b/src/routers/vault/middlewares/evol/account.js
@@ -1,4 +1,5 @@
"use strict";
+const EvolAccount = require("../../types/EvolAccount.js");
const regexes = {
token: /^[a-zA-Z0-9-_]{6,128}$/, // UUID
@@ -7,48 +8,6 @@ const regexes = {
gid: /^[23][0-9]{6}$/, // account id
};
-const get_account_list = async (req, vault_id) => {
- const accounts = [];
- const claimed = await req.app.locals.vault.claimed_game_accounts.findAll({
- where: {vaultId: vault_id},
- });
-
- for (const acc_ of claimed) {
- const acc = await req.app.locals.evol.login.findByPk(acc_.accountId);
-
- if (acc === null || acc === undefined) {
- // unexpected: account was deleted
- console.info(`Vault.evol.account: unlinking deleted account ${acc_.accountId} {${vault_id}} [${req.ip}]`);
- await acc_.destroy(); // un-claim the account
- continue;
- }
-
- const chars = [];
- const chars_ = await req.app.locals.evol.char.findAll({
- where: {accountId: acc.accountId},
- });
-
- for (const char of chars_) {
- chars.push({
- // TODO: make this a class
- name: char.name,
- charId: char.charId,
- level: char.baseLevel,
- sex: char.sex,
- });
- }
-
- accounts.push({
- // TODO: make this a class
- name: acc.userid,
- accountId: acc.accountId,
- chars,
- });
- }
-
- return accounts;
-};
-
const get_accounts = async (req, res, next) => {
const token = String(req.get("X-VAULT-SESSION") || "");
@@ -83,21 +42,12 @@ const get_accounts = async (req, res, next) => {
return;
}
- let accounts = session.gameAccounts;
-
- if (accounts.length < 1) {
- console.info(`Vault.evol.account: fetching evol accounts {${session.vault}} [${req.ip}]`);
- accounts = await get_account_list(req, session.vault);
- session.gameAccounts = accounts;
- req.app.locals.cooldown(req, 3e3);
- } else {
- req.app.locals.cooldown(req, 1e3);
- }
-
res.status(200).json({
status: "success",
- accounts,
+ accounts: session.gameAccounts,
});
+
+ req.app.locals.cooldown(req, 1e3);
};
const new_account = async (req, res, next) => {
@@ -181,11 +131,7 @@ const new_account = async (req, res, next) => {
});
// now add it to the evol cache
- const account = {
- name: evol_acc.userid,
- accountId: evol_acc.accountId,
- chars: [],
- };
+ const account = new EvolAccount(evol_acc.accountId, evol_acc.userid);
session.gameAccounts.push(account);
req.app.locals.logger.info(`Vault.evol.account: created a new game account: ${account.accountId} {${session.vault}} [${req.ip}]`);
diff --git a/src/routers/vault/middlewares/legacy/account.js b/src/routers/vault/middlewares/legacy/account.js
index fa42ca2..bdd9a84 100644
--- a/src/routers/vault/middlewares/legacy/account.js
+++ b/src/routers/vault/middlewares/legacy/account.js
@@ -1,6 +1,10 @@
"use strict";
const md5saltcrypt = require("../../utils/md5saltcrypt.js");
const flatfile = require("../../utils/flatfile.js");
+const LegacyAccount = require("../../types/LegacyAccount.js");
+const LegacyChar = require("../../types/LegacyChar.js");
+const EvolAccount = require("../../types/EvolAccount.js");
+const EvolChar = require("../../types/EvolChar.js");
const regexes = {
token: /^[a-zA-Z0-9-_]{6,128}$/, // UUID
@@ -10,48 +14,6 @@ const regexes = {
gid: /^[23][0-9]{6}$/, // account id
};
-const get_account_list = async (req, vault_id) => {
- const accounts = [];
- const claimed = await req.app.locals.vault.claimed_legacy_accounts.findAll({
- where: {vaultId: vault_id},
- });
-
- for (const acc_ of claimed) {
- const acc = await req.app.locals.legacy.login.findByPk(acc_.accountId);
-
- if (acc === null || acc === undefined) {
- // unexpected: account was deleted
- console.info(`Vault.legacy.account: unlinking deleted account ${acc_.accountId} {${vault_id}} [${req.ip}]`);
- await acc_.destroy(); // un-claim the account
- continue;
- }
-
- const chars = [];
- const chars_ = await req.app.locals.legacy.char.findAll({
- where: {accountId: acc.accountId},
- });
-
- for (const char of chars_) {
- chars.push({
- name: char.name,
- charId: char.charId,
- revoltId: char.revoltId,
- level: char.baseLevel,
- sex: char.sex,
- });
- }
-
- accounts.push({
- name: acc.userid,
- accountId: acc.accountId,
- revoltId: acc.revoltId,
- chars,
- });
- }
-
- return accounts;
-};
-
const get_accounts = async (req, res, next) => {
const token = String(req.get("X-VAULT-SESSION") || "");
@@ -86,21 +48,12 @@ const get_accounts = async (req, res, next) => {
return;
}
- let accounts = session.legacyAccounts;
-
- if (accounts.length < 1) {
- console.info(`Vault.legacy.account: fetching legacy accounts {${session.vault}} [${req.ip}]`);
- accounts = await get_account_list(req, session.vault);
- session.legacyAccounts = accounts;
- req.app.locals.cooldown(req, 3e3);
- } else {
- req.app.locals.cooldown(req, 1e3);
- }
-
res.status(200).json({
status: "success",
- accounts,
+ accounts: session.legacyAccounts,
});
+
+ req.app.locals.cooldown(req, 1e3);
};
const claim_by_password = async (req, res, next) => {
@@ -228,28 +181,22 @@ const claim_by_password = async (req, res, next) => {
});
// now we must update the session cache:
- const chars = [];
- const chars_ = await req.app.locals.legacy.char.findAll({
+ const chars = await req.app.locals.legacy.char.findAll({
where: {accountId: legacy.accountId},
});
- for (const char of chars_) {
- chars.push({
- // TODO: make this a class
- name: char.name,
- charId: char.charId,
- revoltId: char.revoltId,
- level: char.baseLevel,
- sex: char.sex,
- });
+ const account = new LegacyAccount(legacy.accountId, legacy.userid);
+ account.revoltId = legacy.revoltId;
+
+ for (const char_ of chars) {
+ const char = new LegacyChar(account, char_.charId, char_.name);
+ char.revoltId = char_.revoltId;
+ char.baseLevel = char_.baseLevel;
+ char.gender = char_.sex;
+
+ account.chars.push(char);
}
- const account = {
- name: legacy.userid,
- accountId: legacy.accountId,
- revoltId: legacy.revoltId,
- chars,
- };
session.legacyAccounts.push(account);
res.status(200).json({
@@ -374,14 +321,14 @@ const migrate = async (req, res, next) => {
vaultId: session.vault,
});
- // now add it to the evol cache
- const cache_key = session.gameAccounts.push({
- name: evol_acc.userid,
- accountId: evol_acc.accountId,
- chars: [],
- }) - 1;
+ const evol_account = new EvolAccount(evol_acc.accountId, evol_acc.userid);
+ evol_account.legacyId = legacy.accountId;
+ evol_account.legacyAccount = legacy;
+
+ // update legacy account cache
+ legacy.revoltId = evol_acc.accountId;
+ legacy.revoltAccount = evol_acc;
- legacy.revoltId = evol_acc.accountId; // update legacy cache
await req.app.locals.legacy.login.update({ // update sql
revoltId: evol_acc.accountId,
}, {where: {
@@ -391,6 +338,7 @@ const migrate = async (req, res, next) => {
// XXX: ideally we should be using createBulk but we also want to update
for (const [num, char] of legacy.chars.entries()) {
if (char.revoltId) {
+ // already migrated
continue;
}
@@ -416,14 +364,17 @@ const migrate = async (req, res, next) => {
});
// update the evol cache
- session.gameAccounts[cache_key].chars.push({
- name: evol_char.name,
- charId: evol_char.charId,
- level: 1,
- sex: evol_char.sex,
- });
+ const evol_char_ = new EvolChar(evol_account, evol_char.charId, evol_char.name);
+ evol_char_.legacyChar = char;
+ evol_char_.legacyId = char.charId;
+ evol_char_.gender = evol_char.sex;
+
+ evol_account.chars.push(evol_char_);
+
+ // update legacy cache
+ char.revoltId = evol_char.charId;
+ char.revoltAccount = evol_account;
- char.revoltId = evol_char.charId; // update legacy cache
await req.app.locals.legacy.char.update({ // update sql
revoltId: evol_char.charId,
}, {where: {
@@ -431,11 +382,14 @@ const migrate = async (req, res, next) => {
}});
}
+ session.gameAccounts.push(evol_account);
+
// TODO: try/catch each of the await operations
res.status(200).json({
status: "success",
- account: session.gameAccounts[cache_key],
+ session,
+ account: evol_account,
});
req.app.locals.logger.info(`Vault.legacy.account: migrated Legacy account ${legacy.accountId} {${session.vault}} [${req.ip}]`);
diff --git a/src/routers/vault/middlewares/session.js b/src/routers/vault/middlewares/session.js
index 0073e90..990de49 100644
--- a/src/routers/vault/middlewares/session.js
+++ b/src/routers/vault/middlewares/session.js
@@ -3,6 +3,7 @@ const uuidv4 = require("uuid/v4");
const nodemailer = require("nodemailer");
const Claim = require("../utils/claim.js");
const Session = require("../types/Session.js");
+const game_accounts = require("../utils/game_accounts.js");
let transporter = nodemailer.createTransport({
sendmail: true,
@@ -82,10 +83,7 @@ const auth_session = async (req, res, next) => {
// already authed, tell client
res.status(200).json({
status: "success",
- session: {
- expires: session.expires,
- identity: session.identity,
- }
+ session,
});
req.app.locals.cooldown(req, 500);
return;
@@ -154,6 +152,10 @@ const auth_session = async (req, res, next) => {
req.app.locals.cooldown(req, 6e4);
+ // pre-cache the accounts and chars in the session cache
+ await game_accounts.get_legacy(req, session);
+ await game_accounts.get_evol(req, session);
+
// authenticate this session
session.authenticated = true;
@@ -165,26 +167,32 @@ const auth_session = async (req, res, next) => {
if (session.identity !== session.primaryIdentity) {
// user did not log in with their primary identity
- // TODO: allow to block logging in with non-primary identities
const primary = await req.app.locals.vault.identity.findByPk(session.primaryIdentity);
- transporter.sendMail({
- from: process.env.VAULT__MAILER__FROM,
- to: primary.email,
- subject: "The Mana World security notice",
- text: "Someone has logged in to your Vault account using an email address that " +
- "is not your primary address. If this wasn't you, please contact us immediately.\n\n" +
- "To stop receiving login notices, use your primary email address when logging in."
- }, (err, info) => {});
- }
- // TODO: already cache the identities and accounts in the session
+ if (primary === null || primary === undefined) {
+ // the vault account has no primary identity (bug): let's fix this
+ console.warn(`Vault.session: fixing account with a deleted primary identity {${session.vault}} [${req.ip}]`);
+ await req.app.locals.vault.login.update({
+ primaryIdentity: session.identity,
+ }, {where: {
+ id: session.vault,
+ }});
+ session.primaryIdentity = session.identity;
+ } else {
+ transporter.sendMail({
+ from: process.env.VAULT__MAILER__FROM,
+ to: primary.email,
+ subject: "The Mana World security notice",
+ text: "Someone has logged in to your Vault account using an email address that " +
+ "is not your primary address. If this wasn't you, please contact us immediately.\n\n" +
+ "To stop receiving login notices, use your primary email address when logging in."
+ }, (err, info) => {});
+ }
+ }
res.status(200).json({
status: "success",
- session: {
- expires: session.expires,
- identity: session.identity,
- }
+ session,
});
};
diff --git a/src/routers/vault/types/Char.js b/src/routers/vault/types/Char.js
new file mode 100644
index 0000000..a90b950
--- /dev/null
+++ b/src/routers/vault/types/Char.js
@@ -0,0 +1,34 @@
+/**
+ * represents a generic game character
+ */
+module.exports = class Char {
+ /** reference to the parent GameAccount */
+ account = null;
+ /** the ID of this char */
+ charId = 0;
+ /** the public name */
+ name = "";
+ /** the level of the char */
+ baseLevel = 1;
+ /** gender of the char */
+ gender = "N";
+
+ constructor (acc, id, name) {
+ this.account = acc;
+ this.charId = id;
+ this.name = name;
+ }
+
+ /**
+ * serialize for sending over the network
+ * @param {*} key
+ */
+ toJSON (key) {
+ return {
+ charId: this.charId,
+ name: this.name,
+ level: this.baseLevel,
+ sex: this.gender,
+ };
+ }
+}
diff --git a/src/routers/vault/types/EvolAccount.js b/src/routers/vault/types/EvolAccount.js
new file mode 100644
index 0000000..6db03bf
--- /dev/null
+++ b/src/routers/vault/types/EvolAccount.js
@@ -0,0 +1,21 @@
+const GameAccount = require("./GameAccount.js");
+
+/**
+ * represents an Evol game account
+ */
+module.exports = class EvolAccount extends GameAccount {
+ /** account id of the source legacy account (ported) */
+ legacyId = null;
+ /** reference to the LegacyAccount */
+ legacyAccount = null;
+
+ /**
+ * serialize for sending over the network
+ * @param {*} key
+ */
+ toJSON (key) {
+ return Object.assign({
+ legacyId: this.legacyId,
+ }, super.toJSON());
+ }
+}
diff --git a/src/routers/vault/types/EvolChar.js b/src/routers/vault/types/EvolChar.js
new file mode 100644
index 0000000..1107d62
--- /dev/null
+++ b/src/routers/vault/types/EvolChar.js
@@ -0,0 +1,21 @@
+const Char = require("./Char.js");
+
+/**
+ * represents an Evol game char
+ */
+module.exports = class EvolChar extends Char {
+ /** char id of the source legacy char (ported) */
+ legacyId = null;
+ /** reference to the LegacyChar */
+ legacyChar = null;
+
+ /**
+ * serialize for sending over the network
+ * @param {*} key
+ */
+ toJSON (key) {
+ return Object.assign({
+ legacyId: this.legacyId,
+ }, super.toJSON());
+ }
+}
diff --git a/src/routers/vault/types/GameAccount.js b/src/routers/vault/types/GameAccount.js
new file mode 100644
index 0000000..fa94808
--- /dev/null
+++ b/src/routers/vault/types/GameAccount.js
@@ -0,0 +1,38 @@
+/**
+ * represents a generic game account
+ */
+module.exports = class GameAccount {
+ /** the GID of the account */
+ accountId = 0;
+ /** the login username */
+ userid = "";
+ /** the email address associated with the account */
+ email = null;
+ /** Char[] */
+ chars = [];
+ /** the last time the account logged in */
+ lastLogin = null;
+ /** the last IP that was used to log in */
+ lastIP = null;
+ /** the total number of times the account logged in */
+ loginCount = 0;
+ /** whether the account is banned */
+ banned = false;
+
+ constructor (id, name) {
+ this.accountId = id;
+ this.userid = name;
+ }
+
+ /**
+ * serialize for sending over the network
+ * @param {*} key
+ */
+ toJSON (key) {
+ return {
+ accountId: this.accountId,
+ name: this.userid,
+ chars: this.chars,
+ };
+ }
+}
diff --git a/src/routers/vault/types/LegacyAccount.js b/src/routers/vault/types/LegacyAccount.js
new file mode 100644
index 0000000..747e6df
--- /dev/null
+++ b/src/routers/vault/types/LegacyAccount.js
@@ -0,0 +1,21 @@
+const GameAccount = require("./GameAccount.js");
+
+/**
+ * represents a Legacy game account
+ */
+module.exports = class LegacyAccount extends GameAccount {
+ /** account id of the target evol account (ported) */
+ revoltId = null;
+ /** reference to the EvolAccount of the target evol account */
+ revoltAccount = null;
+
+ /**
+ * serialize for sending over the network
+ * @param {*} key
+ */
+ toJSON (key) {
+ return Object.assign({
+ revoltId: this.revoltId,
+ }, super.toJSON());
+ }
+}
diff --git a/src/routers/vault/types/LegacyChar.js b/src/routers/vault/types/LegacyChar.js
new file mode 100644
index 0000000..b893c3f
--- /dev/null
+++ b/src/routers/vault/types/LegacyChar.js
@@ -0,0 +1,21 @@
+const Char = require("./Char.js");
+
+/**
+ * represents a Legacy game char
+ */
+module.exports = class LegacyChar extends Char {
+ /** char id of the target evol char (ported) */
+ revoltId = null;
+ /** reference to the EvolChar */
+ revoltChar = null;
+
+ /**
+ * serialize for sending over the network
+ * @param {*} key
+ */
+ toJSON (key) {
+ return Object.assign({
+ revoltId: this.revoltId,
+ }, super.toJSON());
+ }
+}
diff --git a/src/routers/vault/types/Session.js b/src/routers/vault/types/Session.js
index 34bd250..1809cac 100644
--- a/src/routers/vault/types/Session.js
+++ b/src/routers/vault/types/Session.js
@@ -2,20 +2,42 @@
* holds a cache of all the user data fetched from SQL
*/
module.exports = class Session {
- expires = new Date(); // expiry Date
- vault = null; // Vault account id
- authenticated = false; // whether the user logged in
- identity = null; // the identity that was used to log in
- email; // the email address of the identity that was used to log in
- identities = []; // cache holding all identities
- primaryIdentity = null; // the main identity of the account
- allowNonPrimary = true; // whether to allow logging in with a non-primary ident
- legacyAccounts = []; // cache holding all legacy game accounts
- gameAccounts = []; // cache holding all evol game accounts
- ip; // ip that was used to init the session
+ /** expiry Date */
+ expires = new Date();
+ /** Vault account id */
+ vault = null;
+ /** whether the user logged in */
+ authenticated = false;
+ /** the identity that was used to log in */
+ identity = null;
+ /** the email address of the identity that was used to log in */
+ email;
+ /** cache holding all identities */
+ identities = [];
+ /** the main identity of the account */
+ primaryIdentity = null;
+ /** whether to allow logging in with a non-primary ident */
+ allowNonPrimary = true;
+ /** LegacyAccount[] cache holding all legacy game accounts */
+ legacyAccounts = [];
+ /** EvolAccount[] cache holding all evol game accounts */
+ gameAccounts = [];
+ /** ip that was used to init the session */
+ ip;
constructor (ip, email) {
this.ip = ip;
this.email = email;
}
+
+ /**
+ * serialize for sending over the network
+ * @param {*} key
+ */
+ toJSON (key) {
+ return {
+ expires: this.expires,
+ identity: this.identity,
+ }
+ }
}
diff --git a/src/routers/vault/utils/claim.js b/src/routers/vault/utils/claim.js
index b3dbe9d..d28e076 100644
--- a/src/routers/vault/utils/claim.js
+++ b/src/routers/vault/utils/claim.js
@@ -1,4 +1,6 @@
const { Op } = require("sequelize");
+const LegacyAccount = require("../types/LegacyAccount.js");
+const LegacyChar = require("../types/LegacyChar.js");
// claim by email // TODO: DRY this
const claim_accounts = async (req, email, vault_id, session = null) => {
@@ -49,27 +51,24 @@ const claim_accounts = async (req, email, vault_id, session = null) => {
});
if (session !== null) {
- const chars = [];
const chars_ = await locals.legacy.char.findAll({
where: {accountId: acc.accountId},
});
+ const legacy_account = new LegacyAccount(acc.accountId, acc.userid);
+ legacy_account.revoltId = acc.revoltId;
+
for (const char of chars_) {
- chars.push({
- name: char.name,
- charId: char.charId,
- revoltId: char.revoltId,
- level: char.baseLevel,
- sex: char.sex,
- });
+ const legacy_char = new LegacyChar(legacy_account, char.charId, char.name);
+ legacy_char.revoltId = char.revoltId;
+ legacy_char.baseLevel = char.baseLevel;
+ legacy_char.gender = char.sex;
+
+ legacy_account.chars.push(legacy_char);
}
+
// add to session cache
- session.legacyAccounts.push({
- name: acc.userid,
- accountId: acc.accountId,
- revoltId: acc.revoltId,
- chars,
- });
+ session.legacyAccounts.push(legacy_account);
}
locals.logger.info(`Vault.legacy.account: linked Legacy account ${acc.accountId} to Vault account {${vault_id}} [${req.ip}]`);
diff --git a/src/routers/vault/utils/game_accounts.js b/src/routers/vault/utils/game_accounts.js
new file mode 100644
index 0000000..c19feb5
--- /dev/null
+++ b/src/routers/vault/utils/game_accounts.js
@@ -0,0 +1,123 @@
+const LegacyAccount = require("../types/LegacyAccount.js");
+const LegacyChar = require("../types/LegacyChar.js");
+const EvolAccount = require("../types/EvolAccount.js");
+const EvolChar = require("../types/EvolChar.js");
+
+/**
+ * fetch the legacy game accounts and cache in the Session
+ * @param {*} req - the express request
+ * @param {Session} session - the Session
+ * @return {Promise<LegacyAccount[]>} a promise resolving to an array of LegacyAccount
+ */
+const get_legacy_accounts = async (req, session) => {
+ const accounts = [];
+ const claimed = await req.app.locals.vault.claimed_legacy_accounts.findAll({
+ where: {vaultId: session.vault},
+ });
+
+ for (const acc_ of claimed) {
+ const acc = await req.app.locals.legacy.login.findByPk(acc_.accountId);
+
+ if (acc === null || acc === undefined) {
+ // unexpected: account was deleted
+ console.info(`Vault.legacy.account: unlinking deleted account ${acc_.accountId} {${session.vault}} [${req.ip}]`);
+ await acc_.destroy(); // un-claim the account
+ continue;
+ }
+
+ const account = new LegacyAccount(acc.accountId, acc.userid);
+ account.revoltId = acc.revoltId;
+
+ const chars = await req.app.locals.legacy.char.findAll({
+ where: {accountId: acc.accountId},
+ });
+
+ for (const char of chars) {
+ const char_ = new LegacyChar(account, char.charId, char.name);
+ char_.baseLevel = char.baseLevel;
+ char_.gender = char.sex;
+ char_.revoltId = char.revoltId;
+
+ account.chars.push(char_);
+ }
+
+ accounts.push(account);
+ }
+
+ session.legacyAccounts = accounts;
+ return accounts;
+};
+
+/**
+ * fetch the evol game accounts and cache in the Session
+ * @param {*} req - the express request
+ * @param {Session} session - the Session
+ * @return {Promise<EvolAccount[]>} a promise resolving to an array of EvolAccount
+ */
+const get_account_list = async (req, session) => {
+ const accounts = [];
+ const claimed = await req.app.locals.vault.claimed_game_accounts.findAll({
+ where: {vaultId: session.vault},
+ });
+
+ for (const acc_ of claimed) {
+ const acc = await req.app.locals.evol.login.findByPk(acc_.accountId);
+
+ if (acc === null || acc === undefined) {
+ // unexpected: account was deleted
+ console.info(`Vault.evol.account: unlinking deleted account ${acc_.accountId} {${session.vault}} [${req.ip}]`);
+ await acc_.destroy(); // un-claim the account
+ continue;
+ }
+
+ const account = new EvolAccount(acc.accountId, acc.userid);
+
+ // check if this is an imported account
+ for (const legacy_acc of session.legacyAccounts) {
+ if (legacy_acc.revoltId === account.accountId) {
+ account.legacyId = legacy_acc.accountId;
+
+ // two-way binding
+ account.legacyAccount = legacy_acc;
+ legacy_acc.revoltAccount = account;
+ break;
+ }
+ }
+
+ const chars = await req.app.locals.evol.char.findAll({
+ where: {accountId: acc.accountId},
+ });
+
+ for (const char of chars) {
+ const char_ = new EvolChar(account, char.charId, char.name);
+ char_.baseLevel = char.baseLevel;
+ char_.gender = char.sex;
+
+ // check if this is an imported char
+ for (const legacy_acc of session.legacyAccounts) {
+ for (const legacy_char of legacy_acc.chars) {
+ if (legacy_char.revoltId === char_.charId) {
+ char_.legacyId = legacy_char.charId;
+
+ // two-way binding
+ char_.legacyChar = legacy_char;
+ legacy_char.revoltChar = char_;
+ break;
+ }
+ }
+ }
+
+ account.chars.push(char_);
+ }
+
+ accounts.push(account);
+ }
+
+ session.gameAccounts = accounts;
+ return accounts;
+};
+
+module.exports = {
+ get_evol: get_account_list,
+ get_legacy: get_legacy_accounts,
+};