mirror of
https://github.com/JuniorDark/RustyHearts-API.git
synced 2026-05-07 14:11:44 -04:00
Version 1.3.0
- Implemented authentication and billing routes for Jpn region. - Refactored and changed the project structure from CommonJS to ES Modules
This commit is contained in:
parent
9584e58143
commit
c3d9e7afb5
76 changed files with 3847 additions and 1109 deletions
334
src/services/accountDBService.js
Normal file
334
src/services/accountDBService.js
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
import sql from "mssql";
|
||||
import {
|
||||
generateMD5Hash,
|
||||
comparePassword,
|
||||
hashPassword,
|
||||
} from "../utils/hashUtils.js";
|
||||
import { connAccount } from "../utils/dbConfig.js";
|
||||
import configs from "../config.js";
|
||||
const { config } = configs;
|
||||
import {
|
||||
sendConfirmationEmail,
|
||||
sendVerificationEmail,
|
||||
sendPasswordResetEmail,
|
||||
sendPasswordChangedEmail,
|
||||
} from "../mailer/mailer.js";
|
||||
import { logger } from "../utils/logger.js";
|
||||
|
||||
// ==============================================
|
||||
// Account Management Functions
|
||||
// ==============================================
|
||||
|
||||
export async function getAccount(identifier) {
|
||||
const pool = connAccount;
|
||||
const result = await pool
|
||||
.request()
|
||||
.input("Identifier", sql.VarChar(50), identifier)
|
||||
.execute("GetAccount");
|
||||
|
||||
const row = result.recordset[0];
|
||||
if (!row || row.Result !== "AccountExists") {
|
||||
throw new Error(row?.Result || "AccountNotFound");
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
export async function createAccount(account, email, password, ip, verificationCode) {
|
||||
const isValidVerificationCode = await verifyCode(
|
||||
email,
|
||||
verificationCode,
|
||||
"Account"
|
||||
);
|
||||
if (isValidVerificationCode !== "ValidVerificationCode") {
|
||||
return isValidVerificationCode;
|
||||
}
|
||||
|
||||
const md5Password = generateMD5Hash(account, password);
|
||||
const passwordHash = await hashPassword(md5Password, 10);
|
||||
|
||||
const pool = connAccount;
|
||||
const request = pool.request();
|
||||
request.input("WindyCode", sql.VarChar, account);
|
||||
request.input("AccountPwd", sql.VarChar, passwordHash);
|
||||
request.input("Email", sql.VarChar, email);
|
||||
request.input("RegisterIP", sql.VarChar, ip);
|
||||
request.input("ServerId", sql.Int, config.serverId);
|
||||
request.input("ShopBalance", sql.BigInt, config.shopBalance);
|
||||
const result = await request.execute("CreateAccount");
|
||||
const row = result.recordset[0];
|
||||
|
||||
if (row.Result == "AccountCreated") {
|
||||
sendConfirmationEmail(email, account);
|
||||
await clearVerificationCode(email);
|
||||
}
|
||||
|
||||
return row.Result;
|
||||
}
|
||||
|
||||
export async function validateCredentials(account, password) {
|
||||
try {
|
||||
const accountStatus = await getAccount(account);
|
||||
const passwordHash = generateMD5Hash(account, password);
|
||||
const passwordMatch = await comparePassword(
|
||||
passwordHash,
|
||||
accountStatus.AccountPwd
|
||||
);
|
||||
return accountStatus.Result == "AccountExists" && passwordMatch;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// Authentication Functions
|
||||
// ==============================================
|
||||
|
||||
export async function authenticateUser(account, password, ip, isMd5 = false) {
|
||||
const pool = connAccount;
|
||||
|
||||
// Get account info
|
||||
const { recordset } = await pool
|
||||
.request()
|
||||
.input("Identifier", sql.VarChar(50), account)
|
||||
.execute("GetAccount");
|
||||
|
||||
const row = recordset[0];
|
||||
if (!row || row.Result !== "AccountExists") {
|
||||
return { status: row.Result };
|
||||
}
|
||||
|
||||
// Verify password
|
||||
const passwordHash = isMd5 ? password : generateMD5Hash(account, password);
|
||||
const passwordMatch = await comparePassword(passwordHash, row.AccountPwd);
|
||||
|
||||
// Authenticate
|
||||
const { recordset: authRecordset } = await pool
|
||||
.request()
|
||||
.input("Identifier", sql.VarChar(50), account)
|
||||
.input("password_verify_result", sql.Bit, passwordMatch ? 1 : 0)
|
||||
.input("LastLoginIP", sql.VarChar(50), ip)
|
||||
.execute("AuthenticateUser");
|
||||
|
||||
const authRow = authRecordset[0];
|
||||
if (!authRow || authRow.Result !== "LoginSuccess") {
|
||||
return { status: authRow.Result };
|
||||
}
|
||||
|
||||
return {
|
||||
status: authRow.Result,
|
||||
authId: authRow.AuthID,
|
||||
token: authRow.Token,
|
||||
};
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// Password Management Functions
|
||||
// ==============================================
|
||||
|
||||
export async function changeAccountPassword(email, password, verificationCode) {
|
||||
|
||||
// Get account information
|
||||
const accountInfo = await getAccount(email);
|
||||
if (!accountInfo || accountInfo.Result !== "AccountExists") {
|
||||
return accountInfo;
|
||||
}
|
||||
|
||||
// Check verification code
|
||||
const verificationResult = await verifyCode(
|
||||
email,
|
||||
verificationCode,
|
||||
"Password"
|
||||
);
|
||||
|
||||
if (!verificationResult || verificationResult !== "ValidVerificationCode") {
|
||||
return verificationResult;
|
||||
}
|
||||
|
||||
const accountName = accountInfo.WindyCode;
|
||||
const currentHash = accountInfo.AccountPwd;
|
||||
const passwordHash = generateMD5Hash(accountName, password);
|
||||
|
||||
// Verify if password is the same
|
||||
const isSamePassword = await comparePassword(passwordHash, currentHash);
|
||||
if (isSamePassword) {
|
||||
return "SamePassword";
|
||||
}
|
||||
|
||||
// Hash and update password
|
||||
const newPasswordHash = await hashPassword(passwordHash);
|
||||
const updateResult = await updatePassword(email, newPasswordHash);
|
||||
|
||||
if (!updateResult || updateResult.Result !== "PasswordChanged") {
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
// Clear verification code and send email
|
||||
await clearVerificationCode(email);
|
||||
sendPasswordChangedEmail(email, accountName);
|
||||
|
||||
return updateResult.Result;
|
||||
}
|
||||
|
||||
export async function updatePassword(email, newPasswordHash) {
|
||||
const pool = connAccount;
|
||||
const result = await pool
|
||||
.request()
|
||||
.input("Email", sql.VarChar, email)
|
||||
.input("AccountPwd", sql.VarChar, newPasswordHash)
|
||||
.execute("UpdateAccountPassword");
|
||||
|
||||
return result.recordset[0];
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// Verification Code Functions
|
||||
// ==============================================
|
||||
|
||||
export function generateVerificationCode() {
|
||||
const verificationCode = Math.floor(10000 + Math.random() * 90000).toString();
|
||||
|
||||
// Set the expiration time 10 minutes from now in the specified timezone
|
||||
const expirationTime = new Date(Date.now() + 600000).toLocaleString(
|
||||
"en-US",
|
||||
config.timeZone
|
||||
);
|
||||
|
||||
return {
|
||||
code: verificationCode,
|
||||
expiration: expirationTime,
|
||||
};
|
||||
}
|
||||
|
||||
export async function sendAccountVerificationEmail(email) {
|
||||
const verificationCode = generateVerificationCode();
|
||||
|
||||
const insertRow = await setAccountVerificationCode(
|
||||
email,
|
||||
verificationCode.code,
|
||||
verificationCode.expiration
|
||||
);
|
||||
|
||||
if (insertRow.Result == "Success") {
|
||||
// Send verification code email
|
||||
sendVerificationEmail(email, verificationCode.code);
|
||||
return "EmailSent";
|
||||
} else {
|
||||
return insertRow.Result;
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendPasswordVerificationEmail(email) {
|
||||
const verificationCode = generateVerificationCode();
|
||||
|
||||
const insertRow = await setPasswordVerificationCode(
|
||||
email,
|
||||
verificationCode.code,
|
||||
verificationCode.expiration
|
||||
);
|
||||
|
||||
if (insertRow.Result == "Success") {
|
||||
// Send verification code email
|
||||
sendPasswordResetEmail(email, verificationCode.code);
|
||||
return "EmailSent";
|
||||
} else {
|
||||
return insertRow.Result;
|
||||
}
|
||||
}
|
||||
|
||||
export async function setAccountVerificationCode(
|
||||
email,
|
||||
verificationCode,
|
||||
expirationTime
|
||||
) {
|
||||
const pool = connAccount;
|
||||
const result = await pool
|
||||
.request()
|
||||
.input("Email", sql.VarChar, email)
|
||||
.input("VerificationCode", sql.VarChar, verificationCode)
|
||||
.input("ExpirationTime", sql.DateTime, expirationTime)
|
||||
.execute("SetAccountVerificationCode");
|
||||
const row = result.recordset[0];
|
||||
return row;
|
||||
}
|
||||
|
||||
export async function setPasswordVerificationCode(
|
||||
email,
|
||||
verificationCode,
|
||||
expirationTime
|
||||
) {
|
||||
const pool = connAccount;
|
||||
const result = await pool
|
||||
.request()
|
||||
.input("Email", sql.VarChar, email)
|
||||
.input("VerificationCode", sql.VarChar, verificationCode)
|
||||
.input("ExpirationTime", sql.DateTime, expirationTime)
|
||||
.execute("SetPasswordVerificationCode");
|
||||
return result.recordset[0];
|
||||
}
|
||||
|
||||
export async function verifyCode(email, verificationCode, verificationCodeType) {
|
||||
const pool = connAccount;
|
||||
const result = await pool
|
||||
.request()
|
||||
.input("Email", sql.VarChar, email)
|
||||
.input("VerificationCode", sql.VarChar, verificationCode)
|
||||
.input("VerificationCodeType", sql.VarChar, verificationCodeType)
|
||||
.execute("GetVerificationCode");
|
||||
const row = result.recordset[0];
|
||||
|
||||
return row.Result;
|
||||
}
|
||||
|
||||
export async function clearVerificationCode(email) {
|
||||
const pool = connAccount;
|
||||
await pool
|
||||
.request()
|
||||
.input("Email", sql.VarChar, email)
|
||||
.execute("ClearVerificationCode");
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
// Billing Functions
|
||||
// ==============================================
|
||||
|
||||
export async function getCurrency(userId) {
|
||||
const pool = connAccount;
|
||||
const result = await pool
|
||||
.request()
|
||||
.input("UserId", sql.VarChar(50), userId)
|
||||
.input("ServerId", sql.Int, config.serverId)
|
||||
.execute("GetCurrency");
|
||||
|
||||
const row = result.recordset[0];
|
||||
if (!row || row.Result !== "Success") {
|
||||
throw new Error(row?.Result || "Failed to get balance");
|
||||
}
|
||||
return row.Zen;
|
||||
}
|
||||
|
||||
export async function setCurrency(userId, newBalance) {
|
||||
const pool = connAccount;
|
||||
const result = await pool
|
||||
.request()
|
||||
.input("UserId", sql.VarChar(50), userId)
|
||||
.input("ServerId", sql.Int, config.serverId)
|
||||
.input("NewBalance", sql.BigInt, newBalance)
|
||||
.execute("SetCurrency");
|
||||
|
||||
if (result.rowsAffected[0] === 0) {
|
||||
throw new Error("Balance update failed");
|
||||
}
|
||||
}
|
||||
|
||||
export async function logBillingTransaction(transaction) {
|
||||
const pool = connAccount;
|
||||
await pool
|
||||
.request()
|
||||
.input("userid", sql.VarChar(50), transaction.userId)
|
||||
.input("charid", sql.VarChar(50), transaction.charId)
|
||||
.input("uniqueid", sql.VarChar(50), transaction.uniqueId)
|
||||
.input("amount", sql.BigInt, transaction.amount)
|
||||
.input("itemid", sql.VarChar(50), transaction.itemId)
|
||||
.input("itemcount", sql.Int, transaction.count)
|
||||
.execute("SetBillingLog");
|
||||
}
|
||||
20
src/services/authDBService.js
Normal file
20
src/services/authDBService.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import sql from "mssql";
|
||||
import configs from "../config.js";
|
||||
const { config } = configs;
|
||||
import { logger } from "../utils/logger.js";
|
||||
|
||||
// ==============================================
|
||||
// Fetch online count from the database
|
||||
// ==============================================
|
||||
export async function fetchOnlineCount(pool) {
|
||||
try {
|
||||
const QUERY = 'SELECT COUNT(*) AS OnlineCount FROM AuthTable WHERE online = @online';
|
||||
const request = pool.request();
|
||||
request.input('online', sql.Int, 1);
|
||||
const result = await request.query(QUERY);
|
||||
return result.recordset[0].OnlineCount;
|
||||
} catch (error) {
|
||||
logger.error('Online count query failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue