diff --git a/.gitignore b/.gitignore index 5cb8f362..dba9a63f 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ /config /log /db -/manual_log \ No newline at end of file +/manual_log +/scripts \ No newline at end of file diff --git a/back/api/config.ts b/back/api/config.ts index c52bc637..9da30007 100644 --- a/back/api/config.ts +++ b/back/api/config.ts @@ -5,33 +5,40 @@ import { Logger } from 'winston'; import config from '../config'; import * as fs from 'fs'; import { celebrate, Joi } from 'celebrate'; -import { execSync } from 'child_process'; const route = Router(); export default (app: Router) => { app.use('/', route); + route.get( - '/config/:key', + '/configs/files', async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - let content = '未找到文件'; - switch (req.params.key) { - case 'config': - content = getFileContentByName(config.confFile); - break; - case 'sample': - content = getFileContentByName(config.sampleFile); - break; - case 'crontab': - content = getFileContentByName(config.crontabFile); - break; - case 'extra': - content = getFileContentByName(config.extraFile); - break; - default: - break; - } + const fileList = fs.readdirSync(config.configPath, 'utf-8'); + res.send({ + code: 200, + data: fileList + .filter((x) => !config.blackFileList.includes(x)) + .map((x) => { + return { title: x, value: x }; + }), + }); + } catch (e) { + logger.error('🔥 error: %o', e); + return next(e); + } + }, + ); + + route.get( + '/configs/:file', + async (req: Request, res: Response, next: NextFunction) => { + const logger: Logger = Container.get('logger'); + try { + const content = getFileContentByName( + `${config.configPath}${req.params.file}`, + ); res.send({ code: 200, data: content }); } catch (e) { logger.error('🔥 error: %o', e); @@ -41,7 +48,7 @@ export default (app: Router) => { ); route.post( - '/save', + '/configs/save', celebrate({ body: Joi.object({ name: Joi.string().required(), @@ -52,11 +59,8 @@ export default (app: Router) => { const logger: Logger = Container.get('logger'); try { const { name, content } = req.body; - const path = (config.fileMap as any)[name]; + const path = `${config.configPath}${name}`; fs.writeFileSync(path, content); - if (name === 'crontab.list') { - execSync(`crontab ${path}`); - } res.send({ code: 200, msg: '保存成功' }); } catch (e) { logger.error('🔥 error: %o', e); diff --git a/back/api/cookie.ts b/back/api/env.ts similarity index 69% rename from back/api/cookie.ts rename to back/api/env.ts index bf75c20f..122a3f7c 100644 --- a/back/api/cookie.ts +++ b/back/api/env.ts @@ -1,6 +1,6 @@ import { Router, Request, Response, NextFunction } from 'express'; import { Container } from 'typedi'; -import CookieService from '../services/cookie'; +import EnvService from '../services/env'; import { Logger } from 'winston'; import { celebrate, Joi } from 'celebrate'; const route = Router(); @@ -8,12 +8,12 @@ const route = Router(); export default (app: Router) => { app.use('/', route); route.get( - '/cookies', + '/envs', async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const cookieService = Container.get(CookieService); - const data = await cookieService.cookies('', { position: -1 }, true); + const envService = Container.get(EnvService); + const data = await envService.envs('', { position: -1 }); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -23,15 +23,19 @@ export default (app: Router) => { ); route.post( - '/cookies', + '/envs', celebrate({ - body: Joi.array().items(Joi.string().required()).min(1), + body: Joi.object({ + value: Joi.string().required(), + name: Joi.string().required(), + remarks: Joi.string().optional(), + }), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const cookieService = Container.get(CookieService); - const data = await cookieService.create(req.body); + const envService = Container.get(EnvService); + const data = await envService.create(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -41,18 +45,20 @@ export default (app: Router) => { ); route.put( - '/cookies', + '/envs', celebrate({ body: Joi.object({ value: Joi.string().required(), + name: Joi.string().required(), + remarks: Joi.string().optional(), _id: Joi.string().required(), }), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const cookieService = Container.get(CookieService); - const data = await cookieService.update(req.body); + const envService = Container.get(EnvService); + const data = await envService.update(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -62,15 +68,15 @@ export default (app: Router) => { ); route.delete( - '/cookies', + '/envs', celebrate({ body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const cookieService = Container.get(CookieService); - const data = await cookieService.remove(req.body); + const envService = Container.get(EnvService); + const data = await envService.remove(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -80,7 +86,7 @@ export default (app: Router) => { ); route.put( - '/cookies/:id/move', + '/envs/:id/move', celebrate({ params: Joi.object({ id: Joi.string().required(), @@ -93,28 +99,8 @@ export default (app: Router) => { async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const cookieService = Container.get(CookieService); - const data = await cookieService.move(req.params.id, req.body); - return res.send({ code: 200, data }); - } catch (e) { - logger.error('🔥 error: %o', e); - return next(e); - } - }, - ); - - route.get( - '/cookies/:id/refresh', - celebrate({ - params: Joi.object({ - id: Joi.string().required(), - }), - }), - async (req: Request, res: Response, next: NextFunction) => { - const logger: Logger = Container.get('logger'); - try { - const cookieService = Container.get(CookieService); - const data = await cookieService.refreshCookie(req.params.id); + const envService = Container.get(EnvService); + const data = await envService.move(req.params.id, req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -124,15 +110,15 @@ export default (app: Router) => { ); route.put( - '/cookies/disable', + '/envs/disable', celebrate({ body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const cookieService = Container.get(CookieService); - const data = await cookieService.disabled(req.body); + const envService = Container.get(EnvService); + const data = await envService.disabled(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -142,15 +128,36 @@ export default (app: Router) => { ); route.put( - '/cookies/enable', + '/envs/enable', celebrate({ body: Joi.array().items(Joi.string().required()), }), async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const cookieService = Container.get(CookieService); - const data = await cookieService.enabled(req.body); + const envService = Container.get(EnvService); + const data = await envService.enabled(req.body); + return res.send({ code: 200, data }); + } catch (e) { + logger.error('🔥 error: %o', e); + return next(e); + } + }, + ); + + route.put( + '/envs/name', + celebrate({ + body: Joi.object({ + ids: Joi.array().items(Joi.string().required()), + name: Joi.string().required(), + }), + }), + async (req: Request, res: Response, next: NextFunction) => { + const logger: Logger = Container.get('logger'); + try { + const envService = Container.get(EnvService); + const data = await envService.updateNames(req.body); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); @@ -160,7 +167,7 @@ export default (app: Router) => { ); route.get( - '/cookies/:id', + '/envs/:id', celebrate({ params: Joi.object({ id: Joi.string().required(), @@ -169,8 +176,8 @@ export default (app: Router) => { async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { - const cookieService = Container.get(CookieService); - const data = await cookieService.get(req.params.id); + const envService = Container.get(EnvService); + const data = await envService.get(req.params.id); return res.send({ code: 200, data }); } catch (e) { logger.error('🔥 error: %o', e); diff --git a/back/api/index.ts b/back/api/index.ts index 38ba05e3..b200f60f 100644 --- a/back/api/index.ts +++ b/back/api/index.ts @@ -1,17 +1,19 @@ import { Router } from 'express'; import auth from './auth'; -import cookie from './cookie'; +import env from './env'; import config from './config'; import log from './log'; import cron from './cron'; +import script from './script'; export default () => { const app = Router(); auth(app); - cookie(app); + env(app); config(app); log(app); cron(app); + script(app); return app; }; diff --git a/back/api/script.ts b/back/api/script.ts new file mode 100644 index 00000000..c66b11f2 --- /dev/null +++ b/back/api/script.ts @@ -0,0 +1,48 @@ +import { getFileContentByName, getLastModifyFilePath } from '../config/util'; +import { Router, Request, Response, NextFunction } from 'express'; +import { Container } from 'typedi'; +import { Logger } from 'winston'; +import config from '../config'; +import * as fs from 'fs'; +import { celebrate, Joi } from 'celebrate'; +const route = Router(); + +export default (app: Router) => { + app.use('/', route); + + route.get( + '/scripts/files', + async (req: Request, res: Response, next: NextFunction) => { + const logger: Logger = Container.get('logger'); + try { + const fileList = fs.readdirSync(config.scriptPath, 'utf-8'); + res.send({ + code: 200, + data: fileList.map((x) => { + return { title: x, value: x, key: x }; + }), + }); + } catch (e) { + logger.error('🔥 error: %o', e); + return next(e); + } + }, + ); + + route.get( + '/scripts/:file', + async (req: Request, res: Response, next: NextFunction) => { + const logger: Logger = Container.get('logger'); + try { + console.log(req.params.file); + const content = getFileContentByName( + `${config.scriptPath}${req.params.file}`, + ); + res.send({ code: 200, data: content }); + } catch (e) { + logger.error('🔥 error: %o', e); + return next(e); + } + }, + ); +}; diff --git a/back/config/index.ts b/back/config/index.ts index 6d129ea4..d7eb56ee 100644 --- a/back/config/index.ts +++ b/back/config/index.ts @@ -5,13 +5,15 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; const envFound = dotenv.config(); const rootPath = path.resolve(__dirname, '../../'); -const cookieFile = path.join(rootPath, 'config/cookie.sh'); +const envFile = path.join(rootPath, 'config/env.sh'); const confFile = path.join(rootPath, 'config/config.sh'); const sampleFile = path.join(rootPath, 'sample/config.sample.sh'); const crontabFile = path.join(rootPath, 'config/crontab.list'); const confBakDir = path.join(rootPath, 'config/bak/'); const authConfigFile = path.join(rootPath, 'config/auth.json'); const extraFile = path.join(rootPath, 'config/extra.sh'); +const configPath = path.join(rootPath, 'config/'); +const scriptPath = path.join(rootPath, 'scripts/'); const logPath = path.join(rootPath, 'log/'); const authError = '错误的用户名密码,请重试'; const loginFaild = '请先登录!'; @@ -19,7 +21,7 @@ const configString = 'config sample crontab shareCode diy'; const dbPath = path.join(rootPath, 'db/'); const manualLogPath = path.join(rootPath, 'manual_log/'); const cronDbFile = path.join(rootPath, 'db/crontab.db'); -const cookieDbFile = path.join(rootPath, 'db/cookie.db'); +const envDbFile = path.join(rootPath, 'db/env.db'); const configFound = dotenv.config({ path: confFile }); if (envFound.error) { @@ -50,14 +52,12 @@ export default { crontabFile, sampleFile, confFile, - cookieFile, - fileMap: { - 'config.sh': confFile, - 'crontab.list': crontabFile, - 'extra.sh': extraFile, - }, + envFile, dbPath, cronDbFile, - cookieDbFile, + envDbFile, manualLogPath, + configPath, + scriptPath, + blackFileList: ['auth.json', 'config.sh.sample', 'cookie.sh', 'crontab.list'], }; diff --git a/back/data/cookie.ts b/back/data/env.ts similarity index 53% rename from back/data/cookie.ts rename to back/data/env.ts index 62cc3747..bc069ed2 100644 --- a/back/data/cookie.ts +++ b/back/data/env.ts @@ -1,22 +1,25 @@ -export class Cookie { +export class Env { value?: string; timestamp?: string; created?: number; _id?: string; - status?: CookieStatus; + status?: EnvStatus; position?: number; + name?: number; + remarks?: number; - constructor(options: Cookie) { + constructor(options: Env) { this.value = options.value; this._id = options._id; this.created = options.created || new Date().valueOf(); - this.status = options.status || CookieStatus.noacquired; + this.status = options.status || EnvStatus.noacquired; this.timestamp = new Date().toString(); - this.position = options.position; + this.name = options.name; + this.remarks = options.remarks; } } -export enum CookieStatus { +export enum EnvStatus { 'noacquired', 'normal', 'disabled', @@ -24,4 +27,4 @@ export enum CookieStatus { 'abnormal', } -export const initCookiePosition = 9999999999; +export const initEnvPosition = 9999999999; diff --git a/back/loaders/initData.ts b/back/loaders/initData.ts index f7aa9093..88162747 100644 --- a/back/loaders/initData.ts +++ b/back/loaders/initData.ts @@ -2,7 +2,7 @@ import { exec } from 'child_process'; import { Container } from 'typedi'; import { Crontab, CrontabStatus } from '../data/cron'; import CronService from '../services/cron'; -import CookieService from '../services/cookie'; +import EnvService from '../services/env'; const initData = [ { @@ -30,7 +30,7 @@ const initData = [ export default async () => { const cronService = Container.get(CronService); - const cookieService = Container.get(CookieService); + const envService = Container.get(EnvService); const cronDb = cronService.getDb(); cronDb.count({}, async (err, count) => { @@ -97,7 +97,7 @@ export default async () => { // 初始化保存一次ck和定时任务数据 await cronService.autosave_crontab(); - await cookieService.set_cookies(); + await envService.set_envs(); }; function randomSchedule(from: number, to: number) { diff --git a/back/services/cookie.ts b/back/services/cookie.ts deleted file mode 100644 index 016b4052..00000000 --- a/back/services/cookie.ts +++ /dev/null @@ -1,333 +0,0 @@ -import { Service, Inject } from 'typedi'; -import winston from 'winston'; -import fetch from 'node-fetch'; -import { getFileContentByName } from '../config/util'; -import config from '../config'; -import * as fs from 'fs'; -import got from 'got'; -import DataStore from 'nedb'; -import { Cookie, CookieStatus, initCookiePosition } from '../data/cookie'; - -@Service() -export default class CookieService { - private cronDb = new DataStore({ filename: config.cookieDbFile }); - constructor(@Inject('logger') private logger: winston.Logger) { - this.cronDb.loadDatabase((err) => { - if (err) throw err; - }); - } - - public async getCookies() { - const content = getFileContentByName(config.cookieFile); - return this.formatCookie(content.split('\n').filter((x) => !!x)); - } - - public async addCookie(cookies: string[]) { - let content = getFileContentByName(config.cookieFile); - const originCookies = content.split('\n').filter((x) => !!x); - const result = originCookies.concat(cookies); - fs.writeFileSync(config.cookieFile, result.join('\n')); - return ''; - } - - public async updateCookie({ cookie, oldCookie }) { - let content = getFileContentByName(config.cookieFile); - const cookies = content.split('\n'); - const index = cookies.findIndex((x) => x === oldCookie); - if (index !== -1) { - cookies[index] = cookie; - fs.writeFileSync(config.cookieFile, cookies.join('\n')); - return ''; - } else { - return '未找到要原有Cookie'; - } - } - - public async deleteCookie(cookie: string) { - let content = getFileContentByName(config.cookieFile); - const cookies = content.split('\n'); - const index = cookies.findIndex((x) => x === cookie); - if (index !== -1) { - cookies.splice(index, 1); - fs.writeFileSync(config.cookieFile, cookies.join('\n')); - return ''; - } else { - return '未找到要删除的Cookie'; - } - } - - private async formatCookie(data: any[]) { - const result = []; - for (const x of data) { - const { nickname, status } = await this.getJdInfo(x); - if (/pt_pin=(.+?);/.test(x)) { - result.push({ - pin: x.match(/pt_pin=(.+?);/)[1], - cookie: x, - status, - nickname: nickname, - }); - } else { - result.push({ - pin: 'pin未匹配到', - cookie: x, - status, - nickname: nickname, - }); - } - } - return result; - } - - public async refreshCookie(_id: string) { - const current = await this.get(_id); - const { status, nickname } = await this.getJdInfo(current.value); - return { - ...current, - status, - nickname, - }; - } - - private getJdInfo(cookie: string) { - return fetch( - `https://me-api.jd.com/user_new/info/GetJDUserInfoUnion?orgFlag=JD_PinGou_New&callSource=mainorder&channel=4&isHomewhite=0&sceneval=2&_=${Date.now()}&sceneval=2&g_login_type=1&g_ty=ls`, - { - method: 'get', - headers: { - Accept: '*/*', - 'Accept-Encoding': 'gzip, deflate, br', - 'Accept-Language': 'zh-cn', - Connection: 'keep-alive', - Cookie: cookie, - Referer: 'https://home.m.jd.com/myJd/newhome.action', - 'User-Agent': - 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1', - Host: 'me-api.jd.com', - }, - }, - ) - .then((x) => x.json()) - .then((x) => { - if (x.retcode === '0' && x.data && x.data.userInfo) { - return { - nickname: x.data.userInfo.baseInfo.nickname, - status: CookieStatus.normal, - }; - } else if (x.retcode === 13) { - return { status: CookieStatus.invalid, nickname: '-' }; - } - return { status: CookieStatus.abnormal, nickname: '-' }; - }); - } - - private async formatCookies(cookies: Cookie[]) { - const result = []; - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i]; - if (cookie.status !== CookieStatus.disabled) { - const { status, nickname } = await this.getJdInfo(cookie.value); - result.push({ ...cookie, status, nickname }); - } else { - result.push({ ...cookie, nickname: '-' }); - } - } - return result; - } - - public async create(payload: string[]): Promise { - const cookies = await this.cookies(); - let position = initCookiePosition; - if (cookies && cookies.length > 0) { - position = cookies[cookies.length - 1].position; - } - const tabs = payload.map((x) => { - const cookie = new Cookie({ value: x, position }); - position = position / 2; - cookie.position = position; - return cookie; - }); - const docs = await this.insert(tabs); - await this.set_cookies(); - return await this.formatCookies(docs); - } - - public async insert(payload: Cookie[]): Promise { - return new Promise((resolve) => { - this.cronDb.insert(payload, (err, docs) => { - if (err) { - this.logger.error(err); - } else { - resolve(docs); - } - }); - }); - } - - public async update(payload: Cookie): Promise { - const { _id, ...other } = payload; - const doc = await this.get(_id); - const tab = new Cookie({ ...doc, ...other }); - const newDoc = await this.updateDb(tab); - await this.set_cookies(); - const [newCookie] = await this.formatCookies([newDoc]); - return newCookie; - } - - private async updateDb(payload: Cookie): Promise { - return new Promise((resolve) => { - this.cronDb.update( - { _id: payload._id }, - payload, - { returnUpdatedDocs: true }, - (err, num, doc) => { - if (err) { - this.logger.error(err); - } else { - resolve(doc as Cookie); - } - }, - ); - }); - } - - public async remove(ids: string[]) { - return new Promise((resolve: any) => { - this.cronDb.remove( - { _id: { $in: ids } }, - { multi: true }, - async (err) => { - await this.set_cookies(); - resolve(); - }, - ); - }); - } - - public async move( - _id: string, - { - fromIndex, - toIndex, - }: { - fromIndex: number; - toIndex: number; - }, - ) { - let targetPosition: number; - const isUpward = fromIndex > toIndex; - const cookies = await this.cookies(); - if (toIndex === 0 || toIndex === cookies.length - 1) { - targetPosition = isUpward - ? cookies[0].position * 2 - : cookies[toIndex].position / 2; - } else { - targetPosition = isUpward - ? (cookies[toIndex].position + cookies[toIndex - 1].position) / 2 - : (cookies[toIndex].position + cookies[toIndex + 1].position) / 2; - } - this.update({ - _id, - position: targetPosition, - }); - await this.set_cookies(); - } - - public async cookies( - searchText?: string, - sort: any = { position: -1 }, - needDetail: boolean = false, - ): Promise { - let query = {}; - if (searchText) { - const reg = new RegExp(searchText); - query = { - $or: [ - { - name: reg, - }, - { - command: reg, - }, - ], - }; - } - const newDocs = await this.find(query, sort); - if (needDetail) { - return await this.formatCookies(newDocs); - } else { - return newDocs; - } - } - - private async find(query: any, sort: any): Promise { - return new Promise((resolve) => { - this.cronDb - .find(query) - .sort({ ...sort }) - .exec((err, docs) => { - resolve(docs); - }); - }); - } - - public async get(_id: string): Promise { - return new Promise((resolve) => { - this.cronDb.find({ _id }).exec((err, docs) => { - resolve(docs[0]); - }); - }); - } - - public async getBySort(sort: any): Promise { - return new Promise((resolve) => { - this.cronDb - .find({}) - .sort({ ...sort }) - .limit(1) - .exec((err, docs) => { - resolve(docs[0]); - }); - }); - } - - public async disabled(ids: string[]) { - return new Promise((resolve: any) => { - this.cronDb.update( - { _id: { $in: ids } }, - { $set: { status: CookieStatus.disabled } }, - { multi: true }, - async (err) => { - await this.set_cookies(); - resolve(); - }, - ); - }); - } - - public async enabled(ids: string[]) { - return new Promise((resolve: any) => { - this.cronDb.update( - { _id: { $in: ids } }, - { $set: { status: CookieStatus.noacquired } }, - { multi: true }, - async (err, num) => { - await this.set_cookies(); - resolve(); - }, - ); - }); - } - - public async set_cookies() { - const cookies = await this.cookies(); - let cookie_string = ''; - cookies.forEach((tab) => { - if (tab.status !== CookieStatus.disabled) { - cookie_string += tab.value; - cookie_string += '\n'; - } - }); - fs.writeFileSync(config.cookieFile, cookie_string); - } -} diff --git a/back/services/env.ts b/back/services/env.ts new file mode 100644 index 00000000..16ffd178 --- /dev/null +++ b/back/services/env.ts @@ -0,0 +1,227 @@ +import { Service, Inject } from 'typedi'; +import winston from 'winston'; +import { getFileContentByName } from '../config/util'; +import config from '../config'; +import * as fs from 'fs'; +import DataStore from 'nedb'; +import { Env, EnvStatus, initEnvPosition } from '../data/env'; +import _ from 'lodash'; + +@Service() +export default class EnvService { + private cronDb = new DataStore({ filename: config.envDbFile }); + constructor(@Inject('logger') private logger: winston.Logger) { + this.cronDb.loadDatabase((err) => { + if (err) throw err; + }); + } + + public async create(payload: Env): Promise { + const envs = await this.envs(); + let position = initEnvPosition; + if (envs && envs.length > 0) { + position = envs[envs.length - 1].position; + } + const tab = new Env({ ...payload, position: position / 2 }); + const doc = await this.insert(tab); + await this.set_envs(); + return doc; + } + + public async insert(payload: Env): Promise { + return new Promise((resolve) => { + this.cronDb.insert(payload, (err, doc) => { + if (err) { + this.logger.error(err); + } else { + resolve(doc); + } + }); + }); + } + + public async update(payload: Env): Promise { + const { _id, ...other } = payload; + const doc = await this.get(_id); + const tab = new Env({ ...doc, ...other }); + const newDoc = await this.updateDb(tab); + await this.set_envs(); + return newDoc; + } + + private async updateDb(payload: Env): Promise { + return new Promise((resolve) => { + this.cronDb.update( + { _id: payload._id }, + payload, + { returnUpdatedDocs: true }, + (err, num, doc) => { + if (err) { + this.logger.error(err); + } else { + resolve(doc as Env); + } + }, + ); + }); + } + + public async remove(ids: string[]) { + return new Promise((resolve: any) => { + this.cronDb.remove( + { _id: { $in: ids } }, + { multi: true }, + async (err) => { + await this.set_envs(); + resolve(); + }, + ); + }); + } + + public async move( + _id: string, + { + fromIndex, + toIndex, + }: { + fromIndex: number; + toIndex: number; + }, + ) { + let targetPosition: number; + const isUpward = fromIndex > toIndex; + const envs = await this.envs(); + if (toIndex === 0 || toIndex === envs.length - 1) { + targetPosition = isUpward + ? envs[0].position * 2 + : envs[toIndex].position / 2; + } else { + targetPosition = isUpward + ? (envs[toIndex].position + envs[toIndex - 1].position) / 2 + : (envs[toIndex].position + envs[toIndex + 1].position) / 2; + } + this.update({ + _id, + position: targetPosition, + }); + await this.set_envs(); + } + + public async envs( + searchText?: string, + sort: any = { position: -1 }, + query: any = {}, + ): Promise { + let condition = { ...query }; + if (searchText) { + const reg = new RegExp(searchText); + condition = { + $or: [ + { + value: reg, + }, + { + name: reg, + }, + { + remarks: reg, + }, + ], + }; + } + const newDocs = await this.find(condition, sort); + return newDocs; + } + + private async find(query: any, sort: any): Promise { + return new Promise((resolve) => { + this.cronDb + .find(query) + .sort({ ...sort }) + .exec((err, docs) => { + resolve(docs); + }); + }); + } + + public async get(_id: string): Promise { + return new Promise((resolve) => { + this.cronDb.find({ _id }).exec((err, docs) => { + resolve(docs[0]); + }); + }); + } + + public async getBySort(sort: any): Promise { + return new Promise((resolve) => { + this.cronDb + .find({}) + .sort({ ...sort }) + .limit(1) + .exec((err, docs) => { + resolve(docs[0]); + }); + }); + } + + public async disabled(ids: string[]) { + return new Promise((resolve: any) => { + this.cronDb.update( + { _id: { $in: ids } }, + { $set: { status: EnvStatus.disabled } }, + { multi: true }, + async (err) => { + await this.set_envs(); + resolve(); + }, + ); + }); + } + + public async enabled(ids: string[]) { + return new Promise((resolve: any) => { + this.cronDb.update( + { _id: { $in: ids } }, + { $set: { status: EnvStatus.noacquired } }, + { multi: true }, + async (err, num) => { + await this.set_envs(); + resolve(); + }, + ); + }); + } + + public async updateNames({ ids, name }: { ids: string[]; name: string }) { + return new Promise((resolve: any) => { + this.cronDb.update( + { _id: { $in: ids } }, + { $set: { name } }, + { multi: true }, + async (err, num) => { + await this.set_envs(); + resolve(); + }, + ); + }); + } + + public async set_envs() { + const envs = await this.envs( + '', + { position: -1 }, + { status: { $ne: EnvStatus.disabled }, name: { $exists: true } }, + ); + const groups = _.groupBy(envs, 'name'); + console.log(groups); + let env_string = ''; + for (const key in groups) { + if (Object.prototype.hasOwnProperty.call(groups, key)) { + const group = groups[key]; + env_string += `export ${key}="${_.map(group, 'value').join('&')}"\n`; + } + } + fs.writeFileSync(config.envFile, env_string); + } +} diff --git a/package.json b/package.json index f07b5b22..57de9a44 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "dotenv": "^8.2.0", "express": "^4.17.1", "express-jwt": "^6.0.0", - "got": "^11.8.2", "jsonwebtoken": "^8.5.1", "nedb": "^1.8.0", "node-fetch": "^2.6.1", @@ -42,12 +41,13 @@ "winston": "^3.3.3" }, "devDependencies": { - "@ant-design/pro-layout": "^6.5.0", "@ant-design/icons": "^4.6.2", + "@ant-design/pro-layout": "^6.5.0", "@types/cors": "^2.8.10", "@types/express": "^4.17.8", "@types/express-jwt": "^6.0.1", "@types/jsonwebtoken": "^8.5.0", + "@types/lodash": "^4.14.170", "@types/nedb": "^1.8.11", "@types/node": "^14.11.2", "@types/node-fetch": "^2.5.8", @@ -60,6 +60,7 @@ "compression-webpack-plugin": "6.1.1", "darkreader": "^4.9.27", "lint-staged": "^10.0.7", + "lodash": "^4.17.21", "nodemon": "^2.0.4", "prettier": "^2.2.0", "qrcode.react": "^1.0.1", diff --git a/sample/config.sample.sh b/sample/config.sample.sh index 5766d3c9..4d2bbc1b 100644 --- a/sample/config.sample.sh +++ b/sample/config.sample.sh @@ -29,16 +29,6 @@ RandomDelay="300" ## 如果你自己会写shell脚本,并且希望在每次运行 ql update 命令时,额外运行你的 shell 脚本,请赋值为 "true",默认为true EnableExtraShell="true" -## 自动按顺序进行账号间互助(选填) 设置为 true 时,将直接导入code最新日志来进行互助 -AutoHelpOther="" - -## 定义 jcode 脚本导出的互助码模板样式(选填) -## 不填 使用“按编号顺序助力模板”,Cookie编号在前的优先助力 -## 填 0 使用“全部一致助力模板”,所有账户要助力的码全部一致 -## 填 1 使用“均等机会助力模板”,所有账户获得助力次数一致 -## 填 2 使用“随机顺序助力模板”,本套脚本内账号间随机顺序助力,每次生成的顺序都不一致。 -HelpType="" - ## 是否自动启动bot,默认不启动,设置为true时自动启动,目前需要自行克隆bot仓库所需代码,存到ql/repo目录下,文件夹命名为dockerbot AutoStartBot="" @@ -117,94 +107,4 @@ export GOBOT_URL="" export GOBOT_TOKEN="" export GOBOT_QQ="" -## 如果只是想要屏蔽某个ck不执行某个脚本,可以参考下面 case 这个命令的例子来控制,脚本名称包含后缀 -## case $1 in -## test.js) -## TempBlockCookie="5" -## ;; -## esac - -## 需组合的环境变量列表,env_name需要和var_name一一对应,如何有新活动按照格式添加(不懂勿动) -env_name=( - JD_COOKIE - FRUITSHARECODES - PETSHARECODES - PLANT_BEAN_SHARECODES - DREAM_FACTORY_SHARE_CODES - DDFACTORY_SHARECODES - JDZZ_SHARECODES - JDJOY_SHARECODES - JXNC_SHARECODES - BOOKSHOP_SHARECODES - JD_CASH_SHARECODES - JDSGMH_SHARECODES - JDCFD_SHARECODES - JDHEALTH_SHARECODES -) -var_name=( - Cookie - ForOtherFruit - ForOtherPet - ForOtherBean - ForOtherDreamFactory - ForOtherJdFactory - ForOtherJdzz - ForOtherJoy - ForOtherJxnc - ForOtherBookShop - ForOtherCash - ForOtherSgmh - ForOtherCfd - ForOtherHealth -) - -## name_js为脚本文件名,如果使用ql repo命令拉取,文件名含有作者名 -## 所有有互助码的活动,把脚本名称列在 name_js 中,对应 config.sh 中互助码后缀列在 name_config 中,中文名称列在 name_chinese 中。 -## name_js、name_config 和 name_chinese 中的三个名称必须一一对应。 -name_js=( - jd_fruit - jd_pet - jd_plantBean - jd_dreamFactory - jd_jdfactory - jd_jdzz - jd_crazy_joy - jd_jxnc - jd_bookshop - jd_cash - jd_sgmh - jd_cfd - jd_health -) -name_config=( - Fruit - Pet - Bean - DreamFactory - JdFactory - Jdzz - Joy - Jxnc - BookShop - Cash - Sgmh - Cfd - Health -) -name_chinese=( - 东东农场 - 东东萌宠 - 京东种豆得豆 - 京喜工厂 - 东东工厂 - 京东赚赚 - crazyJoy任务 - 京喜农场 - 口袋书店 - 签到领现金 - 闪购盲盒 - 京喜财富岛 - 东东健康社区 -) - ## 其他需要的变量,脚本中需要的变量使用 export 变量名= 声明即可 diff --git a/sample/package.json b/sample/package.json index b80b416a..6f56bc9f 100644 --- a/sample/package.json +++ b/sample/package.json @@ -5,7 +5,6 @@ "dependencies": { "crypto-js": "^4.0.0", "download": "^8.0.0", - "got": "^11.5.1", "http-server": "^0.12.3", "qrcode-terminal": "^0.12.0", "request": "^2.88.2", diff --git a/shell/code.sh b/shell/code.sh deleted file mode 100755 index 474ff259..00000000 --- a/shell/code.sh +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env bash - -## 导入通用变量与函数 -dir_shell=/ql/shell -. $dir_shell/share.sh - -## 生成pt_pin清单 -gen_pt_pin_array() { - local tmp1 tmp2 i pt_pin_temp - for ((user_num = 1; user_num <= $user_sum; user_num++)); do - tmp1=Cookie$user_num - tmp2=${!tmp1} - i=$(($user_num - 1)) - pt_pin_temp=$(echo $tmp2 | perl -pe "{s|.*pt_pin=([^; ]+)(?=;?).*|\1|; s|%|\\\x|g}") - [[ $pt_pin_temp == *\\x* ]] && pt_pin[i]=$(printf $pt_pin_temp) || pt_pin[i]=$pt_pin_temp - done -} - -## 导出互助码的通用程序,$1:去掉后缀的脚本名称,$2:config.sh中的后缀,$3:活动中文名称 -export_codes_sub() { - local task_name=$1 - local config_name=$2 - local chinese_name=$3 - local config_name_my=My$config_name - local config_name_for_other=ForOther$config_name - local i j k m n pt_pin_in_log code tmp_grep tmp_my_code tmp_for_other user_num random_num_list - if cd $dir_log/$task_name &>/dev/null && [[ $(ls) ]]; then - ## 寻找所有互助码以及对应的pt_pin - i=0 - pt_pin_in_log=() - code=() - pt_pin_and_code=$(ls -r *.log | xargs awk -v var="的$chinese_name好友互助码" 'BEGIN{FS="[( )】]+"; OFS="&"} $3~var {print $2,$4}') - for line in $pt_pin_and_code; do - pt_pin_in_log[i]=$(echo $line | awk -F "&" '{print $1}') - code[i]=$(echo $line | awk -F "&" '{print $2}') - let i++ - done - - ## 输出My系列变量 - if [[ ${#code[*]} -gt 0 ]]; then - for ((m = 0; m < ${#pt_pin[*]}; m++)); do - tmp_my_code="" - j=$((m + 1)) - for ((n = 0; n < ${#code[*]}; n++)); do - if [[ ${pt_pin[m]} == ${pt_pin_in_log[n]} ]]; then - tmp_my_code=${code[n]} - break - fi - done - echo "$config_name_my$j='$tmp_my_code'" - done - else - echo "## 从日志中未找到任何互助码" - fi - - ## 输出ForOther系列变量 - if [[ ${#code[*]} -gt 0 ]]; then - echo - case $HelpType in - 0) ## 全部一致 - tmp_for_other="" - for ((m = 0; m < ${#pt_pin[*]}; m++)); do - j=$((m + 1)) - tmp_for_other="$tmp_for_other@\${$config_name_my$j}" - done - echo "${config_name_for_other}1=\"$tmp_for_other\"" | perl -pe "s|($config_name_for_other\d+=\")@|\1|" - for ((m = 1; m < ${#pt_pin[*]}; m++)); do - j=$((m + 1)) - echo "$config_name_for_other$j=\"\${${config_name_for_other}1}\"" - done - ;; - - 1) ## 均等助力 - for ((m = 0; m < ${#pt_pin[*]}; m++)); do - tmp_for_other="" - j=$((m + 1)) - for ((n = $m; n < $(($user_sum + $m)); n++)); do - [[ $m -eq $n ]] && continue - if [[ $((n + 1)) -le $user_sum ]]; then - k=$((n + 1)) - else - k=$((n + 1 - $user_sum)) - fi - tmp_for_other="$tmp_for_other@\${$config_name_my$k}" - done - echo "$config_name_for_other$j=\"$tmp_for_other\"" | perl -pe "s|($config_name_for_other\d+=\")@|\1|" - done - ;; - - 2) ## 本套脚本内账号间随机顺序助力 - for ((m = 0; m < ${#pt_pin[*]}; m++)); do - tmp_for_other="" - random_num_list=$(seq $user_sum | sort -R) - j=$((m + 1)) - for n in $random_num_list; do - [[ $j -eq $n ]] && continue - tmp_for_other="$tmp_for_other@\${$config_name_my$n}" - done - echo "$config_name_for_other$j=\"$tmp_for_other\"" | perl -pe "s|($config_name_for_other\d+=\")@|\1|" - done - ;; - - *) ## 按编号优先 - for ((m = 0; m < ${#pt_pin[*]}; m++)); do - tmp_for_other="" - j=$((m + 1)) - for ((n = 0; n < ${#pt_pin[*]}; n++)); do - [[ $m -eq $n ]] && continue - k=$((n + 1)) - tmp_for_other="$tmp_for_other@\${$config_name_my$k}" - done - echo "$config_name_for_other$j=\"$tmp_for_other\"" | perl -pe "s|($config_name_for_other\d+=\")@|\1|" - done - ;; - esac - fi - else - echo "## 未运行过 $task_name.js 脚本,未产生日志" - fi -} - -## 汇总输出 -export_all_codes() { - gen_pt_pin_array - echo -e "\n# 从日志提取互助码,编号和配置文件中Cookie编号完全对应,如果为空就是所有日志中都没有。\n\n# 即使某个MyXxx变量未赋值,也可以将其变量名填在ForOtherXxx中,jtask脚本会自动过滤空值。\n" - echo -n "# 你选择的互助码模板为:" - case $HelpType in - 0) - echo "所有账号助力码全部一致。" - ;; - 1) - echo "所有账号机会均等助力。" - ;; - 2) - echo "本套脚本内账号间随机顺序助力。" - ;; - *) - echo "按账号编号优先。" - ;; - esac - for ((i = 0; i < ${#name_js[*]}; i++)); do - echo -e "\n## ${name_chinese[i]}:" - export_codes_sub "${name_js[i]}" "${name_config[i]}" "${name_chinese[i]}" - done -} - -## 执行并写入日志 -log_time=$(date "+%Y-%m-%d-%H-%M-%S") -log_path="$dir_code/$log_time.log" -make_dir "$dir_code" -export_all_codes | perl -pe "{s|京东种豆|种豆|; s|crazyJoy任务|疯狂的JOY|}" | tee $log_path diff --git a/shell/share.sh b/shell/share.sh index 28ca2c55..3f4bf0c0 100755 --- a/shell/share.sh +++ b/shell/share.sh @@ -18,7 +18,7 @@ ql_static_repo=$dir_repo/static ## 文件 file_config_sample=$dir_sample/config.sample.sh -file_cookie=$dir_config/cookie.sh +file_env=$dir_config/env.sh file_sharecode=$dir_config/sharecode.sh file_config_user=$dir_config/config.sh file_auth_sample=$dir_sample/auth.sample.json @@ -53,15 +53,9 @@ original_name=( ## 导入配置文件 import_config() { [ -f $file_config_user ] && . $file_config_user - user_sum=0 - for line in $(cat $file_cookie); do - let user_sum+=1 - eval Cookie${user_sum}="\"${line}\"" - done command_timeout_time=${CommandTimeoutTime:-"1h"} github_proxy_url=${GithubProxyUrl:-""} - block_cookie=${TempBlockCookie:-""} file_extensions=${RepoFileExtensions:-"js py"} } @@ -161,9 +155,9 @@ fix_config() { echo fi - if [ ! -f $file_cookie ]; then - echo -e "检测到config配置目录下不存在cookie.sh,创建一个空文件用于初始化...\n" - touch $file_cookie + if [ ! -f $file_env ]; then + echo -e "检测到config配置目录下不存在env.sh,创建一个空文件用于初始化...\n" + touch $file_env echo fi @@ -289,13 +283,8 @@ git_pull_scripts() { cd $dir_current } -init_env() { - TempBlockCookie="" -} - ## 导入配置文件,检测平台,创建软连接,识别命令,修复配置文件 detect_termux detect_macos define_cmd -init_env import_config $1 diff --git a/shell/task.sh b/shell/task.sh index 0c181557..91c35dad 100755 --- a/shell/task.sh +++ b/shell/task.sh @@ -5,41 +5,6 @@ dir_shell=/ql/shell . $dir_shell/share.sh . $dir_shell/api.sh -## 组合Cookie和互助码子程序,$1:要组合的内容 -combine_sub() { - local what_combine=$1 - local combined_all="" - local tmp1 tmp2 - for ((i = 1; i <= $user_sum; i++)); do - for num in $block_cookie; do - [[ $i -eq $num ]] && continue 2 - done - local tmp1=$what_combine$i - local tmp2=${!tmp1} - combined_all="$combined_all&$tmp2" - done - echo $combined_all | perl -pe "{s|^&||; s|^@+||; s|&@|&|g; s|@+&|&|g; s|@+|@|g; s|@+$||}" -} - -## 正常依次运行时,组合所有账号的Cookie与互助码 -combine_all() { - for ((i = 0; i < ${#env_name[*]}; i++)); do - result=$(combine_sub ${var_name[i]}) - if [[ $result ]]; then - export ${env_name[i]}="$result" - fi - done -} - -## 并发运行时,直接申明每个账号的Cookie与互助码,$1:用户Cookie编号 -combine_one() { - local user_num=$1 - for ((i = 0; i < ${#env_name[*]}; i++)); do - local tmp=${var_name[i]}$user_num - export ${env_name[i]}=${!tmp} - done -} - ## 选择python3还是node define_program() { local p1=$1 @@ -118,7 +83,6 @@ run_normal() { random_delay fi fi - combine_all log_time=$(date "+%Y-%m-%d-%H-%M-%S") log_dir_tmp="${p1##*/}" log_dir="$dir_log/${log_dir_tmp%%.*}" @@ -131,18 +95,20 @@ run_normal() { update_cron_status "\"$id\"" "1" } -## 并发执行,因为是并发,所以日志只能直接记录在日志文件中(日志文件以Cookie编号结尾),前台执行并发跑时不会输出日志 ## 并发执行时,设定的 RandomDelay 不会生效,即所有任务立即执行 run_concurrent() { local p1=$1 + local p3=$3 + local envs=$(eval echo "\$${p3}") + local array=(${envs//&/}) cd $dir_scripts define_program "$p1" log_dir="$dir_log/${p1%%.*}" make_dir $log_dir log_time=$(date "+%Y-%m-%d-%H-%M-%S.%N") echo -e "\n各账号间已经在后台开始并发执行,前台不输入日志,日志直接写入文件中。\n" - for ((user_num = 1; user_num <= $user_sum; user_num++)); do - combine_one $user_num + for i in "${!array[@]}"; do + export ${p3}=${array[i]} log_path="$log_dir/${log_time}_${user_num}.log" timeout $command_timeout_time $which_program $p1 &>$log_path & done @@ -173,7 +139,7 @@ main() { run_normal $1 $2 ;; conc) - run_concurrent $1 $2 + run_concurrent $1 $2 $3 ;; *) run_else "$@" diff --git a/src/app.tsx b/src/app.tsx index a726a792..71ebfbb2 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -6,9 +6,9 @@ const titleMap: any = { '/': '控制面板', '/login': '登录', '/crontab': '定时任务', - '/cookie': 'Session管理', + '/env': '环境变量', '/config': '配置文件', - '/diy': '自定义脚本', + '/script': '查看脚本', '/diff': '对比工具', '/log': '日志', '/setting': '系统设置', diff --git a/src/layouts/defaultProps.tsx b/src/layouts/defaultProps.tsx index 547d6988..f2019f18 100644 --- a/src/layouts/defaultProps.tsx +++ b/src/layouts/defaultProps.tsx @@ -25,10 +25,10 @@ export default { component: '@/pages/crontab/index', }, { - path: '/cookie', - name: 'Session管理', + path: '/env', + name: '环境变量', icon: , - component: '@/pages/cookie/index', + component: '@/pages/env/index', }, { path: '/config', @@ -37,10 +37,10 @@ export default { component: '@/pages/config/index', }, { - path: '/diy', - name: '自定义脚本', + path: '/script', + name: '查看脚本', icon: , - component: '@/pages/diy/index', + component: '@/pages/script/index', }, { path: '/diff', diff --git a/src/layouts/index.less b/src/layouts/index.less index 8d364b9b..1804cb2a 100644 --- a/src/layouts/index.less +++ b/src/layouts/index.less @@ -36,7 +36,7 @@ body { } } -.session-wrapper { +.env-wrapper { th { white-space: nowrap; } @@ -51,6 +51,15 @@ body { } } +.config-wrapper { + .config-select { + width: 250px; + } + .ant-page-header-heading-left { + min-width: 100px; + } +} + @media (max-width: 768px) { .ant-pro-grid-content.wide { .ant-pro-page-container-children-content { diff --git a/src/pages/config/index.tsx b/src/pages/config/index.tsx index a0842325..438438da 100644 --- a/src/pages/config/index.tsx +++ b/src/pages/config/index.tsx @@ -1,5 +1,5 @@ import React, { PureComponent, Fragment, useState, useEffect } from 'react'; -import { Button, message, Modal } from 'antd'; +import { Button, message, Modal, TreeSelect } from 'antd'; import config from '@/utils/config'; import { PageContainer } from '@ant-design/pro-layout'; import { Controlled as CodeMirror } from 'react-codemirror2'; @@ -11,27 +11,42 @@ const Config = () => { const [marginTop, setMarginTop] = useState(-72); const [value, setValue] = useState(''); const [loading, setLoading] = useState(true); + const [title, setTitle] = useState('config.sh'); + const [select, setSelect] = useState('config.sh'); + const [data, setData] = useState([]); - const getConfig = () => { + const getConfig = (name: string) => { + request.get(`${config.apiPrefix}configs/${name}`).then((data: any) => { + setValue(data.data); + }); + }; + + const getFiles = () => { setLoading(true); request - .get(`${config.apiPrefix}config/config`) + .get(`${config.apiPrefix}configs/files`) .then((data: any) => { - setValue(data.data); + setData(data.data); }) .finally(() => setLoading(false)); }; const updateConfig = () => { request - .post(`${config.apiPrefix}save`, { - data: { content: value, name: 'config.sh' }, + .post(`${config.apiPrefix}configs/save`, { + data: { content: value, name: select }, }) .then((data: any) => { message.success(data.msg); }); }; + const onSelect = (value: any, node: any) => { + setSelect(value); + setTitle(node.value); + getConfig(node.value); + }; + useEffect(() => { if (document.body.clientWidth < 768) { setWidth('auto'); @@ -42,14 +57,24 @@ const Config = () => { setMarginLeft(0); setMarginTop(-72); } - getConfig(); + getFiles(); + getConfig('config.sh'); }, []); return ( , , diff --git a/src/pages/diff/index.tsx b/src/pages/diff/index.tsx index 6012a695..12b06a96 100644 --- a/src/pages/diff/index.tsx +++ b/src/pages/diff/index.tsx @@ -15,7 +15,7 @@ const Crontab = () => { const [loading, setLoading] = useState(true); const getConfig = () => { - request.get(`${config.apiPrefix}config/config`).then((data) => { + request.get(`${config.apiPrefix}configs/config.sh`).then((data) => { setValue(data.data); }); }; @@ -23,7 +23,7 @@ const Crontab = () => { const getSample = () => { setLoading(true); request - .get(`${config.apiPrefix}config/sample`) + .get(`${config.apiPrefix}config/config.sample.sh`) .then((data) => { setSample(data.data); }) diff --git a/src/pages/diy/index.less b/src/pages/diy/index.less deleted file mode 100644 index e69de29b..00000000 diff --git a/src/pages/diy/index.tsx b/src/pages/diy/index.tsx deleted file mode 100644 index a110a01e..00000000 --- a/src/pages/diy/index.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, { PureComponent, Fragment, useState, useEffect } from 'react'; -import { Button, message, Modal } from 'antd'; -import config from '@/utils/config'; -import { PageContainer } from '@ant-design/pro-layout'; -import { Controlled as CodeMirror } from 'react-codemirror2'; -import { request } from '@/utils/http'; - -const Crontab = () => { - const [width, setWidth] = useState('100%'); - const [marginLeft, setMarginLeft] = useState(0); - const [marginTop, setMarginTop] = useState(-72); - const [value, setValue] = useState(''); - const [loading, setLoading] = useState(true); - - const getConfig = () => { - setLoading(true); - request - .get(`${config.apiPrefix}config/extra`) - .then((data) => { - setValue(data.data); - }) - .finally(() => setLoading(false)); - }; - - const updateConfig = () => { - request - .post(`${config.apiPrefix}save`, { - data: { content: value, name: 'extra.sh' }, - }) - .then((data) => { - message.success(data.msg); - }); - }; - - useEffect(() => { - if (document.body.clientWidth < 768) { - setWidth('auto'); - setMarginLeft(0); - setMarginTop(0); - } else { - setWidth('100%'); - setMarginLeft(0); - setMarginTop(-72); - } - getConfig(); - }, []); - - return ( - - 保存 - , - ]} - header={{ - style: { - padding: '4px 16px 4px 15px', - position: 'sticky', - top: 0, - left: 0, - zIndex: 20, - marginTop, - width, - marginLeft, - }, - }} - > - { - setValue(value); - }} - onChange={(editor, data, value) => {}} - /> - - ); -}; - -export default Crontab; diff --git a/src/pages/env/editNameModal.tsx b/src/pages/env/editNameModal.tsx new file mode 100644 index 00000000..1affc835 --- /dev/null +++ b/src/pages/env/editNameModal.tsx @@ -0,0 +1,77 @@ +import React, { useEffect, useState } from 'react'; +import { Modal, message, Input, Form } from 'antd'; +import { request } from '@/utils/http'; +import config from '@/utils/config'; + +const EditNameModal = ({ + ids, + handleCancel, + visible, +}: { + ids?: string[]; + visible: boolean; + handleCancel: () => void; +}) => { + const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); + + const handleOk = async (values: any) => { + console.log(values); + console.log(ids); + setLoading(true); + const { code, data } = await request.put(`${config.apiPrefix}envs/name`, { + data: { + ids, + name: values.name, + }, + }); + if (code === 200) { + message.success('更新环境变量名称成功'); + } else { + message.error(data); + } + setLoading(false); + handleCancel(); + }; + + useEffect(() => { + form.resetFields(); + }, [ids]); + + return ( + { + form + .validateFields() + .then((values) => { + handleOk(values); + }) + .catch((info) => { + console.log('Validate Failed:', info); + }); + }} + onCancel={() => handleCancel()} + confirmLoading={loading} + destroyOnClose + > +
+ + + +
+
+ ); +}; + +export default EditNameModal; diff --git a/src/pages/cookie/index.less b/src/pages/env/index.less similarity index 100% rename from src/pages/cookie/index.less rename to src/pages/env/index.less diff --git a/src/pages/cookie/index.tsx b/src/pages/env/index.tsx similarity index 74% rename from src/pages/cookie/index.tsx rename to src/pages/env/index.tsx index 11c59458..27134f66 100644 --- a/src/pages/cookie/index.tsx +++ b/src/pages/env/index.tsx @@ -19,8 +19,8 @@ import { import config from '@/utils/config'; import { PageContainer } from '@ant-design/pro-layout'; import { request } from '@/utils/http'; -import QRCode from 'qrcode.react'; -import CookieModal from './modal'; +import EnvModal from './modal'; +import EditNameModal from './editNameModal'; import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import './index.less'; @@ -28,17 +28,12 @@ import './index.less'; const { Text } = Typography; enum Status { - '未获取', - '正常', + '已启用', '已禁用', - '已失效', - '状态异常', } enum StatusColor { - 'default', 'success', - 'warning', 'error', } @@ -106,7 +101,7 @@ const DragableBodyRow = ({ ); }; -const Config = () => { +const Env = () => { const columns = [ { title: '序号', @@ -116,25 +111,17 @@ const Config = () => { }, }, { - title: '昵称', - dataIndex: 'nickname', - key: 'nickname', + title: '名称', + dataIndex: 'name', + key: 'name', align: 'center' as const, - width: '15%', - render: (text: string, record: any, index: number) => { - const match = record.value.match(/pt_pin=([^; ]+)(?=;?)/); - const val = (match && match[1]) || '未匹配用户名'; - return ( - {record.nickname || val} - ); - }, }, { title: '值', dataIndex: 'value', key: 'value', align: 'center' as const, - width: '50%', + width: '45%', render: (text: string, record: any) => { return ( { display: 'inline-block', wordBreak: 'break-all', cursor: 'text', + width: '100%', }} > {text} @@ -150,28 +138,24 @@ const Config = () => { ); }, }, + { + title: '备注', + dataIndex: 'remarks', + key: 'remarks', + align: 'center' as const, + }, { title: '状态', key: 'status', dataIndex: 'status', align: 'center' as const, - width: '15%', + width: 60, render: (text: string, record: any, index: number) => { return ( - + {Status[record.status]} - {record.status !== Status.已禁用 && ( - - refreshStatus(record, index)}> - - - - )} ); }, @@ -183,12 +167,12 @@ const Config = () => { render: (text: string, record: any, index: number) => ( - editCookie(record, index)}> + editEnv(record, index)}> - enabledOrDisabledCookie(record, index)}> + enabledOrDisabledEnv(record, index)}> {record.status === Status.已禁用 ? ( ) : ( @@ -197,7 +181,7 @@ const Config = () => { - deleteCookie(record, index)}> + deleteEnv(record, index)}> @@ -211,39 +195,27 @@ const Config = () => { const [value, setValue] = useState([]); const [loading, setLoading] = useState(true); const [isModalVisible, setIsModalVisible] = useState(false); - const [editedCookie, setEditedCookie] = useState(); + const [isEditNameModalVisible, setIsEditNameModalVisible] = useState(false); + const [editedEnv, setEditedEnv] = useState(); const [selectedRowIds, setSelectedRowIds] = useState([]); - const getCookies = () => { + const getEnvs = () => { setLoading(true); request - .get(`${config.apiPrefix}cookies`) + .get(`${config.apiPrefix}envs`) .then((data: any) => { setValue(data.data); }) .finally(() => setLoading(false)); }; - const refreshStatus = (record: any, index: number) => { - request - .get(`${config.apiPrefix}cookies/${record._id}/refresh`) - .then(async (data: any) => { - if (data.data && data.data.value) { - (value as any).splice(index, 1, data.data); - setValue([...(value as any)] as any); - } else { - message.error('更新状态失败'); - } - }); - }; - - const enabledOrDisabledCookie = (record: any, index: number) => { + const enabledOrDisabledEnv = (record: any, index: number) => { Modal.confirm({ title: `确认${record.status === Status.已禁用 ? '启用' : '禁用'}`, content: ( <> 确认{record.status === Status.已禁用 ? '启用' : '禁用'} - Cookie{' '} + Env{' '} {record.value} {' '} @@ -253,7 +225,7 @@ const Config = () => { onOk() { request .put( - `${config.apiPrefix}cookies/${ + `${config.apiPrefix}envs/${ record.status === Status.已禁用 ? 'enable' : 'disable' }`, { @@ -284,22 +256,22 @@ const Config = () => { }); }; - const addCookie = () => { - setEditedCookie(null as any); + const addEnv = () => { + setEditedEnv(null as any); setIsModalVisible(true); }; - const editCookie = (record: any, index: number) => { - setEditedCookie(record); + const editEnv = (record: any, index: number) => { + setEditedEnv(record); setIsModalVisible(true); }; - const deleteCookie = (record: any, index: number) => { + const deleteEnv = (record: any, index: number) => { Modal.confirm({ title: '确认删除', content: ( <> - 确认删除Cookie{' '} + 确认删除Env{' '} {record.value} {' '} @@ -308,7 +280,7 @@ const Config = () => { ), onOk() { request - .delete(`${config.apiPrefix}cookies`, { data: [record._id] }) + .delete(`${config.apiPrefix}envs`, { data: [record._id] }) .then((data: any) => { if (data.code === 200) { message.success('删除成功'); @@ -326,25 +298,25 @@ const Config = () => { }); }; - const handleCancel = (cookies?: any[]) => { + const handleCancel = (env?: any[]) => { setIsModalVisible(false); - if (cookies && cookies.length > 0) { - handleCookies(cookies); - } + handleEnv(env); }; - const handleCookies = (cookies: any[]) => { + const handleEditNameCancel = (env?: any[]) => { + setIsEditNameModalVisible(false); + getEnvs(); + }; + + const handleEnv = (env: any) => { const result = [...value]; - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i]; - const index = value.findIndex((x) => x._id === cookie._id); - if (index === -1) { - result.push(cookie); - } else { - result.splice(index, 1, { - ...cookie, - }); - } + const index = value.findIndex((x) => x._id === env._id); + if (index === -1) { + result.push(env); + } else { + result.splice(index, 1, { + ...env, + }); } setValue(result); }; @@ -366,7 +338,7 @@ const Config = () => { newData.splice(hoverIndex, 0, dragRow); setValue([...newData]); request - .put(`${config.apiPrefix}cookies/${dragRow._id}/move`, { + .put(`${config.apiPrefix}envs/${dragRow._id}/move`, { data: { fromIndex: dragIndex, toIndex: hoverIndex }, }) .then((data: any) => { @@ -387,18 +359,18 @@ const Config = () => { onChange: onSelectChange, }; - const delCookies = () => { + const delEnvs = () => { Modal.confirm({ title: '确认删除', - content: <>确认删除选中的Cookie吗, + content: <>确认删除选中的Env吗, onOk() { request - .delete(`${config.apiPrefix}cookies`, { data: selectedRowIds }) + .delete(`${config.apiPrefix}envs`, { data: selectedRowIds }) .then((data: any) => { if (data.code === 200) { message.success('批量删除成功'); setSelectedRowIds([]); - getCookies(); + getEnvs(); } else { message.error(data); } @@ -410,18 +382,18 @@ const Config = () => { }); }; - const operateCookies = (operationStatus: number) => { + const operateEnvs = (operationStatus: number) => { Modal.confirm({ title: `确认${OperationName[operationStatus]}`, - content: <>确认{OperationName[operationStatus]}选中的Cookie吗, + content: <>确认{OperationName[operationStatus]}选中的Env吗, onOk() { request - .put(`${config.apiPrefix}cookies/${OperationPath[operationStatus]}`, { + .put(`${config.apiPrefix}envs/${OperationPath[operationStatus]}`, { data: selectedRowIds, }) .then((data: any) => { if (data.code === 200) { - getCookies(); + getEnvs(); } else { message.error(data); } @@ -433,6 +405,10 @@ const Config = () => { }); }; + const modifyName = () => { + setIsEditNameModalVisible(true); + }; + useEffect(() => { if (document.body.clientWidth < 768) { setWidth('auto'); @@ -443,16 +419,16 @@ const Config = () => { setMarginLeft(0); setMarginTop(-72); } - getCookies(); + getEnvs(); }, []); return ( addCookie()}> - 添加Cookie + , ]} header={{ @@ -473,20 +449,27 @@ const Config = () => { +