mirror of
				https://github.com/whyour/qinglong.git
				synced 2025-11-04 20:06:08 +08:00 
			
		
		
		
	重构环境变量管理,添加脚本查看
This commit is contained in:
		
							parent
							
								
									7ed1abde36
								
							
						
					
					
						commit
						0fade7a5a9
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -26,3 +26,4 @@
 | 
			
		|||
/log
 | 
			
		||||
/db
 | 
			
		||||
/manual_log
 | 
			
		||||
/scripts
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										48
									
								
								back/api/script.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								back/api/script.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -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'],
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<Cookie[]> {
 | 
			
		||||
    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<Cookie[]> {
 | 
			
		||||
    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<Cookie> {
 | 
			
		||||
    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<Cookie> {
 | 
			
		||||
    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<Cookie[]> {
 | 
			
		||||
    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<Cookie[]> {
 | 
			
		||||
    return new Promise((resolve) => {
 | 
			
		||||
      this.cronDb
 | 
			
		||||
        .find(query)
 | 
			
		||||
        .sort({ ...sort })
 | 
			
		||||
        .exec((err, docs) => {
 | 
			
		||||
          resolve(docs);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async get(_id: string): Promise<Cookie> {
 | 
			
		||||
    return new Promise((resolve) => {
 | 
			
		||||
      this.cronDb.find({ _id }).exec((err, docs) => {
 | 
			
		||||
        resolve(docs[0]);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async getBySort(sort: any): Promise<Cookie> {
 | 
			
		||||
    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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										227
									
								
								back/services/env.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								back/services/env.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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<Env> {
 | 
			
		||||
    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<Env> {
 | 
			
		||||
    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<Env> {
 | 
			
		||||
    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<Env> {
 | 
			
		||||
    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<Env[]> {
 | 
			
		||||
    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<Env[]> {
 | 
			
		||||
    return new Promise((resolve) => {
 | 
			
		||||
      this.cronDb
 | 
			
		||||
        .find(query)
 | 
			
		||||
        .sort({ ...sort })
 | 
			
		||||
        .exec((err, docs) => {
 | 
			
		||||
          resolve(docs);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async get(_id: string): Promise<Env> {
 | 
			
		||||
    return new Promise((resolve) => {
 | 
			
		||||
      this.cronDb.find({ _id }).exec((err, docs) => {
 | 
			
		||||
        resolve(docs[0]);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async getBySort(sort: any): Promise<Env> {
 | 
			
		||||
    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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 变量名= 声明即可
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										151
									
								
								shell/code.sh
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								shell/code.sh
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 "$@"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,9 +6,9 @@ const titleMap: any = {
 | 
			
		|||
  '/': '控制面板',
 | 
			
		||||
  '/login': '登录',
 | 
			
		||||
  '/crontab': '定时任务',
 | 
			
		||||
  '/cookie': 'Session管理',
 | 
			
		||||
  '/env': '环境变量',
 | 
			
		||||
  '/config': '配置文件',
 | 
			
		||||
  '/diy': '自定义脚本',
 | 
			
		||||
  '/script': '查看脚本',
 | 
			
		||||
  '/diff': '对比工具',
 | 
			
		||||
  '/log': '日志',
 | 
			
		||||
  '/setting': '系统设置',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,10 +25,10 @@ export default {
 | 
			
		|||
        component: '@/pages/crontab/index',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/cookie',
 | 
			
		||||
        name: 'Session管理',
 | 
			
		||||
        path: '/env',
 | 
			
		||||
        name: '环境变量',
 | 
			
		||||
        icon: <RadiusSettingOutlined />,
 | 
			
		||||
        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: <FormOutlined />,
 | 
			
		||||
        component: '@/pages/diy/index',
 | 
			
		||||
        component: '@/pages/script/index',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        path: '/diff',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<any[]>([]);
 | 
			
		||||
 | 
			
		||||
  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 (
 | 
			
		||||
    <PageContainer
 | 
			
		||||
      className="ql-container-wrapper"
 | 
			
		||||
      title="config.sh"
 | 
			
		||||
      className="ql-container-wrapper config-wrapper"
 | 
			
		||||
      title={title}
 | 
			
		||||
      extra={[
 | 
			
		||||
        <TreeSelect
 | 
			
		||||
          className="config-select"
 | 
			
		||||
          value={select}
 | 
			
		||||
          dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
 | 
			
		||||
          treeData={data}
 | 
			
		||||
          key="value"
 | 
			
		||||
          defaultValue="config.sh"
 | 
			
		||||
          onSelect={onSelect}
 | 
			
		||||
        />,
 | 
			
		||||
        <Button key="1" type="primary" onClick={updateConfig}>
 | 
			
		||||
          保存
 | 
			
		||||
        </Button>,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
      })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 (
 | 
			
		||||
    <PageContainer
 | 
			
		||||
      className="ql-container-wrapper"
 | 
			
		||||
      title="extra.sh"
 | 
			
		||||
      extra={[
 | 
			
		||||
        <Button key="1" type="primary" onClick={updateConfig}>
 | 
			
		||||
          保存
 | 
			
		||||
        </Button>,
 | 
			
		||||
      ]}
 | 
			
		||||
      header={{
 | 
			
		||||
        style: {
 | 
			
		||||
          padding: '4px 16px 4px 15px',
 | 
			
		||||
          position: 'sticky',
 | 
			
		||||
          top: 0,
 | 
			
		||||
          left: 0,
 | 
			
		||||
          zIndex: 20,
 | 
			
		||||
          marginTop,
 | 
			
		||||
          width,
 | 
			
		||||
          marginLeft,
 | 
			
		||||
        },
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <CodeMirror
 | 
			
		||||
        value={value}
 | 
			
		||||
        options={{
 | 
			
		||||
          lineNumbers: true,
 | 
			
		||||
          lineWrapping: true,
 | 
			
		||||
          styleActiveLine: true,
 | 
			
		||||
          matchBrackets: true,
 | 
			
		||||
          mode: 'shell',
 | 
			
		||||
        }}
 | 
			
		||||
        onBeforeChange={(editor, data, value) => {
 | 
			
		||||
          setValue(value);
 | 
			
		||||
        }}
 | 
			
		||||
        onChange={(editor, data, value) => {}}
 | 
			
		||||
      />
 | 
			
		||||
    </PageContainer>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Crontab;
 | 
			
		||||
							
								
								
									
										77
									
								
								src/pages/env/editNameModal.tsx
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/pages/env/editNameModal.tsx
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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 (
 | 
			
		||||
    <Modal
 | 
			
		||||
      title="修改环境变量名称"
 | 
			
		||||
      visible={visible}
 | 
			
		||||
      forceRender
 | 
			
		||||
      onOk={() => {
 | 
			
		||||
        form
 | 
			
		||||
          .validateFields()
 | 
			
		||||
          .then((values) => {
 | 
			
		||||
            handleOk(values);
 | 
			
		||||
          })
 | 
			
		||||
          .catch((info) => {
 | 
			
		||||
            console.log('Validate Failed:', info);
 | 
			
		||||
          });
 | 
			
		||||
      }}
 | 
			
		||||
      onCancel={() => handleCancel()}
 | 
			
		||||
      confirmLoading={loading}
 | 
			
		||||
      destroyOnClose
 | 
			
		||||
    >
 | 
			
		||||
      <Form
 | 
			
		||||
        form={form}
 | 
			
		||||
        layout="vertical"
 | 
			
		||||
        name="edit_name_modal"
 | 
			
		||||
        preserve={false}
 | 
			
		||||
      >
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          name="name"
 | 
			
		||||
          rules={[{ required: true, message: '请输入新的环境变量名称' }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input placeholder="请输入新的环境变量名称" />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
      </Form>
 | 
			
		||||
    </Modal>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default EditNameModal;
 | 
			
		||||
							
								
								
									
										174
									
								
								src/pages/cookie/index.tsx → src/pages/env/index.tsx
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										174
									
								
								src/pages/cookie/index.tsx → src/pages/env/index.tsx
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -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 (
 | 
			
		||||
          <span style={{ cursor: 'text' }}>{record.nickname || val} </span>
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      title: '值',
 | 
			
		||||
      dataIndex: 'value',
 | 
			
		||||
      key: 'value',
 | 
			
		||||
      align: 'center' as const,
 | 
			
		||||
      width: '50%',
 | 
			
		||||
      width: '45%',
 | 
			
		||||
      render: (text: string, record: any) => {
 | 
			
		||||
        return (
 | 
			
		||||
          <span
 | 
			
		||||
| 
						 | 
				
			
			@ -143,6 +130,7 @@ const Config = () => {
 | 
			
		|||
              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 (
 | 
			
		||||
          <Space size="middle" style={{ cursor: 'text' }}>
 | 
			
		||||
            <Tag
 | 
			
		||||
              color={StatusColor[record.status] || StatusColor[3]}
 | 
			
		||||
              style={{ marginRight: 0 }}
 | 
			
		||||
            >
 | 
			
		||||
            <Tag color={StatusColor[record.status]} style={{ marginRight: 0 }}>
 | 
			
		||||
              {Status[record.status]}
 | 
			
		||||
            </Tag>
 | 
			
		||||
            {record.status !== Status.已禁用 && (
 | 
			
		||||
              <Tooltip title="刷新">
 | 
			
		||||
                <a onClick={() => refreshStatus(record, index)}>
 | 
			
		||||
                  <SyncOutlined />
 | 
			
		||||
                </a>
 | 
			
		||||
              </Tooltip>
 | 
			
		||||
            )}
 | 
			
		||||
          </Space>
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -183,12 +167,12 @@ const Config = () => {
 | 
			
		|||
      render: (text: string, record: any, index: number) => (
 | 
			
		||||
        <Space size="middle">
 | 
			
		||||
          <Tooltip title="编辑">
 | 
			
		||||
            <a onClick={() => editCookie(record, index)}>
 | 
			
		||||
            <a onClick={() => editEnv(record, index)}>
 | 
			
		||||
              <EditOutlined />
 | 
			
		||||
            </a>
 | 
			
		||||
          </Tooltip>
 | 
			
		||||
          <Tooltip title={record.status === Status.已禁用 ? '启用' : '禁用'}>
 | 
			
		||||
            <a onClick={() => enabledOrDisabledCookie(record, index)}>
 | 
			
		||||
            <a onClick={() => enabledOrDisabledEnv(record, index)}>
 | 
			
		||||
              {record.status === Status.已禁用 ? (
 | 
			
		||||
                <CheckCircleOutlined />
 | 
			
		||||
              ) : (
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +181,7 @@ const Config = () => {
 | 
			
		|||
            </a>
 | 
			
		||||
          </Tooltip>
 | 
			
		||||
          <Tooltip title="删除">
 | 
			
		||||
            <a onClick={() => deleteCookie(record, index)}>
 | 
			
		||||
            <a onClick={() => deleteEnv(record, index)}>
 | 
			
		||||
              <DeleteOutlined />
 | 
			
		||||
            </a>
 | 
			
		||||
          </Tooltip>
 | 
			
		||||
| 
						 | 
				
			
			@ -211,39 +195,27 @@ const Config = () => {
 | 
			
		|||
  const [value, setValue] = useState<any[]>([]);
 | 
			
		||||
  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<string[]>([]);
 | 
			
		||||
 | 
			
		||||
  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{' '}
 | 
			
		||||
          <Text style={{ wordBreak: 'break-all' }} type="warning">
 | 
			
		||||
            {record.value}
 | 
			
		||||
          </Text>{' '}
 | 
			
		||||
| 
						 | 
				
			
			@ -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{' '}
 | 
			
		||||
          <Text style={{ wordBreak: 'break-all' }} type="warning">
 | 
			
		||||
            {record.value}
 | 
			
		||||
          </Text>{' '}
 | 
			
		||||
| 
						 | 
				
			
			@ -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,26 +298,26 @@ 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);
 | 
			
		||||
    const index = value.findIndex((x) => x._id === env._id);
 | 
			
		||||
    if (index === -1) {
 | 
			
		||||
        result.push(cookie);
 | 
			
		||||
      result.push(env);
 | 
			
		||||
    } else {
 | 
			
		||||
      result.splice(index, 1, {
 | 
			
		||||
          ...cookie,
 | 
			
		||||
        ...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 (
 | 
			
		||||
    <PageContainer
 | 
			
		||||
      className="session-wrapper"
 | 
			
		||||
      title="Session管理"
 | 
			
		||||
      className="env-wrapper"
 | 
			
		||||
      title="环境变量"
 | 
			
		||||
      extra={[
 | 
			
		||||
        <Button key="2" type="primary" onClick={() => addCookie()}>
 | 
			
		||||
          添加Cookie
 | 
			
		||||
        <Button key="2" type="primary" onClick={() => addEnv()}>
 | 
			
		||||
          添加Env
 | 
			
		||||
        </Button>,
 | 
			
		||||
      ]}
 | 
			
		||||
      header={{
 | 
			
		||||
| 
						 | 
				
			
			@ -473,20 +449,27 @@ const Config = () => {
 | 
			
		|||
          <Button
 | 
			
		||||
            type="primary"
 | 
			
		||||
            style={{ marginBottom: 5 }}
 | 
			
		||||
            onClick={delCookies}
 | 
			
		||||
            onClick={modifyName}
 | 
			
		||||
          >
 | 
			
		||||
            批量修改变量名称
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button
 | 
			
		||||
            type="primary"
 | 
			
		||||
            style={{ marginBottom: 5, marginLeft: 8 }}
 | 
			
		||||
            onClick={delEnvs}
 | 
			
		||||
          >
 | 
			
		||||
            批量删除
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button
 | 
			
		||||
            type="primary"
 | 
			
		||||
            onClick={() => operateCookies(0)}
 | 
			
		||||
            onClick={() => operateEnvs(0)}
 | 
			
		||||
            style={{ marginLeft: 8, marginBottom: 5 }}
 | 
			
		||||
          >
 | 
			
		||||
            批量启用
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button
 | 
			
		||||
            type="primary"
 | 
			
		||||
            onClick={() => operateCookies(1)}
 | 
			
		||||
            onClick={() => operateEnvs(1)}
 | 
			
		||||
            style={{ marginLeft: 8, marginRight: 8 }}
 | 
			
		||||
          >
 | 
			
		||||
            批量禁用
 | 
			
		||||
| 
						 | 
				
			
			@ -516,13 +499,18 @@ const Config = () => {
 | 
			
		|||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </DndProvider>
 | 
			
		||||
      <CookieModal
 | 
			
		||||
      <EnvModal
 | 
			
		||||
        visible={isModalVisible}
 | 
			
		||||
        handleCancel={handleCancel}
 | 
			
		||||
        cookie={editedCookie}
 | 
			
		||||
        env={editedEnv}
 | 
			
		||||
      />
 | 
			
		||||
      <EditNameModal
 | 
			
		||||
        visible={isEditNameModalVisible}
 | 
			
		||||
        handleCancel={handleEditNameCancel}
 | 
			
		||||
        ids={selectedRowIds}
 | 
			
		||||
      />
 | 
			
		||||
    </PageContainer>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Config;
 | 
			
		||||
export default Env;
 | 
			
		||||
| 
						 | 
				
			
			@ -3,12 +3,12 @@ import { Modal, message, Input, Form } from 'antd';
 | 
			
		|||
import { request } from '@/utils/http';
 | 
			
		||||
import config from '@/utils/config';
 | 
			
		||||
 | 
			
		||||
const CookieModal = ({
 | 
			
		||||
  cookie,
 | 
			
		||||
const EnvModal = ({
 | 
			
		||||
  env,
 | 
			
		||||
  handleCancel,
 | 
			
		||||
  visible,
 | 
			
		||||
}: {
 | 
			
		||||
  cookie?: any;
 | 
			
		||||
  env?: any;
 | 
			
		||||
  visible: boolean;
 | 
			
		||||
  handleCancel: (cks?: any[]) => void;
 | 
			
		||||
}) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -16,42 +16,28 @@ const CookieModal = ({
 | 
			
		|||
  const [loading, setLoading] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const handleOk = async (values: any) => {
 | 
			
		||||
    const cookies = values.value
 | 
			
		||||
      .split('\n')
 | 
			
		||||
      .map((x: any) => x.trim().replace(/\s/g, ''));
 | 
			
		||||
    let flag = false;
 | 
			
		||||
    for (const coo of cookies) {
 | 
			
		||||
      if (!/pt_key=\S*;\s*pt_pin=\S*;\s*/.test(coo)) {
 | 
			
		||||
        message.error(`${coo}格式有误`);
 | 
			
		||||
        flag = true;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (flag) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    const method = cookie ? 'put' : 'post';
 | 
			
		||||
    const payload = cookie ? { value: cookies[0], _id: cookie._id } : cookies;
 | 
			
		||||
    const { code, data } = await request[method](`${config.apiPrefix}cookies`, {
 | 
			
		||||
    const method = env ? 'put' : 'post';
 | 
			
		||||
    const payload = env ? { ...values, _id: env._id } : values;
 | 
			
		||||
    const { code, data } = await request[method](`${config.apiPrefix}envs`, {
 | 
			
		||||
      data: payload,
 | 
			
		||||
    });
 | 
			
		||||
    if (code === 200) {
 | 
			
		||||
      message.success(cookie ? '更新Cookie成功' : '添加Cookie成功');
 | 
			
		||||
      message.success(env ? '更新Env成功' : '添加Env成功');
 | 
			
		||||
    } else {
 | 
			
		||||
      message.error(data);
 | 
			
		||||
    }
 | 
			
		||||
    setLoading(false);
 | 
			
		||||
    handleCancel(cookie ? [data] : data);
 | 
			
		||||
    handleCancel(data);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    form.resetFields();
 | 
			
		||||
  }, [cookie]);
 | 
			
		||||
  }, [env]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Modal
 | 
			
		||||
      title={cookie ? '编辑Cookie' : '新建Cookie'}
 | 
			
		||||
      title={env ? '编辑Env' : '新建Env'}
 | 
			
		||||
      visible={visible}
 | 
			
		||||
      forceRender
 | 
			
		||||
      onOk={() => {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,29 +57,34 @@ const CookieModal = ({
 | 
			
		|||
      <Form
 | 
			
		||||
        form={form}
 | 
			
		||||
        layout="vertical"
 | 
			
		||||
        name="form_in_modal"
 | 
			
		||||
        name="env_modal"
 | 
			
		||||
        preserve={false}
 | 
			
		||||
        initialValues={cookie}
 | 
			
		||||
        initialValues={env}
 | 
			
		||||
      >
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          name="name"
 | 
			
		||||
          label="名称"
 | 
			
		||||
          rules={[{ required: true, message: '请输入环境变量名称' }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input placeholder="请输入环境变量名称" />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
        <Form.Item
 | 
			
		||||
          name="value"
 | 
			
		||||
          rules={[
 | 
			
		||||
            { required: true, message: '请输入Cookie' },
 | 
			
		||||
            {
 | 
			
		||||
              pattern: /pt_key=\S*;\s*pt_pin=\S*;\s*/,
 | 
			
		||||
              message: 'Cookie格式错误,注意分号(pt_key=***;pt_pin=***;)',
 | 
			
		||||
            },
 | 
			
		||||
          ]}
 | 
			
		||||
          label="值"
 | 
			
		||||
          rules={[{ required: true, message: '请输入环境变量值' }]}
 | 
			
		||||
        >
 | 
			
		||||
          <Input.TextArea
 | 
			
		||||
            rows={4}
 | 
			
		||||
            autoSize={true}
 | 
			
		||||
            placeholder="请输入cookie,可直接换行输入多个cookie"
 | 
			
		||||
            placeholder="请输入环境变量值"
 | 
			
		||||
          />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
        <Form.Item name="remarks" label="备注">
 | 
			
		||||
          <Input />
 | 
			
		||||
        </Form.Item>
 | 
			
		||||
      </Form>
 | 
			
		||||
    </Modal>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default CookieModal;
 | 
			
		||||
export default EnvModal;
 | 
			
		||||
							
								
								
									
										37
									
								
								src/pages/script/index.module.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/pages/script/index.module.less
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
@import '~@/styles/variable.less';
 | 
			
		||||
 | 
			
		||||
.left-tree {
 | 
			
		||||
  &-container {
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    background-color: #fff;
 | 
			
		||||
    height: calc(100vh - 128px);
 | 
			
		||||
    height: calc(100vh - var(--vh-offset, 0px) - 128px);
 | 
			
		||||
    width: @tree-width;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
  }
 | 
			
		||||
  &-scroller {
 | 
			
		||||
    flex: 1;
 | 
			
		||||
    overflow: auto;
 | 
			
		||||
  }
 | 
			
		||||
  &-search {
 | 
			
		||||
    margin-bottom: 16px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.log-container {
 | 
			
		||||
  display: flex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
:global {
 | 
			
		||||
  .log-wrapper {
 | 
			
		||||
    .ant-pro-grid-content.wide .ant-pro-page-container-children-content {
 | 
			
		||||
      background-color: #f8f8f8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .CodeMirror {
 | 
			
		||||
      width: calc(100% - 32px - @tree-width);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										170
									
								
								src/pages/script/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/pages/script/index.tsx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,170 @@
 | 
			
		|||
import { useState, useEffect, useCallback, Key } from 'react';
 | 
			
		||||
import { TreeSelect, Tree, Input } 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';
 | 
			
		||||
import styles from './index.module.less';
 | 
			
		||||
 | 
			
		||||
function getFilterData(keyword: string, data: any) {
 | 
			
		||||
  const expandedKeys: string[] = [];
 | 
			
		||||
  if (keyword) {
 | 
			
		||||
    const tree: any = [];
 | 
			
		||||
    data.forEach((item: any) => {
 | 
			
		||||
      if (item.title.toLocaleLowerCase().includes(keyword)) {
 | 
			
		||||
        tree.push(item);
 | 
			
		||||
        expandedKeys.push(...item.children.map((x: any) => x.key));
 | 
			
		||||
      } else {
 | 
			
		||||
        const children: any[] = [];
 | 
			
		||||
        (item.children || []).forEach((subItem: any) => {
 | 
			
		||||
          if (subItem.title.toLocaleLowerCase().includes(keyword)) {
 | 
			
		||||
            children.push(subItem);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        if (children.length > 0) {
 | 
			
		||||
          tree.push({
 | 
			
		||||
            ...item,
 | 
			
		||||
            children,
 | 
			
		||||
          });
 | 
			
		||||
          expandedKeys.push(...children.map((x) => x.key));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    return { tree, expandedKeys };
 | 
			
		||||
  }
 | 
			
		||||
  return { tree: data, expandedKeys };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Script = () => {
 | 
			
		||||
  const [width, setWidth] = useState('100%');
 | 
			
		||||
  const [marginLeft, setMarginLeft] = useState(0);
 | 
			
		||||
  const [marginTop, setMarginTop] = useState(-72);
 | 
			
		||||
  const [title, setTitle] = useState('请选择脚本文件');
 | 
			
		||||
  const [value, setValue] = useState('请选择脚本文件');
 | 
			
		||||
  const [select, setSelect] = useState();
 | 
			
		||||
  const [data, setData] = useState<any[]>([]);
 | 
			
		||||
  const [filterData, setFilterData] = useState<any[]>([]);
 | 
			
		||||
  const [loading, setLoading] = useState(false);
 | 
			
		||||
  const [isPhone, setIsPhone] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const getScripts = () => {
 | 
			
		||||
    request.get(`${config.apiPrefix}scripts/files`).then((data) => {
 | 
			
		||||
      setData(data.data);
 | 
			
		||||
      setFilterData(data.data);
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const getDetail = (node: any) => {
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    request
 | 
			
		||||
      .get(`${config.apiPrefix}scripts/${node.value}`)
 | 
			
		||||
      .then((data) => {
 | 
			
		||||
        setValue(data.data);
 | 
			
		||||
      })
 | 
			
		||||
      .finally(() => setLoading(false));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onSelect = (value: any, node: any) => {
 | 
			
		||||
    console.log(value);
 | 
			
		||||
    console.log(node);
 | 
			
		||||
    setSelect(value);
 | 
			
		||||
    setTitle(node.parent || node.value);
 | 
			
		||||
    getDetail(node);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onTreeSelect = useCallback((keys: Key[], e: any) => {
 | 
			
		||||
    onSelect(keys[0], e.node);
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  const onSearch = useCallback(
 | 
			
		||||
    (e) => {
 | 
			
		||||
      const keyword = e.target.value;
 | 
			
		||||
      const { tree } = getFilterData(keyword.toLocaleLowerCase(), data);
 | 
			
		||||
      setFilterData(tree);
 | 
			
		||||
    },
 | 
			
		||||
    [data, setFilterData],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (document.body.clientWidth < 768) {
 | 
			
		||||
      setWidth('auto');
 | 
			
		||||
      setMarginLeft(0);
 | 
			
		||||
      setMarginTop(0);
 | 
			
		||||
      setIsPhone(true);
 | 
			
		||||
    } else {
 | 
			
		||||
      setWidth('100%');
 | 
			
		||||
      setMarginLeft(0);
 | 
			
		||||
      setMarginTop(-72);
 | 
			
		||||
      setIsPhone(false);
 | 
			
		||||
    }
 | 
			
		||||
    getScripts();
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <PageContainer
 | 
			
		||||
      className="ql-container-wrapper log-wrapper"
 | 
			
		||||
      title={title}
 | 
			
		||||
      extra={
 | 
			
		||||
        isPhone && [
 | 
			
		||||
          <TreeSelect
 | 
			
		||||
            className="log-select"
 | 
			
		||||
            value={select}
 | 
			
		||||
            dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
 | 
			
		||||
            treeData={data}
 | 
			
		||||
            placeholder="请选择脚本文件"
 | 
			
		||||
            showSearch
 | 
			
		||||
            key="value"
 | 
			
		||||
            onSelect={onSelect}
 | 
			
		||||
          />,
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
      header={{
 | 
			
		||||
        style: {
 | 
			
		||||
          padding: '4px 16px 4px 15px',
 | 
			
		||||
          position: 'sticky',
 | 
			
		||||
          top: 0,
 | 
			
		||||
          left: 0,
 | 
			
		||||
          zIndex: 20,
 | 
			
		||||
          marginTop,
 | 
			
		||||
          width,
 | 
			
		||||
          marginLeft,
 | 
			
		||||
        },
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <div className={`${styles['log-container']}`}>
 | 
			
		||||
        {!isPhone && (
 | 
			
		||||
          <div className={styles['left-tree-container']}>
 | 
			
		||||
            <Input.Search
 | 
			
		||||
              className={styles['left-tree-search']}
 | 
			
		||||
              onChange={onSearch}
 | 
			
		||||
            ></Input.Search>
 | 
			
		||||
            <div className={styles['left-tree-scroller']}>
 | 
			
		||||
              <Tree
 | 
			
		||||
                className={styles['left-tree']}
 | 
			
		||||
                treeData={filterData}
 | 
			
		||||
                onSelect={onTreeSelect}
 | 
			
		||||
              ></Tree>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
        <CodeMirror
 | 
			
		||||
          value={value}
 | 
			
		||||
          options={{
 | 
			
		||||
            lineNumbers: true,
 | 
			
		||||
            lineWrapping: true,
 | 
			
		||||
            styleActiveLine: true,
 | 
			
		||||
            matchBrackets: true,
 | 
			
		||||
            mode: 'shell',
 | 
			
		||||
            readOnly: true,
 | 
			
		||||
          }}
 | 
			
		||||
          onBeforeChange={(editor, data, value) => {
 | 
			
		||||
            setValue(value);
 | 
			
		||||
          }}
 | 
			
		||||
          onChange={(editor, data, value) => {}}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </PageContainer>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Script;
 | 
			
		||||
| 
						 | 
				
			
			@ -9,11 +9,11 @@ message.config({
 | 
			
		|||
const time = Date.now();
 | 
			
		||||
const errorHandler = function (error: any) {
 | 
			
		||||
  if (error.response) {
 | 
			
		||||
    const message = error.data
 | 
			
		||||
    const msg = error.data
 | 
			
		||||
      ? error.data.message || error.data
 | 
			
		||||
      : error.response.statusText;
 | 
			
		||||
    if (error.response.status !== 401 && error.response.status !== 502) {
 | 
			
		||||
      message.error(message);
 | 
			
		||||
      message.error(msg);
 | 
			
		||||
    } else {
 | 
			
		||||
      console.log(error.response);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user