This commit is contained in:
熊潇 2025-03-25 00:38:41 +08:00
parent cb490470c1
commit 64c70ce527
14 changed files with 261 additions and 73 deletions

3
.gitignore vendored
View File

@ -19,3 +19,6 @@ release/*
.turbo .turbo
.env .env
pack-dist
app.config.json5.envision

View File

@ -0,0 +1,22 @@
{
"name": "codecenter",
"version": "1.0.0",
"scripts": {
"start": "pm2 start dist/app.mjs --name codecenter"
},
"dependencies": {
"@kevisual/router": "^0.0.10-beta.1",
"@kevisual/use-config": "^1.0.10",
"ioredis": "^5.6.0",
"minio": "^8.0.5",
"pg": "^8.14.1",
"sequelize": "^6.37.6",
"sqlite3": "^5.1.7",
"socket.io": "^4.8.1",
"@msgpack/msgpack": "3.1.1",
"pino": "^9.6.0",
"pino-pretty": "^13.0.0",
"pm2": "^6.0.5",
"dotenv": "^16.4.7"
}
}

View File

@ -5,6 +5,7 @@
"type": "module", "type": "module",
"main": "index.js", "main": "index.js",
"author": "abearxiong", "author": "abearxiong",
"basename": "/root/code-center",
"scripts": { "scripts": {
"watch": "rollup -c rollup.config.mjs -w", "watch": "rollup -c rollup.config.mjs -w",
"dev": "cross-env NODE_ENV=development nodemon --delay 2.5 -e js,cjs,mjs --exec node dist/app.mjs", "dev": "cross-env NODE_ENV=development nodemon --delay 2.5 -e js,cjs,mjs --exec node dist/app.mjs",
@ -13,11 +14,14 @@
"build": "rimraf dist && rollup -c rollup.config.mjs", "build": "rimraf dist && rollup -c rollup.config.mjs",
"deploy": "rsync -avz --delete ./dist/ --exclude='app.config.json5' light:~/apps/codecenter/dist", "deploy": "rsync -avz --delete ./dist/ --exclude='app.config.json5' light:~/apps/codecenter/dist",
"deploy:sky": "rsync -avz --delete ./dist/ --exclude='app.config.json5' sky:~/kevisual/dist", "deploy:sky": "rsync -avz --delete ./dist/ --exclude='app.config.json5' sky:~/kevisual/dist",
"deploy:envision": "rsync -avz --delete ./dist/ --exclude='app.config.json5' envision:~/kevisual/dist",
"clean": "rm -rf dist", "clean": "rm -rf dist",
"reload": "ssh light pm2 restart codecenter", "reload": "ssh light pm2 restart codecenter",
"reload:sky": "ssh sky pm2 restart codecenter", "reload:sky": "ssh sky pm2 restart codecenter",
"reload:envision": "ssh envision pm2 restart codecenter",
"pub:me": "npm run build && npm run deploy && npm run reload", "pub:me": "npm run build && npm run deploy && npm run reload",
"pub:sky": "npm run build && npm run deploy:sky && npm run reload:sky", "pub:sky": "npm run build && npm run deploy:sky && npm run reload:sky",
"pub:envision": "npm run build && npm run deploy:envision && npm run reload:envision",
"start": "pm2 start dist/app.mjs --name codecenter", "start": "pm2 start dist/app.mjs --name codecenter",
"release": "node ./config/release/index.mjs", "release": "node ./config/release/index.mjs",
"pub": "envision pack -p -u", "pub": "envision pack -p -u",
@ -30,9 +34,7 @@
"keywords": [], "keywords": [],
"types": "types/index.d.ts", "types": "types/index.d.ts",
"files": [ "files": [
"types", "dist"
"dist",
"src"
], ],
"license": "UNLICENSED", "license": "UNLICENSED",
"dependencies": { "dependencies": {

View File

@ -289,9 +289,19 @@
import { User, UserInit, UserServices } from '@kevisual/code-center-module/models'; import { User, UserInit, UserServices } from '@kevisual/code-center-module/models';
export { User, UserInit, UserServices }; export { User, UserInit, UserServices };
UserInit(null, null, { import { OrgInit } from '@kevisual/code-center-module/models';
const init = async () => {
await OrgInit(null, null, {
alter: true,
logging: false,
}).catch((e) => {
console.error('Org sync', e);
});
await UserInit(null, null, {
alter: true, alter: true,
logging: false, logging: false,
}).catch((e) => { }).catch((e) => {
console.error('User sync', e); console.error('User sync', e);
}); });
};
init();

View File

@ -18,6 +18,7 @@ export const addAuth = (app: App) => {
.route({ .route({
path: 'auth', path: 'auth',
id: 'auth', id: 'auth',
isDebug: true,
}) })
.define(async (ctx) => { .define(async (ctx) => {
const token = ctx.query.token; const token = ctx.query.token;

View File

@ -0,0 +1,90 @@
import { app } from '@/app.ts';
import { AppModel } from './module/app.ts';
import { AppDomainModel } from './module/app-domain.ts';
app
.route({
path: 'app',
key: 'getDomainApp',
})
.define(async (ctx) => {
const { domain } = ctx.query.data;
// const query = {
// }
const domainInfo = await AppDomainModel.findOne({ where: { domain } });
if (!domainInfo) {
ctx.throw(404, 'app not found');
}
const app = await AppModel.findByPk(domainInfo.appId);
if (!app) {
ctx.throw(404, 'app not found');
}
ctx.body = app;
return ctx;
})
.addTo(app);
app
.route({
path: 'app-domain',
key: 'create',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const uid = tokenUser.uid;
const { domain, appId } = ctx.query.data || {};
if (!domain || !appId) {
ctx.throw(400, 'domain and appId are required');
}
const domainInfo = await AppDomainModel.create({ domain, appId, uid });
ctx.body = domainInfo;
return ctx;
})
.addTo(app);
app
.route({
path: 'app-domain',
key: 'update',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const uid = tokenUser.uid;
const { id, domain, appId, status } = ctx.query.data || {};
if (!domain && !id) {
ctx.throw(400, 'domain and id are required at least one');
}
if (!status) {
ctx.throw(400, 'status is required');
}
let domainInfo: AppDomainModel | null = null;
if (id) {
domainInfo = await AppDomainModel.findByPk(id);
}
if (!domainInfo && domain) {
domainInfo = await AppDomainModel.findOne({ where: { domain, appId } });
}
if (!domainInfo) {
ctx.throw(404, 'domain not found');
}
if (domainInfo.uid !== uid) {
ctx.throw(403, 'domain must be owned by the user');
}
if (!domainInfo.checkCanUpdateStatus(status)) {
ctx.throw(400, 'domain status can not be updated');
}
if (status) {
domainInfo.status = status;
}
if (appId) {
domainInfo.appId = appId;
}
await domainInfo.save({ fields: ['status', 'appId'] });
ctx.body = domainInfo;
return ctx;
})
.addTo(app);

View File

@ -2,5 +2,6 @@ import './list.ts';
import './user-app.ts'; import './user-app.ts';
import './public/index.ts'; import './public/index.ts';
import './domain.ts';
export * from './module/index.ts'; export * from './module/index.ts';

View File

@ -140,6 +140,7 @@ app
path: 'app', path: 'app',
key: 'uploadFiles', key: 'uploadFiles',
middleware: ['auth'], middleware: ['auth'],
isDebug: true,
}) })
.define(async (ctx) => { .define(async (ctx) => {
try { try {
@ -264,23 +265,6 @@ app
}) })
.addTo(app); .addTo(app);
app
.route({
path: 'app',
key: 'getDomainApp',
})
.define(async (ctx) => {
const { domain } = ctx.query.data;
// const query = {
// }
const app = await AppModel.findOne({ where: { domain } });
if (!app) {
throw new CustomError('app not found');
}
ctx.body = app;
return ctx;
})
.addTo(app);
app app
.route({ .route({

View File

@ -0,0 +1,64 @@
import { sequelize } from '../../../modules/sequelize.ts';
import { DataTypes, Model } from 'sequelize';
export type DomainList = Partial<InstanceType<typeof AppDomainModel>>;
// 审核,通过,驳回
const appDomainStatus = ['audit', 'auditReject', 'auditPending', 'running', 'stop'] as const;
type AppDomainStatus = (typeof appDomainStatus)[number];
/**
*
*/
export class AppDomainModel extends Model {
declare id: string;
declare domain: string;
declare appId: string;
// 状态,
declare status: AppDomainStatus;
declare uid: string;
declare createdAt: Date;
declare updatedAt: Date;
checkCanUpdateStatus(newStatus: AppDomainStatus) {
// 原本是运行中,可以改为停止,原本是停止,可以改为运行。
if (this.status === 'running' || this.status === 'stop') {
return true;
}
// 原本是审核状态,不能修改。
return false;
}
}
AppDomainModel.init(
{
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
comment: 'id',
},
domain: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
appId: {
type: DataTypes.STRING,
allowNull: false,
},
uid: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
tableName: 'kv_app_domain',
paranoid: true,
},
);
AppDomainModel.sync({ alter: true, logging: false }).catch((e) => {
console.error('AppDomainModel sync', e);
});

View File

@ -36,7 +36,6 @@ export class AppModel extends Model {
declare title: string; declare title: string;
declare description: string; declare description: string;
declare version: string; declare version: string;
declare domain: string;
declare key: string; declare key: string;
declare uid: string; declare uid: string;
declare pid: string; declare pid: string;
@ -69,10 +68,6 @@ AppModel.init(
type: DataTypes.STRING, type: DataTypes.STRING,
defaultValue: '', defaultValue: '',
}, },
domain: {
type: DataTypes.STRING,
defaultValue: '',
},
key: { key: {
type: DataTypes.STRING, type: DataTypes.STRING,
// 和 uid 组合唯一 // 和 uid 组合唯一

View File

@ -11,3 +11,5 @@ import './file/index.ts';
import './micro-app/index.ts'; import './micro-app/index.ts';
import './config/index.ts'; import './config/index.ts';
import './mark/index.ts';

1
src/routes/mark/index.ts Normal file
View File

@ -0,0 +1 @@
import './list.ts';

View File

@ -44,26 +44,27 @@ app
id: markModel.id, id: markModel.id,
}; };
} else { } else {
const [markModel, created] = await MarkModel.findOrCreate({ ctx.throw(400, 'id is required');
where: { // const [markModel, created] = await MarkModel.findOrCreate({
uid: tokenUser.id, // where: {
puid: tokenUser.uid, // uid: tokenUser.id,
title: dayjs().format('YYYY-MM-DD'), // puid: tokenUser.uid,
}, // title: dayjs().format('YYYY-MM-DD'),
defaults: { // },
title: dayjs().format('YYYY-MM-DD'), // defaults: {
uid: tokenUser.id, // title: dayjs().format('YYYY-MM-DD'),
markType: 'wallnote', // uid: tokenUser.id,
tags: ['daily'], // markType: 'wallnote',
}, // tags: ['daily'],
}); // },
ctx.body = { // });
version: Number(markModel.version), // ctx.body = {
updatedAt: markModel.updatedAt, // version: Number(markModel.version),
createdAt: markModel.createdAt, // updatedAt: markModel.updatedAt,
id: markModel.id, // createdAt: markModel.createdAt,
created: created, // id: markModel.id,
}; // created: created,
// };
} }
}) })
.addTo(app); .addTo(app);
@ -87,24 +88,25 @@ app
} }
ctx.body = markModel; ctx.body = markModel;
} else { } else {
ctx.throw(400, 'id is required');
// id 不存在获取当天的title为 日期的一条数据 // id 不存在获取当天的title为 日期的一条数据
const [markModel, created] = await MarkModel.findOrCreate({ // const [markModel, created] = await MarkModel.findOrCreate({
where: { // where: {
uid: tokenUser.id, // uid: tokenUser.id,
puid: tokenUser.uid, // puid: tokenUser.uid,
title: dayjs().format('YYYY-MM-DD'), // title: dayjs().format('YYYY-MM-DD'),
}, // },
defaults: { // defaults: {
title: dayjs().format('YYYY-MM-DD'), // title: dayjs().format('YYYY-MM-DD'),
uid: tokenUser.id, // uid: tokenUser.id,
markType: 'wallnote', // markType: 'wallnote',
tags: ['daily'], // tags: ['daily'],
uname: tokenUser.username, // uname: tokenUser.username,
puid: tokenUser.uid, // puid: tokenUser.uid,
version: 1, // version: 1,
}, // },
}); // });
ctx.body = markModel; // ctx.body = markModel;
} }
}) })
.addTo(app); .addTo(app);

View File

@ -12,6 +12,7 @@ export const createCookie = (token: any, ctx: any) => {
if (!domain) { if (!domain) {
return; return;
} }
//TODO, 获取访问的 hostname 如果访问的和 domain 的不一致也创建cookie
const browser = ctx.req.headers['user-agent']; const browser = ctx.req.headers['user-agent'];
const isBrowser = browser.includes('Mozilla'); // 浏览器 const isBrowser = browser.includes('Mozilla'); // 浏览器
if (isBrowser && ctx.res.cookie) { if (isBrowser && ctx.res.cookie) {
@ -50,11 +51,21 @@ export type ReqHeaders = {
cookie: string; // 饼干 cookie: string; // 饼干
}; };
export const getSomeInfoFromReq = (ctx: any) => { export const getSomeInfoFromReq = (ctx: any) => {
const headers = ctx.req.headers as ReqHeaders; const headers = ctx.req?.headers as ReqHeaders;
const userAgent = headers['user-agent']; if (!headers) {
const host = headers['host']; console.log('no req headers', ctx.req);
const ip = headers['x-forwarded-for'] || ctx.req.connection.remoteAddress; return {
console.log('req headers', headers); 'user-agent': '',
browser: '',
isBrowser: false,
host: '',
ip: '',
headers: {},
};
}
const userAgent = headers?.['user-agent'] || '';
const host = headers?.['host'];
const ip = headers?.['x-forwarded-for'] || ctx.req?.connection?.remoteAddress;
return { return {
'user-agent': userAgent, 'user-agent': userAgent,
browser: userAgent, browser: userAgent,