mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-09-06 16:02:51 +08:00
99 lines
3.2 KiB
JavaScript
99 lines
3.2 KiB
JavaScript
import crypto from 'node:crypto'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import { createInterface } from 'node:readline/promises'
|
|
import { pathToFileURL } from 'node:url'
|
|
import Datastore from '@seald-io/nedb'
|
|
|
|
const DB_DIR = path.join(process.cwd(), 'app/db')
|
|
const USERNAME_LENGTH = 8
|
|
const PASSWORD_LENGTH = 16
|
|
|
|
const exitWithError = (message) => {
|
|
console.error(`重置登录凭据失败:${ message }`)
|
|
process.exitCode = 1
|
|
}
|
|
|
|
const randomString = (length) => {
|
|
const characters = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
|
|
return Array.from({ length }, () => characters[crypto.randomInt(characters.length)]).join('')
|
|
}
|
|
|
|
const confirmReset = async ({ input = process.stdin, output = process.stdout } = {}) => {
|
|
const readline = createInterface({ input, output })
|
|
try {
|
|
const answer = await Promise.race([
|
|
readline.question('此操作将重置管理员用户名和密码,并注销所有现有会话,确认继续?(y/N): '),
|
|
new Promise(resolve => readline.once('close', () => resolve('')))
|
|
])
|
|
return answer.trim() === 'y' || answer.trim() === 'Y'
|
|
} finally {
|
|
readline.close()
|
|
}
|
|
}
|
|
|
|
const resetPassword = async ({ dbDir = DB_DIR } = {}) => {
|
|
const keyDBPath = path.join(dbDir, 'key.db')
|
|
const sessionDBPath = path.join(dbDir, 'session.db')
|
|
if (!fs.existsSync(keyDBPath)) {
|
|
throw new Error(`未找到登录数据库:${ keyDBPath }`)
|
|
}
|
|
if (!fs.statSync(keyDBPath).isFile()) {
|
|
throw new Error(`登录数据库不是有效文件:${ keyDBPath }`)
|
|
}
|
|
|
|
const keyDB = new Datastore({ filename: keyDBPath })
|
|
await keyDB.loadDatabaseAsync()
|
|
const keyData = await keyDB.findOneAsync({})
|
|
if (!keyData?._id) {
|
|
throw new Error('登录数据库中没有有效的管理员账号')
|
|
}
|
|
|
|
let sessionDB
|
|
if (fs.existsSync(sessionDBPath)) {
|
|
if (!fs.statSync(sessionDBPath).isFile()) {
|
|
throw new Error(`会话数据库不是有效文件:${ sessionDBPath }`)
|
|
}
|
|
sessionDB = new Datastore({ filename: sessionDBPath })
|
|
await sessionDB.loadDatabaseAsync()
|
|
}
|
|
|
|
const username = randomString(USERNAME_LENGTH)
|
|
const password = randomString(PASSWORD_LENGTH)
|
|
const passwordHash = crypto.createHash('sha1').update(password).digest('hex')
|
|
const jwtToken = randomString(32)
|
|
|
|
if (sessionDB) {
|
|
await sessionDB.updateAsync({}, { $set: { revoked: true } }, { multi: true })
|
|
}
|
|
|
|
await keyDB.updateAsync(
|
|
{ _id: keyData._id },
|
|
{ $set: { user: username, pwd: passwordHash, jwtToken } }
|
|
)
|
|
|
|
return { username, password }
|
|
}
|
|
|
|
const main = async () => {
|
|
if (!fs.existsSync('/.dockerenv')) {
|
|
throw new Error('此脚本仅允许在 EasyNode Docker 容器内运行')
|
|
}
|
|
if (!await confirmReset()) {
|
|
console.log('操作已取消,登录凭据未修改。')
|
|
return
|
|
}
|
|
|
|
const { username, password } = await resetPassword()
|
|
console.log('EasyNode 管理员用户名和密码已重置,所有现有登录会话已失效。')
|
|
console.log(`新用户名:${ username }`)
|
|
console.log(`新密码:${ password }`)
|
|
console.log('请登录后立即修改用户名和密码,并妥善保存。')
|
|
}
|
|
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
main().catch(error => exitWithError(error.message))
|
|
}
|
|
|
|
export { confirmReset, resetPassword }
|