diff --git a/.prettierignore b/.prettierignore index 9e89e44f..596be03a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,22 +2,22 @@ **/*.svg **/*.ejs **/*.html -.umi -.umi-production -.umi-test -.history -.tmp -node_modules +/.umi +/.umi-production +/.umi-test +/.history +/.tmp +/node_modules npm-debug.log* yarn-error.log yarn.lock package-lock.json -static -data +/static +/data DS_Store -src/.umi -src/.umi-production -src/.umi-test +/src/.umi +/src/.umi-production +/src/.umi-test .env.local .env version.ts diff --git a/back/api/system.ts b/back/api/system.ts index 0680260d..b8ae89eb 100644 --- a/back/api/system.ts +++ b/back/api/system.ts @@ -33,7 +33,7 @@ export default (app: Router) => { const logger: Logger = Container.get('logger'); try { const userService = Container.get(UserService); - const authInfo = await userService.getUserInfo(); + const authInfo = await userService.getAuthInfo(); const { version, changeLog, changeLogLink, publishTime } = await parseVersion(config.versionFile); diff --git a/back/api/user.ts b/back/api/user.ts index cdb0eb61..f0a31a30 100644 --- a/back/api/user.ts +++ b/back/api/user.ts @@ -90,7 +90,7 @@ export default (app: Router) => { const logger: Logger = Container.get('logger'); try { const userService = Container.get(UserService); - const authInfo = await userService.getUserInfo(); + const authInfo = await userService.getAuthInfo(); res.send({ code: 200, data: { diff --git a/back/data/system.ts b/back/data/system.ts index bca6c4d6..122960bf 100644 --- a/back/data/system.ts +++ b/back/data/system.ts @@ -27,6 +27,7 @@ export enum AuthDataType { 'notification' = 'notification', 'removeLogFrequency' = 'removeLogFrequency', 'systemConfig' = 'systemConfig', + 'authConfig' = 'authConfig', } export interface SystemConfigInfo { @@ -46,11 +47,31 @@ export interface LoginLogInfo { status?: LoginStatus; } +export interface AuthInfo { + username: string; + password: string; + retries: number; + lastlogon: number; + lastip: string; + lastaddr: string; + platform: string; + isTwoFactorChecking: boolean; + token: string; + tokens: Record; + twoFactorActivated: boolean; + twoFactorActived: boolean; + twoFactorSecret: string; + avatar: string; +} + export type SystemModelInfo = SystemConfigInfo & Partial & - LoginLogInfo; + LoginLogInfo & + Partial; -export interface SystemInstance extends Model, SystemInfo { } +export interface SystemInstance + extends Model, + SystemInfo {} export const SystemModel = sequelize.define('Auth', { ip: DataTypes.STRING, type: DataTypes.STRING, diff --git a/back/loaders/express.ts b/back/loaders/express.ts index 56ebacdf..ef91b709 100644 --- a/back/loaders/express.ts +++ b/back/loaders/express.ts @@ -4,18 +4,14 @@ import cors from 'cors'; import routes from '../api'; import config from '../config'; import { UnauthorizedError, expressjwt } from 'express-jwt'; -import fs from 'fs/promises'; -import { getPlatform, getToken, safeJSONParse } from '../config/util'; -import Container from 'typedi'; -import OpenService from '../services/open'; +import { getPlatform, getToken } from '../config/util'; import rewrite from 'express-urlrewrite'; -import UserService from '../services/user'; import * as Sentry from '@sentry/node'; -import { EnvModel } from '../data/env'; import { errors } from 'celebrate'; import { createProxyMiddleware } from 'http-proxy-middleware'; import { serveEnv } from '../config/serverEnv'; import Logger from './logger'; +import { IKeyvStore, shareStore } from '../shared/store'; export default ({ app }: { app: Application }) => { app.set('trust proxy', 'loopback'); @@ -58,8 +54,10 @@ export default ({ app }: { app: Application }) => { app.use(async (req, res, next) => { const headerToken = getToken(req); if (req.path.startsWith('/open/')) { - const openService = Container.get(OpenService); - const doc = await openService.findTokenByValue(headerToken); + const apps = await shareStore.getApps(); + const doc = apps?.filter((x) => + x.tokens?.find((y) => y.value === headerToken), + )?.[0]; if (doc && doc.tokens && doc.tokens.length > 0) { const currentToken = doc.tokens.find((x) => x.value === headerToken); const keyMatch = req.path.match(/\/open\/([a-z]+)\/*/); @@ -83,9 +81,9 @@ export default ({ app }: { app: Application }) => { return next(); } - const data = await fs.readFile(config.authConfigFile, 'utf8'); - if (data && headerToken) { - const { token = '', tokens = {} } = safeJSONParse(data); + const authInfo = await shareStore.getAuthInfo(); + if (authInfo && headerToken) { + const { token = '', tokens = {} } = authInfo; if (headerToken === token || tokens[req.platform] === headerToken) { return next(); } @@ -103,8 +101,8 @@ export default ({ app }: { app: Application }) => { if (!['/api/user/init', '/api/user/notification/init'].includes(req.path)) { return next(); } - const userService = Container.get(UserService); - const authInfo = await userService.getUserInfo(); + const authInfo = + (await shareStore.getAuthInfo()) || ({} as IKeyvStore['authInfo']); let isInitialized = true; if ( diff --git a/back/loaders/initData.ts b/back/loaders/initData.ts index f864088b..4b852882 100644 --- a/back/loaders/initData.ts +++ b/back/loaders/initData.ts @@ -12,7 +12,10 @@ import { initPosition } from '../data/env'; import { AuthDataType, SystemModel } from '../data/system'; import SystemService from '../services/system'; import UserService from '../services/user'; -import { writeFile } from 'fs/promises'; +import { writeFile, readFile } from 'fs/promises'; +import { safeJSONParse } from '../config/util'; +import OpenService from '../services/open'; +import { shareStore } from '../shared/store'; export default async () => { const cronService = Container.get(CronService); @@ -20,10 +23,38 @@ export default async () => { const dependenceService = Container.get(DependenceService); const systemService = Container.get(SystemService); const userService = Container.get(UserService); + const openService = Container.get(OpenService); // 初始化增加系统配置 await SystemModel.upsert({ type: AuthDataType.systemConfig }); await SystemModel.upsert({ type: AuthDataType.notification }); + await SystemModel.upsert({ type: AuthDataType.authConfig }); + const authConfig = await SystemModel.findOne({ + where: { type: AuthDataType.authConfig }, + }); + if (!authConfig?.info) { + let authInfo = { + username: 'admin', + password: 'admin', + }; + try { + const content = await readFile(config.authConfigFile, 'utf8'); + authInfo = safeJSONParse(content); + } catch (error) {} + if (authConfig?.id) { + await SystemModel.update( + { info: authInfo }, + { + where: { id: authConfig.id }, + }, + ); + } else { + await SystemModel.create({ + info: authInfo, + type: AuthDataType.authConfig, + }); + } + } // 初始化通知配置 const notifyConfig = await userService.getNotificationMode(); @@ -169,4 +200,11 @@ export default async () => { // 初始化保存一次ck和定时任务数据 await cronService.autosave_crontab(); await envService.set_envs(); + + const authInfo = await userService.getAuthInfo(); + const apps = await openService.findApps(); + await shareStore.updateAuthInfo(authInfo); + if (apps?.length) { + await shareStore.updateApps(apps); + } }; diff --git a/back/loaders/initFile.ts b/back/loaders/initFile.ts index 5983ccfa..f4a321a6 100644 --- a/back/loaders/initFile.ts +++ b/back/loaders/initFile.ts @@ -20,9 +20,7 @@ const bakPath = path.join(dataPath, 'bak/'); const samplePath = path.join(rootPath, 'sample/'); const tmpPath = path.join(logPath, '.tmp/'); const confFile = path.join(configPath, 'config.sh'); -const authConfigFile = path.join(configPath, 'auth.json'); const sampleConfigFile = path.join(samplePath, 'config.sample.sh'); -const sampleAuthFile = path.join(samplePath, 'auth.sample.json'); const sampleTaskShellFile = path.join(samplePath, 'task.sample.sh'); const sampleNotifyJsFile = path.join(samplePath, 'notify.js'); const sampleNotifyPyFile = path.join(samplePath, 'notify.py'); @@ -40,7 +38,6 @@ const sshdPath = path.join(dataPath, 'ssh.d'); const systemLogPath = path.join(dataPath, 'syslog'); export default async () => { - const authFileExist = await fileExist(authConfigFile); const confFileExist = await fileExist(confFile); const scriptDirExist = await fileExist(scriptPath); const preloadDirExist = await fileExist(preloadPath); @@ -100,9 +97,6 @@ export default async () => { } // 初始化文件 - if (!authFileExist) { - await fs.writeFile(authConfigFile, await fs.readFile(sampleAuthFile)); - } if (!confFileExist) { await fs.writeFile(confFile, await fs.readFile(sampleConfigFile)); diff --git a/back/loaders/sock.ts b/back/loaders/sock.ts index b63b6c36..d7db3d97 100644 --- a/back/loaders/sock.ts +++ b/back/loaders/sock.ts @@ -2,9 +2,8 @@ import sockJs from 'sockjs'; import { Server } from 'http'; import { Container } from 'typedi'; import SockService from '../services/sock'; -import config from '../config/index'; -import fs from 'fs/promises'; -import { getPlatform, safeJSONParse } from '../config/util'; +import { getPlatform } from '../config/util'; +import { shareStore } from '../shared/store'; export default async ({ server }: { server: Server }) => { const echo = sockJs.createServer({ prefix: '/api/ws', log: () => {} }); @@ -15,11 +14,11 @@ export default async ({ server }: { server: Server }) => { conn.close('404'); } - const data = await fs.readFile(config.authConfigFile, 'utf8'); + const authInfo = await shareStore.getAuthInfo(); const platform = getPlatform(conn.headers['user-agent'] || '') || 'desktop'; const headerToken = conn.url.replace(`${conn.pathname}?token=`, ''); - if (data) { - const { token = '', tokens = {} } = safeJSONParse(data); + if (authInfo) { + const { token = '', tokens = {} } = authInfo; if (headerToken === token || tokens[platform] === headerToken) { sockService.addClient(conn); diff --git a/back/services/open.ts b/back/services/open.ts index 573f5cb3..9694d593 100644 --- a/back/services/open.ts +++ b/back/services/open.ts @@ -1,19 +1,18 @@ import { Service, Inject } from 'typedi'; import winston from 'winston'; import { createRandomString } from '../config/util'; -import config from '../config'; import { App, AppModel } from '../data/open'; import { v4 as uuidV4 } from 'uuid'; import sequelize, { Op } from 'sequelize'; +import { shareStore } from '../shared/store'; @Service() export default class OpenService { constructor(@Inject('logger') private logger: winston.Logger) {} - public async findTokenByValue(token: string): Promise { + public async findApps(): Promise { const docs = await this.find({}); - const doc = docs.filter((x) => x.tokens?.find((y) => y.value === token)); - return doc[0]; + return docs; } public async create(payload: App): Promise { @@ -34,17 +33,19 @@ export default class OpenService { name: payload.name, scopes: payload.scopes, id: payload.id, - } as any); + } as App); return { ...newDoc, tokens: [] }; } - private async updateDb(payload: App): Promise { + private async updateDb(payload: Partial): Promise { await AppModel.update(payload, { where: { id: payload.id } }); - return await this.getDb({ id: payload.id }); + const apps = await this.find({}); + await shareStore.updateApps(apps); + return apps?.find((x) => x.id === payload.id) as App; } - public async getDb(query: any): Promise { - const doc: any = await AppModel.findOne({ where: query }); + public async getDb(query: Record): Promise { + const doc = await AppModel.findOne({ where: query }); if (!doc) { throw new Error(`App ${JSON.stringify(query)} not found`); } @@ -56,7 +57,7 @@ export default class OpenService { } public async resetSecret(id: number): Promise { - const tab: any = { + const tab: Partial = { client_secret: createRandomString(24, 24), tokens: [], id, @@ -74,7 +75,7 @@ export default class OpenService { public async list( searchText: string = '', sort: any = {}, - query: any = {}, + query: Record = {}, ): Promise { let condition = { ...query }; if (searchText) { @@ -101,7 +102,7 @@ export default class OpenService { } } - private async find(query: any, sort?: any): Promise { + private async find(query: Record, sort?: any): Promise { const docs = await AppModel.findAll({ where: { ...query } }); return docs.map((x) => x.get({ plain: true })); } diff --git a/back/services/user.ts b/back/services/user.ts index 79b9146f..5a951c2b 100644 --- a/back/services/user.ts +++ b/back/services/user.ts @@ -18,6 +18,7 @@ import { SystemModel, SystemModelInfo, LoginStatus, + AuthInfo, } from '../data/system'; import { NotificationInfo } from '../data/notify'; import NotificationService from './notify'; @@ -28,6 +29,7 @@ import dayjs from 'dayjs'; import IP2Region from 'ip2region'; import requestIp from 'request-ip'; import uniq from 'lodash/uniq'; +import { shareStore } from '../shared/store'; @Service() export default class UserService { @@ -48,161 +50,142 @@ export default class UserService { req: Request, needTwoFactor = true, ): Promise { - const _exist = await fileExist(config.authConfigFile); - if (!_exist) { - return this.initAuthInfo(); - } - let { username, password } = payloads; const content = await this.getAuthInfo(); const timestamp = Date.now(); - if (content) { - let { - username: cUsername, - password: cPassword, - retries = 0, - lastlogon, - lastip, - lastaddr, - twoFactorActivated, - twoFactorActived, - tokens = {}, - platform, - } = content; - // patch old field - twoFactorActivated = twoFactorActivated || twoFactorActived; + let { + username: cUsername, + password: cPassword, + retries = 0, + lastlogon, + lastip, + lastaddr, + twoFactorActivated, + twoFactorActived, + tokens = {}, + platform, + } = content; + // patch old field + twoFactorActivated = twoFactorActivated || twoFactorActived; - if ( - (cUsername === 'admin' && cPassword === 'admin') || - !cUsername || - !cPassword - ) { - return this.initAuthInfo(); - } + const retriesTime = Math.pow(3, retries) * 1000; + if (retries > 2 && timestamp - lastlogon < retriesTime) { + const waitTime = Math.ceil( + (retriesTime - (timestamp - lastlogon)) / 1000, + ); + return { + code: 410, + message: `失败次数过多,请${waitTime}秒后重试`, + data: waitTime, + }; + } - const retriesTime = Math.pow(3, retries) * 1000; - if (retries > 2 && timestamp - lastlogon < retriesTime) { - const waitTime = Math.ceil( - (retriesTime - (timestamp - lastlogon)) / 1000, - ); + if ( + username === cUsername && + password === cPassword && + twoFactorActivated && + needTwoFactor + ) { + await this.updateAuthInfo(content, { + isTwoFactorChecking: true, + }); + return { + code: 420, + message: '', + }; + } + + const ip = requestIp.getClientIp(req) || ''; + const query = new IP2Region(); + const ipAddress = query.search(ip); + let address = ''; + if (ipAddress) { + const { country, province, city, isp } = ipAddress; + address = uniq([country, province, city, isp]).filter(Boolean).join(' '); + } + if (username === cUsername && password === cPassword) { + const data = createRandomString(50, 100); + const expiration = twoFactorActivated ? 60 : 20; + let token = jwt.sign({ data }, config.secret as any, { + expiresIn: 60 * 60 * 24 * expiration, + algorithm: 'HS384', + }); + + await this.updateAuthInfo(content, { + token, + tokens: { + ...tokens, + [req.platform]: token, + }, + lastlogon: timestamp, + retries: 0, + lastip: ip, + lastaddr: address, + platform: req.platform, + isTwoFactorChecking: false, + }); + this.notificationService.notify( + '登录通知', + `你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${ + req.platform + }端 登录成功,ip地址 ${ip}`, + ); + await this.insertDb({ + type: AuthDataType.loginLog, + info: { + timestamp, + address, + ip, + platform: req.platform, + status: LoginStatus.success, + }, + }); + this.getLoginLog(); + return { + code: 200, + data: { token, lastip, lastaddr, lastlogon, retries, platform }, + }; + } else { + await this.updateAuthInfo(content, { + retries: retries + 1, + lastlogon: timestamp, + lastip: ip, + lastaddr: address, + platform: req.platform, + }); + this.notificationService.notify( + '登录通知', + `你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${ + req.platform + }端 登录失败,ip地址 ${ip}`, + ); + await this.insertDb({ + type: AuthDataType.loginLog, + info: { + timestamp, + address, + ip, + platform: req.platform, + status: LoginStatus.fail, + }, + }); + this.getLoginLog(); + if (retries > 2) { + const waitTime = Math.round(Math.pow(3, retries + 1)); return { code: 410, message: `失败次数过多,请${waitTime}秒后重试`, data: waitTime, }; - } - - if ( - username === cUsername && - password === cPassword && - twoFactorActivated && - needTwoFactor - ) { - this.updateAuthInfo(content, { - isTwoFactorChecking: true, - }); - return { - code: 420, - message: '', - }; - } - - const ip = requestIp.getClientIp(req) || ''; - const query = new IP2Region(); - const ipAddress = query.search(ip); - let address = ''; - if (ipAddress) { - const { country, province, city, isp } = ipAddress; - address = uniq([country, province, city, isp]) - .filter(Boolean) - .join(' '); - } - if (username === cUsername && password === cPassword) { - const data = createRandomString(50, 100); - const expiration = twoFactorActivated ? 60 : 20; - let token = jwt.sign({ data }, config.secret as any, { - expiresIn: 60 * 60 * 24 * expiration, - algorithm: 'HS384', - }); - - this.updateAuthInfo(content, { - token, - tokens: { - ...tokens, - [req.platform]: token, - }, - lastlogon: timestamp, - retries: 0, - lastip: ip, - lastaddr: address, - platform: req.platform, - isTwoFactorChecking: false, - }); - this.notificationService.notify( - '登录通知', - `你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${ - req.platform - }端 登录成功,ip地址 ${ip}`, - ); - await this.insertDb({ - type: AuthDataType.loginLog, - info: { - timestamp, - address, - ip, - platform: req.platform, - status: LoginStatus.success, - }, - }); - this.getLoginLog(); - return { - code: 200, - data: { token, lastip, lastaddr, lastlogon, retries, platform }, - }; } else { - this.updateAuthInfo(content, { - retries: retries + 1, - lastlogon: timestamp, - lastip: ip, - lastaddr: address, - platform: req.platform, - }); - this.notificationService.notify( - '登录通知', - `你于${dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')}在 ${address} ${ - req.platform - }端 登录失败,ip地址 ${ip}`, - ); - await this.insertDb({ - type: AuthDataType.loginLog, - info: { - timestamp, - address, - ip, - platform: req.platform, - status: LoginStatus.fail, - }, - }); - this.getLoginLog(); - if (retries > 2) { - const waitTime = Math.round(Math.pow(3, retries + 1)); - return { - code: 410, - message: `失败次数过多,请${waitTime}秒后重试`, - data: waitTime, - }; - } else { - return { code: 400, message: config.authError }; - } + return { code: 400, message: config.authError }; } - } else { - return this.initAuthInfo(); } } public async logout(platform: string): Promise { const authInfo = await this.getAuthInfo(); - this.updateAuthInfo(authInfo, { + await this.updateAuthInfo(authInfo, { token: '', tokens: { ...authInfo.tokens, [platform]: '' }, }); @@ -257,35 +240,21 @@ export default class UserService { return { code: 400, message: '密码不能设置为admin' }; } const authInfo = await this.getAuthInfo(); - this.updateAuthInfo(authInfo, { username, password }); + await this.updateAuthInfo(authInfo, { username, password }); return { code: 200, message: '更新成功' }; } public async updateAvatar(avatar: string) { const authInfo = await this.getAuthInfo(); - this.updateAuthInfo(authInfo, { avatar }); + await this.updateAuthInfo(authInfo, { avatar }); return { code: 200, data: avatar, message: '更新成功' }; } - public async getUserInfo(): Promise { - const authFileExist = await fileExist(config.authConfigFile); - if (!authFileExist) { - await createFile( - config.authConfigFile, - JSON.stringify({ - username: 'admin', - password: 'admin', - }), - ); - } - return await this.getAuthInfo(); - } - public async initTwoFactor() { const secret = authenticator.generateSecret(); const authInfo = await this.getAuthInfo(); const otpauth = authenticator.keyuri(authInfo.username, 'qinglong', secret); - this.updateAuthInfo(authInfo, { twoFactorSecret: secret }); + await this.updateAuthInfo(authInfo, { twoFactorSecret: secret }); return { secret, url: otpauth }; } @@ -296,7 +265,7 @@ export default class UserService { secret: authInfo.twoFactorSecret, }); if (isValid) { - this.updateAuthInfo(authInfo, { twoFactorActivated: true }); + await this.updateAuthInfo(authInfo, { twoFactorActivated: true }); } return isValid; } @@ -322,7 +291,7 @@ export default class UserService { return this.login({ username, password }, req, false); } else { const { ip, address } = await getNetIp(req); - this.updateAuthInfo(authInfo, { + await this.updateAuthInfo(authInfo, { lastip: ip, lastaddr: address, platform: req.platform, @@ -333,7 +302,7 @@ export default class UserService { public async deactiveTwoFactor() { const authInfo = await this.getAuthInfo(); - this.updateAuthInfo(authInfo, { + await this.updateAuthInfo(authInfo, { twoFactorActivated: false, twoFactorActived: false, twoFactorSecret: '', @@ -341,16 +310,22 @@ export default class UserService { return true; } - private async getAuthInfo() { - const content = await fs.readFile(config.authConfigFile, 'utf8'); - return safeJSONParse(content); + public async getAuthInfo() { + const authInfo = await shareStore.getAuthInfo(); + if (authInfo) { + return authInfo; + } + const doc = await this.getDb({ type: AuthDataType.authConfig }); + return (doc.info || {}) as AuthInfo; } private async updateAuthInfo(authInfo: any, info: any) { - await fs.writeFile( - config.authConfigFile, - JSON.stringify({ ...authInfo, ...info }), - ); + const result = { ...authInfo, ...info }; + await shareStore.updateAuthInfo(result); + await this.updateAuthDb({ + type: AuthDataType.authConfig, + info: result, + }); } public async getNotificationMode(): Promise { diff --git a/back/shared/store.ts b/back/shared/store.ts new file mode 100644 index 00000000..186adf43 --- /dev/null +++ b/back/shared/store.ts @@ -0,0 +1,34 @@ +import { AuthInfo } from '../data/system'; +import { App } from '../data/open'; +import Keyv from 'keyv'; +import KeyvSqlite from '@keyv/sqlite'; +import config from '../config'; +import path from 'path'; + +export enum EKeyv { + 'apps' = 'apps', + 'authInfo' = 'authInfo', +} + +export interface IKeyvStore { + apps: App[]; + authInfo: AuthInfo; +} + +const keyvSqlite = new KeyvSqlite(path.join(config.dbPath, 'keyv.sqlite')); +export const keyvStore = new Keyv({ store: keyvSqlite }); + +export const shareStore = { + getAuthInfo() { + return keyvStore.get(EKeyv.authInfo); + }, + updateAuthInfo(value: IKeyvStore['authInfo']) { + return keyvStore.set(EKeyv.authInfo, value); + }, + getApps() { + return keyvStore.get(EKeyv.apps); + }, + updateApps(apps: App[]) { + return keyvStore.set(EKeyv.apps, apps); + }, +}; diff --git a/package.json b/package.json index c70d3c45..5fc9ae6f 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,9 @@ "react-dom": "18", "dva-core": "2" } + }, + "overrides": { + "sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3" } }, "dependencies": { @@ -98,7 +101,9 @@ "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0", "request-ip": "3.3.0", - "ip2region": "2.3.0" + "ip2region": "2.3.0", + "keyv": "^5.2.3", + "@keyv/sqlite": "^4.0.1" }, "devDependencies": { "moment": "2.30.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6a9f1998..bf0ed1bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,13 +1,15 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false +overrides: + sqlite3: git+https://github.com/whyour/node-sqlite3.git#v1.0.3 dependencies: '@grpc/grpc-js': specifier: ^1.12.3 version: 1.12.3 + '@keyv/sqlite': + specifier: ^4.0.1 + version: 4.0.1 '@otplib/preset-default': specifier: ^12.0.1 version: 12.0.1 @@ -74,6 +76,9 @@ dependencies: jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 + keyv: + specifier: ^5.2.3 + version: 5.2.3 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -2773,6 +2778,23 @@ packages: resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} dev: false + /@keyv/serialize@1.0.2: + resolution: {integrity: sha512-+E/LyaAeuABniD/RvUezWVXKpeuvwLEA9//nE9952zBaOdBd2mQ3pPoM8cUe2X6IcMByfuSLzmYqnYshG60+HQ==} + dependencies: + buffer: 6.0.3 + dev: false + + /@keyv/sqlite@4.0.1: + resolution: {integrity: sha512-Ngs9jhElXN7efS9WvvCC/p6rXMbihna8eNLVBc421Zf+VcFd+pR4DOcS6yA9V22EKtAy4DEj7LtvEEIm0bb80A==} + engines: {node: '>= 18'} + dependencies: + sqlite3: github.com/whyour/node-sqlite3/3a00af0b5d7603b7f1b290032507320b18a6b741 + transitivePeerDependencies: + - bluebird + - encoding + - supports-color + dev: false + /@lezer/common@1.2.3: resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} dev: true @@ -6377,7 +6399,6 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: true /before@0.0.1: resolution: {integrity: sha512-1J5SWbkoVJH9DTALN8igB4p+nPKZzPrJ/HomqBDLpfUvDXCdjdBmBUcH5McZfur0lftVssVU6BZug5NYh87zTw==} @@ -6572,6 +6593,13 @@ packages: isarray: 1.0.0 dev: true + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + /builtin-status-codes@3.0.0: resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} dev: true @@ -9406,7 +9434,6 @@ packages: /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true /ignore-by-default@1.0.1: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} @@ -10161,6 +10188,12 @@ packages: dependencies: json-buffer: 3.0.1 + /keyv@5.2.3: + resolution: {integrity: sha512-AGKecUfzrowabUv0bH1RIR5Vf7w+l4S3xtQAypKaUpTdIR1EbrAcTxHCrpo9Q+IWeUlFE2palRtgIQcgm+PQJw==} + dependencies: + '@keyv/serialize': 1.0.2 + dev: false + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -16141,3 +16174,7 @@ packages: - encoding - supports-color dev: false + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/shell/share.sh b/shell/share.sh index 8de4ca16..9fb63984 100755 --- a/shell/share.sh +++ b/shell/share.sh @@ -193,12 +193,6 @@ fix_config() { echo fi - if [[ ! -s $file_auth_user ]]; then - echo -e "复制一份 $file_auth_sample 为 $file_auth_user\n" - cp -fv $file_auth_sample $file_auth_user - echo - fi - if [[ ! -s $file_notify_py ]]; then echo -e "复制一份 $file_notify_py_sample 为 $file_notify_py\n" cp -fv $file_notify_py_sample $file_notify_py