From d6c95cea4a85bd8ceb66e77664916897c5f0857a Mon Sep 17 00:00:00 2001 From: wancat Date: Mon, 19 Feb 2024 17:15:52 +0800 Subject: [PATCH] refactor utils --- .eslintrc.js | 5 ++ package.json | 2 +- server/package.json | 5 ++ .../auth/{oauthVerify.js => oauthVerify.ts} | 21 ++++++- server/src/{config.js => config.ts} | 15 ++--- .../models/{userManager.js => userManager.ts} | 7 ++- server/src/utils/{auth.js => auth.ts} | 8 ++- server/src/utils/env.js | 13 ---- server/src/utils/env.ts | 10 ++++ server/src/utils/{logger.js => logger.ts} | 0 server/src/utils/{quest.js => quest.ts} | 12 +++- .../src/utils/{setupTest.js => setupTest.ts} | 0 yarn.lock | 59 +++++++++++++++++++ 13 files changed, 126 insertions(+), 31 deletions(-) rename server/src/auth/{oauthVerify.js => oauthVerify.ts} (57%) rename server/src/{config.js => config.ts} (87%) rename server/src/models/{userManager.js => userManager.ts} (88%) rename server/src/utils/{auth.js => auth.ts} (69%) delete mode 100644 server/src/utils/env.js create mode 100644 server/src/utils/env.ts rename server/src/utils/{logger.js => logger.ts} (100%) rename server/src/utils/{quest.js => quest.ts} (86%) rename server/src/utils/{setupTest.js => setupTest.ts} (100%) diff --git a/.eslintrc.js b/.eslintrc.js index afc6b85..ebd79cc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -60,6 +60,11 @@ module.exports = { tsx: 'never', }, ], + 'lines-between-class-members': [ + 'error', + 'always', + { exceptAfterSingleLine: true }, + ], }, overrides: [ { diff --git a/package.json b/package.json index 0631702..b028471 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "app": "yarn workspace app dev", "app:local": "yarn workspace app dev:local", "build": "yarn workspaces foreach -Apt run build", - "dev": "yarn workspaces foreach -Rpi --from '{server,app}' run watch", + "dev": "yarn workspaces foreach -Api --include '{server,app}' run watch", "server": "yarn workspace server dev", "cli": "yarn workspace client start", "lint": "eslint .", diff --git a/server/package.json b/server/package.json index 584ac14..16ea7dc 100644 --- a/server/package.json +++ b/server/package.json @@ -45,8 +45,13 @@ "yaml": "^2.3.1" }, "devDependencies": { + "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.21", "@types/express-session": "^1.17.10", + "@types/jsonwebtoken": "^9.0.5", + "@types/passport": "^1.0.16", + "@types/passport-oauth2": "^1.4.15", + "@types/validator": "^13.11.9", "concurrently": "^8.2.2", "jest": "^29.6.1", "nodemon": "^3.0.1", diff --git a/server/src/auth/oauthVerify.js b/server/src/auth/oauthVerify.ts similarity index 57% rename from server/src/auth/oauthVerify.js rename to server/src/auth/oauthVerify.ts index ea4aedd..927f0b3 100644 --- a/server/src/auth/oauthVerify.js +++ b/server/src/auth/oauthVerify.ts @@ -1,9 +1,22 @@ import { User } from '@linux-odyssey/models' +import type { Profile } from 'passport' +import type { VerifyCallback } from 'passport-oauth2' -export default async function oauthVerify(provider, profile, query, cb) { +export type SocialLogin = { + provider: string + id: string + displayName: string +} + +export default async function oauthVerify( + provider: string, + profile: Profile, + query: Record, + cb: VerifyCallback +) { process.nextTick(async () => { try { - const email = profile.emails[0]?.value + const email = profile.emails ? profile.emails[0]?.value : undefined if (!email) { return cb(new Error('No email found')) } @@ -26,7 +39,9 @@ export default async function oauthVerify(provider, profile, query, cb) { return cb(null, user) } catch (err) { - return cb(err) + return cb( + err instanceof Error ? err : new Error('An unknown error occurred') + ) } }) } diff --git a/server/src/config.js b/server/src/config.ts similarity index 87% rename from server/src/config.js rename to server/src/config.ts index c45f00d..adc0863 100644 --- a/server/src/config.js +++ b/server/src/config.ts @@ -5,17 +5,18 @@ import { get } from './utils/env.js' // Import dotenv and load ../.env dotenv.config({ path: '../.env' }) -function getTrustProxies(key) { - if (!process.env[key]) { +function getTrustProxies(key: string): string[] { + const value = process.env[key] + if (!value) { return [] } - return process.env[key] + return value .split(',') .map((p) => validator.trim(p)) .filter((p) => validator.isIPRange(p) || validator.isIP(p)) } -function getUrl(key) { +function getUrl(key: string): string { const url = get(key, '') if (validator.isURL(url)) { return validator.trim(url) @@ -50,8 +51,8 @@ function createConfig() { clientSecret: get('GITHUB_CLIENT_SECRET', ''), }, trustedProxies: getTrustProxies('TRUSTED_PROXIES'), - surveyUrl: getUrl('SURVEY_URL', ''), - bugReportUrl: getUrl('BUG_REPORT_URL', ''), + surveyUrl: getUrl('SURVEY_URL'), + bugReportUrl: getUrl('BUG_REPORT_URL'), docker: { network: get('DOCKER_NETWORK', 'host'), @@ -74,7 +75,7 @@ function createConfig() { const config = createConfig() -export function getQuestImage(id) { +export function getQuestImage(id: string): string { return `${config.docker.imagePrefix}${id}` } diff --git a/server/src/models/userManager.js b/server/src/models/userManager.ts similarity index 88% rename from server/src/models/userManager.js rename to server/src/models/userManager.ts index d2f5faf..9651add 100644 --- a/server/src/models/userManager.js +++ b/server/src/models/userManager.ts @@ -1,7 +1,12 @@ import { User, UserProfile } from '@linux-odyssey/models' +import { SocialLogin } from '../auth/oauthVerify.js' import { hashPassword } from '../utils/auth.js' -export async function createUser(username, email, { password, socialLogin }) { +export async function createUser( + username: string, + email: string, + { password, socialLogin }: { password?: string; socialLogin?: SocialLogin } +) { const user = new User({ username, email, diff --git a/server/src/utils/auth.js b/server/src/utils/auth.ts similarity index 69% rename from server/src/utils/auth.js rename to server/src/utils/auth.ts index 2c87137..751ae8f 100644 --- a/server/src/utils/auth.js +++ b/server/src/utils/auth.ts @@ -2,11 +2,11 @@ import jwt from 'jsonwebtoken' import bcrypt from 'bcrypt' import config from '../config.js' -export function hashPassword(password) { +export function hashPassword(password: string): Promise { return bcrypt.hash(password, 10) } -export function genJWT(payload) { +export function genJWT(payload: any): Promise { return new Promise((resolve, reject) => { jwt.sign( payload, @@ -15,6 +15,8 @@ export function genJWT(payload) { (err, token) => { if (err) { reject(err) + } else if (token === undefined) { + reject(new Error('Token is undefined')) } else { resolve(token) } @@ -23,7 +25,7 @@ export function genJWT(payload) { }) } -export function verifyJWT(token) { +export function verifyJWT(token: string): Promise { return new Promise((resolve, reject) => { jwt.verify(token, config.secret, (err, decoded) => { if (err) { diff --git a/server/src/utils/env.js b/server/src/utils/env.js deleted file mode 100644 index 6f2c883..0000000 --- a/server/src/utils/env.js +++ /dev/null @@ -1,13 +0,0 @@ -import crypto from 'crypto' - -export function get(key, defaultValue) { - const value = process.env[key] - if (value != null) { - return value - } - return defaultValue -} - -export function genSecret() { - return crypto.randomUUID() -} diff --git a/server/src/utils/env.ts b/server/src/utils/env.ts new file mode 100644 index 0000000..513af2f --- /dev/null +++ b/server/src/utils/env.ts @@ -0,0 +1,10 @@ +import crypto from 'crypto' + +export function get(key: string, defaultValue: T): T { + const value = process.env[key] + return value !== undefined ? (value as unknown as T) : defaultValue +} + +export function genSecret() { + return crypto.randomUUID() +} diff --git a/server/src/utils/logger.js b/server/src/utils/logger.ts similarity index 100% rename from server/src/utils/logger.js rename to server/src/utils/logger.ts diff --git a/server/src/utils/quest.js b/server/src/utils/quest.ts similarity index 86% rename from server/src/utils/quest.js rename to server/src/utils/quest.ts index 31f7d1e..9fda0b6 100644 --- a/server/src/utils/quest.js +++ b/server/src/utils/quest.ts @@ -8,7 +8,10 @@ import logger from './logger.js' const questDirectory = path.join(process.cwd(), '..', 'quests') class QuestValidationError extends Error { - constructor(message, questId, error) { + questId: string + error: any + + constructor(message: string, questId: string, error: any) { super(message) this.name = 'QuestValidationError' this.questId = questId @@ -16,7 +19,10 @@ class QuestValidationError extends Error { } } -async function mapQuests(callback) { +// eslint-disable-next-line no-unused-vars +type QuestCallback = (id: string, questPath: string) => Promise + +async function mapQuests(callback: QuestCallback) { const questNames = await fs.readdir(questDirectory, { withFileTypes: true, }) @@ -44,7 +50,7 @@ export async function loadAndUpdateQuests() { return mapQuests(async (id, questPath) => { const files = await fs.readdir(questPath) if (!files.includes('game.yml')) { - throw new QuestValidationError(`Missing game.yml`, id) + throw new QuestValidationError(`Missing game.yml`, id, null) } try { diff --git a/server/src/utils/setupTest.js b/server/src/utils/setupTest.ts similarity index 100% rename from server/src/utils/setupTest.js rename to server/src/utils/setupTest.ts diff --git a/yarn.lock b/yarn.lock index 03133b8..204d7d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1351,6 +1351,15 @@ __metadata: languageName: node linkType: hard +"@types/bcrypt@npm:^5.0.2": + version: 5.0.2 + resolution: "@types/bcrypt@npm:5.0.2" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/dd7f05e183b9b1fc08ec499069febf197ab8e9c720766b5bbb5628395082e248f9a444c60882fe7788361fcadc302e21e055ab9c26a300f100e08791c353e6aa + languageName: node + linkType: hard + "@types/body-parser@npm:*": version: 1.19.5 resolution: "@types/body-parser@npm:1.19.5" @@ -1484,6 +1493,15 @@ __metadata: languageName: node linkType: hard +"@types/jsonwebtoken@npm:^9.0.5": + version: 9.0.5 + resolution: "@types/jsonwebtoken@npm:9.0.5" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/c582b8420586f3b9550f7e34992cb32be300bc953636f3b087ed9c180ce7ea5c2e4b35090be2d57f0d3168cc3ca1074932907caa2afe09f4e9c84cf5c0daefa8 + languageName: node + linkType: hard + "@types/mime@npm:*": version: 3.0.4 resolution: "@types/mime@npm:3.0.4" @@ -1507,6 +1525,35 @@ __metadata: languageName: node linkType: hard +"@types/oauth@npm:*": + version: 0.9.4 + resolution: "@types/oauth@npm:0.9.4" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/fcf8f5db7756a3e4d406ce3520abf78fecaf5e14963ad75fe0057425bb8b754e98d09dcb2b73a70e8fbe1e345b7d972d8090cf8cf08247e80d6eaac3acbe3fc7 + languageName: node + linkType: hard + +"@types/passport-oauth2@npm:^1.4.15": + version: 1.4.15 + resolution: "@types/passport-oauth2@npm:1.4.15" + dependencies: + "@types/express": "npm:*" + "@types/oauth": "npm:*" + "@types/passport": "npm:*" + checksum: 10c0/3f2b1c5c96ce0a1af9d55c07b18d68e443adccd9c5d429c0b08c6e35f0ca259bf77c29cecf2b94c1a035ce7c1c84087a2528e5ecc5ecfa0fba543e158bab7427 + languageName: node + linkType: hard + +"@types/passport@npm:*, @types/passport@npm:^1.0.16": + version: 1.0.16 + resolution: "@types/passport@npm:1.0.16" + dependencies: + "@types/express": "npm:*" + checksum: 10c0/7120c1186c8c67e3818683b5b6a4439d102f67da93cc1c7d8f32484f7bf10e8438dd5de0bf571910b23d06caa43dd1ad501933b48618bfaf54e63219500993fe + languageName: node + linkType: hard + "@types/qs@npm:*": version: 6.9.11 resolution: "@types/qs@npm:6.9.11" @@ -1577,6 +1624,13 @@ __metadata: languageName: node linkType: hard +"@types/validator@npm:^13.11.9": + version: 13.11.9 + resolution: "@types/validator@npm:13.11.9" + checksum: 10c0/856ebfcfe25d6c91a90235e0eb27302a737832530898195bbfb265da52ae7fe6d68f684942574f8818d3c262cae7a1de99f145dac73fc57217933af1bfc199cb + languageName: node + linkType: hard + "@types/webidl-conversions@npm:*": version: 7.0.3 resolution: "@types/webidl-conversions@npm:7.0.3" @@ -8433,8 +8487,13 @@ __metadata: "@linux-odyssey/file-graph": "npm:*" "@linux-odyssey/models": "npm:*" "@linux-odyssey/utils": "npm:*" + "@types/bcrypt": "npm:^5.0.2" "@types/express": "npm:^4.17.21" "@types/express-session": "npm:^1.17.10" + "@types/jsonwebtoken": "npm:^9.0.5" + "@types/passport": "npm:^1.0.16" + "@types/passport-oauth2": "npm:^1.4.15" + "@types/validator": "npm:^13.11.9" bcrypt: "npm:^5.1.1" commander: "npm:^11.0.0" concurrently: "npm:^8.2.2"