Refactor: Remove unused Convex HTTP routes and schema definitions

- Deleted `http.ts` which contained custom HTTP routes for authentication and JWKS.
- Removed `schema.ts` that defined database schemas for `github_starred`, `users`, and `sessions`.
- Updated `package.json` to remove the Convex dependency and updated other dependencies.
- Deleted SQL script `clear.sql` for removing constraints from tables.
- Removed `update.sh` script for updating dependencies.
- Deleted demo application routes and models in `app-demo` directory.
- Cleaned up unused modules and imports across various files.
- Removed Redis and common utility scripts that were not in use.
- Deleted test scripts related to user and bucket management.
This commit is contained in:
2026-02-07 00:16:00 +08:00
parent 32f4e9c40f
commit d62a75842f
31 changed files with 10 additions and 1484 deletions

View File

@@ -1,119 +0,0 @@
import { Op } from 'sequelize';
import { AppDemoModel } from './models/index.ts';
import { app } from '@/app.ts';
app
.route({
path: 'app-demo',
key: 'list',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { page = 1, pageSize = 20, search, sort = 'DESC' } = ctx.query;
const searchWhere = search
? {
[Op.or]: [{ title: { [Op.like]: `%${search}%` } }, { summary: { [Op.like]: `%${search}%` } }],
}
: {};
const { rows: appDemo, count } = await AppDemoModel.findAndCountAll({
where: {
uid: tokenUser.id,
...searchWhere,
},
offset: (page - 1) * pageSize,
limit: pageSize,
order: [['updatedAt', sort]],
});
ctx.body = {
list: appDemo,
pagination: {
page,
current: page,
pageSize,
total: count,
},
};
})
.addTo(app);
app
.route({
path: 'app-demo',
key: 'update',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id, data, updatedAt: _clear, createdAt: _clear2, ...rest } = ctx.query.data;
let appDemo: AppDemoModel;
let isNew = false;
if (id) {
const appDemo = await AppDemoModel.findByPk(id);
if (appDemo.uid !== tokenUser.uid) {
ctx.throw(403, 'No permission');
}
} else {
appDemo = await AppDemoModel.create({
data: data,
...rest,
uid: tokenUser.uid,
});
isNew = true;
}
if (!appDemo) {
ctx.throw(404, 'AppDemo not found');
}
if (!isNew) {
appDemo = await appDemo.update({
data: { ...appDemo.data, ...data },
...rest,
});
}
ctx.body = appDemo;
})
.addTo(app);
app
.route({
path: 'app-demo',
key: 'delete',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id, force = false } = ctx.query.data || {};
if (!id) {
ctx.throw(400, 'id is required');
}
const appDemo = await AppDemoModel.findByPk(id);
if (appDemo.uid !== tokenUser.id) {
ctx.throw(403, 'No permission');
}
await appDemo.destroy({ force });
ctx.body = appDemo;
})
.addTo(app);
app
.route({
path: 'app-demo',
key: 'get',
middleware: ['auth'],
})
.define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id } = ctx.query.data || {};
if (!id) {
ctx.throw(400, 'id is required');
}
const appDemo = await AppDemoModel.findByPk(id);
if (appDemo.uid !== tokenUser.id) {
ctx.throw(403, 'No permission');
}
ctx.body = appDemo;
})
.addTo(app);

View File

@@ -1,71 +0,0 @@
import { sequelize } from '@/modules/sequelize.ts';
import { DataTypes, Model } from 'sequelize';
export interface AppDemoData {
[key: string]: any;
}
export type AppDemo = Partial<InstanceType<typeof AppDemoModel>>;
export class AppDemoModel extends Model {
declare id: string;
declare title: string;
declare description: string;
declare summary: string;
declare data: AppDemoData;
declare tags: string[];
declare version: string;
declare uid: string;
declare createdAt: Date;
declare updatedAt: Date;
}
AppDemoModel.init(
{
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
},
title: {
type: DataTypes.TEXT,
defaultValue: '',
},
description: {
type: DataTypes.TEXT,
defaultValue: '',
},
summary: {
type: DataTypes.TEXT,
defaultValue: '',
},
tags: {
type: DataTypes.JSONB,
defaultValue: [],
},
version: {
type: DataTypes.INTEGER,
defaultValue: 0,
},
data: {
type: DataTypes.JSONB,
defaultValue: {},
},
uid: {
type: DataTypes.UUID,
allowNull: false,
},
},
{
sequelize,
tableName: 'kv_app_demo',
paranoid: true,
},
);
// AppDemoModel.sync({ alter: true, logging: false }).catch((e) => {
// console.error('AppDemoModel sync', e);
// });

View File

@@ -1,3 +1 @@
export { sequelize } from './sequelize.ts';
export * from './minio.ts'

View File

@@ -1,13 +0,0 @@
import { Client, } from 'minio';
import { useConfig } from '@kevisual/use-config';
const config = useConfig();
const minioConfig = {
endPoint: config.MINIO_ENDPOINT || 'localhost',
// @ts-ignore
port: parseInt(config.MINIO_PORT || '9000'),
useSSL: config.MINIO_USE_SSL === 'true',
accessKey: config.MINIO_ACCESS_KEY,
secretKey: config.MINIO_SECRET_KEY,
};
export const minioClient = new Client(minioConfig);

View File

@@ -1,14 +0,0 @@
import { program, Command } from 'commander';
// import { useContextKey } from '@kevisual/context';
// import * as redisLib from './modules/redis.ts';
// import * as sequelizeLib from './modules/sequelize.ts';
// import * as minioLib from './modules/minio.ts';
// export const redis = useContextKey('redis', () => redisLib.redis);
// export const minioClient = useContextKey('minioClient', () => minioLib.minioClient);
// export const sequelize = useContextKey('sequelize', () => sequelizeLib.sequelize);
export { program, Command };
program.description('code-center的一部分工具');
program.version('1.0.0', '-v, --version');

View File

@@ -1,5 +1,4 @@
import { app } from '@/app.ts';
import { AppModel, AppListModel } from '../module/index.ts';
import { AppModel } from '../module/index.ts';
export const mvAppFromUserAToUserB = async (userA: string, userB: string) => {
const appList = await AppModel.findAll({
where: {

View File

@@ -80,8 +80,4 @@ AppDomainModel.init(
tableName: 'kv_app_domain',
paranoid: true,
},
);
// AppDomainModel.sync({ alter: true, logging: false }).catch((e) => {
// console.error('AppDomainModel sync', e);
// });
);

View File

@@ -1,327 +0,0 @@
import { useContextKey } from '@kevisual/context';
import { nanoid, customAlphabet } from 'nanoid';
import { DataTypes, Model, ModelAttributes } from 'sequelize';
import type { Sequelize } from 'sequelize';
export const random = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
export type Mark = Partial<InstanceType<typeof MarkModel>>;
export type MarkData = {
md?: string; // markdown
mdList?: string[]; // markdown list
type?: string; // 类型 markdown | json | html | image | video | audio | code | link | file
data?: any;
key?: string; // 文件的名称, 唯一
push?: boolean; // 是否推送到elasticsearch
pushTime?: Date; // 推送时间
summary?: string; // 摘要
nodes?: MarkDataNode[]; // 节点
[key: string]: any;
};
export type MarkFile = {
id: string;
name: string;
url: string;
size: number;
type: 'self' | 'data' | 'generate'; // generate为生成文件
query: string; // 'data.nodes[id].content';
hash: string;
fileKey: string; // 文件的名称, 唯一
};
export type MarkDataNode = {
id?: string;
[key: string]: any;
};
export type MarkConfig = {
[key: string]: any;
};
export type MarkAuth = {
[key: string]: any;
};
/**
* 隐秘内容
* auth
* config
*
*/
export class MarkModel extends Model {
declare id: string;
declare title: string; // 标题可以ai生成
declare description: string; // 描述可以ai生成
declare cover: string; // 封面可以ai生成
declare thumbnail: string; // 缩略图
declare key: string; // 文件路径
declare markType: string; // markdown | json | html | image | video | audio | code | link | file
declare link: string; // 访问链接
declare tags: string[]; // 标签
declare summary: string; // 摘要, description的简化版
declare data: MarkData; // 数据
declare uid: string; // 操作用户的id
declare puid: string; // 父级用户的id, 真实用户
declare config: MarkConfig; // mark属于一定不会暴露的内容。
declare fileList: MarkFile[]; // 文件管理
declare uname: string; // 用户的名称, 或者着别名
declare markedAt: Date; // 标记时间
declare createdAt: Date;
declare updatedAt: Date;
declare version: number;
/**
* 加锁更新data中的node的节点通过node的id
* @param param0
*/
static async updateJsonNode(id: string, node: MarkDataNode, opts?: { operate?: 'update' | 'delete'; Model?: any; sequelize?: Sequelize }) {
const sequelize = opts?.sequelize || (await useContextKey('sequelize'));
const transaction = await sequelize.transaction(); // 开启事务
const operate = opts.operate || 'update';
const isUpdate = operate === 'update';
const Model = opts.Model || MarkModel;
try {
// 1. 获取当前的 JSONB 字段值(加锁)
const mark = await Model.findByPk(id, {
transaction,
lock: transaction.LOCK.UPDATE, // 加锁,防止其他事务同时修改
});
if (!mark) {
throw new Error('Mark not found');
}
// 2. 修改特定的数组元素
const data = mark.data as MarkData;
const items = data.nodes;
if (!node.id) {
node.id = random(12);
}
// 找到要更新的元素
const itemIndex = items.findIndex((item) => item.id === node.id);
if (itemIndex === -1) {
isUpdate && items.push(node);
} else {
if (isUpdate) {
items[itemIndex] = node;
} else {
items.splice(itemIndex, 1);
}
}
const version = Number(mark.version) + 1;
// 4. 更新 JSONB 字段
const result = await mark.update(
{
data: {
...data,
nodes: items,
},
version,
},
{ transaction },
);
await transaction.commit();
return result;
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async updateJsonNodes(id: string, nodes: { node: MarkDataNode; operate?: 'update' | 'delete' }[], opts?: { Model?: any; sequelize?: Sequelize }) {
const sequelize = opts?.sequelize || (await useContextKey('sequelize'));
const transaction = await sequelize.transaction(); // 开启事务
const Model = opts?.Model || MarkModel;
try {
const mark = await Model.findByPk(id, {
transaction,
lock: transaction.LOCK.UPDATE, // 加锁,防止其他事务同时修改
});
if (!mark) {
throw new Error('Mark not found');
}
const data = mark.data as MarkData;
const _nodes = data.nodes || [];
// 过滤不在nodes中的节点
const blankNodes = nodes.filter((node) => !_nodes.find((n) => n.id === node.node.id)).map((node) => node.node);
// 更新或删除节点
const newNodes = _nodes
.map((node) => {
const nodeOperate = nodes.find((n) => n.node.id === node.id);
if (nodeOperate) {
if (nodeOperate.operate === 'delete') {
return null;
}
return nodeOperate.node;
}
return node;
})
.filter((node) => node !== null);
const version = Number(mark.version) + 1;
const result = await mark.update(
{
data: {
...data,
nodes: [...blankNodes, ...newNodes],
},
version,
},
{ transaction },
);
await transaction.commit();
return result;
} catch (error) {
await transaction.rollback();
throw error;
}
}
static async updateData(id: string, data: MarkData, opts: { Model?: any; sequelize?: Sequelize }) {
const sequelize = opts.sequelize || (await useContextKey('sequelize'));
const transaction = await sequelize.transaction(); // 开启事务
const Model = opts.Model || MarkModel;
const mark = await Model.findByPk(id, {
transaction,
lock: transaction.LOCK.UPDATE, // 加锁,防止其他事务同时修改
});
if (!mark) {
throw new Error('Mark not found');
}
const version = Number(mark.version) + 1;
const result = await mark.update(
{
...mark.data,
...data,
data: {
...mark.data,
...data,
},
version,
},
{ transaction },
);
await transaction.commit();
return result;
}
static async createNew(data: any, opts: { Model?: any; sequelize?: Sequelize }) {
const sequelize = opts.sequelize || (await useContextKey('sequelize'));
const transaction = await sequelize.transaction(); // 开启事务
const Model = opts.Model || MarkModel;
const result = await Model.create({ ...data, version: 1 }, { transaction });
await transaction.commit();
return result;
}
}
export type MarkInitOpts<T = any> = {
tableName: string;
sequelize?: Sequelize;
callInit?: (attribute: ModelAttributes) => ModelAttributes;
Model?: T extends typeof MarkModel ? T : typeof MarkModel;
};
export type Opts = {
sync?: boolean;
alter?: boolean;
logging?: boolean | ((...args: any) => any);
force?: boolean;
};
export const MarkMInit = async <T = any>(opts: MarkInitOpts<T>, sync?: Opts) => {
const sequelize = await useContextKey('sequelize');
opts.sequelize = opts.sequelize || sequelize;
const { callInit, Model, ...optsRest } = opts;
const modelAttribute = {
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
comment: 'id',
},
title: {
type: DataTypes.TEXT,
defaultValue: '',
},
key: {
type: DataTypes.TEXT, // 对应的minio的文件路径
defaultValue: '',
},
markType: {
type: DataTypes.TEXT,
defaultValue: 'md', // markdown | json | html | image | video | audio | code | link | file
comment: '类型',
},
description: {
type: DataTypes.TEXT,
defaultValue: '',
},
cover: {
type: DataTypes.TEXT,
defaultValue: '',
comment: '封面',
},
thumbnail: {
type: DataTypes.TEXT,
defaultValue: '',
comment: '缩略图',
},
link: {
type: DataTypes.TEXT,
defaultValue: '',
comment: '链接',
},
tags: {
type: DataTypes.JSONB,
defaultValue: [],
},
summary: {
type: DataTypes.TEXT,
defaultValue: '',
comment: '摘要',
},
config: {
type: DataTypes.JSONB,
defaultValue: {},
},
data: {
type: DataTypes.JSONB,
defaultValue: {},
},
fileList: {
type: DataTypes.JSONB,
defaultValue: [],
},
uname: {
type: DataTypes.STRING,
defaultValue: '',
comment: '用户的名称, 更新后的用户的名称',
},
version: {
type: DataTypes.INTEGER, // 更新刷新版本,多人协作
defaultValue: 1,
},
markedAt: {
type: DataTypes.DATE,
allowNull: true,
comment: '标记时间',
},
uid: {
type: DataTypes.UUID,
allowNull: true,
},
puid: {
type: DataTypes.UUID,
allowNull: true,
},
};
const InitModel = Model || MarkModel;
InitModel.init(callInit ? callInit(modelAttribute) : modelAttribute, {
sequelize,
paranoid: true,
...optsRest,
});
if (sync && sync.sync) {
const { sync: _, ...rest } = sync;
MarkModel.sync({ alter: true, logging: false, ...rest }).catch((e) => {
console.error('MarkModel sync', e);
});
}
};
export const markModelInit = MarkMInit;
export const syncMarkModel = async (sync?: Opts, tableName = 'micro_mark') => {
const sequelize = await useContextKey('sequelize');
await MarkMInit({ sequelize, tableName }, sync);
};

View File

@@ -1,5 +0,0 @@
export * from '@kevisual/code-center-module/src/mark/mark-model.ts';
import { markModelInit, MarkModel, syncMarkModel } from '@kevisual/code-center-module/src/mark/mark-model.ts';
export { markModelInit, MarkModel };
syncMarkModel({ sync: true, alter: true, logging: false });

View File

@@ -1,4 +1,4 @@
import { app, sequelize } from '@/app.ts';
import { app } from '@/app.ts';
import { Org } from '@/models/org.ts';
import { User } from '@/models/user.ts';
import { Op } from 'sequelize';

View File

@@ -1,50 +0,0 @@
import { program, Command } from '../program.ts';
import { initUser, logger, close } from './common.ts';
const usrCommand = new Command('user').description('用户相关操作');
program.addCommand(usrCommand);
const changePwd = new Command('pwd')
.description('修改用户密码')
.option('-u, --username <username>', '用户名')
.option('-p, --password <password>', '新密码')
.action(async (opts) => {
const username = opts.username;
const password = opts.password;
if (!username) {
logger.error('用户名不能为空');
close();
return;
}
const { User } = await initUser();
const newPassword = password || 'kevisual';
logger.info(`用户名: ${username}`);
logger.info(`新密码: ${newPassword}`);
const user = await User.findOne({ where: { username: username }, logging: false });
if (!user) {
logger.error('用户不存在');
return;
}
const newP = await user.createPassword(newPassword);
logger.info('新密码加密成功', '新密码: ', newPassword);
close();
});
usrCommand.addCommand(changePwd);
const list = new Command('list').description('列出所有用户').action(async () => {
console.log('列出所有用户 start');
const { User } = await initUser();
console.log('列出所有用户');
const users = await User.findAll({ limit: 10, order: [['createdAt', 'DESC']] });
if (users.length === 0) {
logger.info('没有用户');
return;
}
users.forEach((user) => {
console.log(`用户名: ${user.username}`);
});
console.log(`用户数量: ${users.length}`);
await close();
});
usrCommand.addCommand(list);

View File

@@ -1,3 +0,0 @@
import * as redisLib from '../modules/redis.ts';
import { useContextKey, useContext } from '@kevisual/context';
export const redis = useContextKey('redis', () => redisLib.redis);

View File

@@ -1,34 +0,0 @@
import { config } from '../modules/config.ts';
import { sequelize } from '../modules/sequelize.ts';
export { program, Command } from '../program.ts';
import { User, UserInit, OrgInit, Org, UserSecretInit, UserSecret } from '../auth/index.ts';
import { Logger } from '@kevisual/logger';
export const close = async () => {
process.exit(0);
};
export { sequelize };
export const logger = new Logger({
level: (config?.LOG_LEVEL || 'info') as any,
showTime: true,
});
export const initUser = async () => {
console.log('init user');
await UserInit(sequelize, undefined, {
alter: true,
logging: false,
});
await OrgInit(sequelize, undefined, {
alter: true,
logging: false,
});
await UserSecretInit(sequelize, undefined, {
alter: true,
logging: false,
});
return {
User: User,
Org: Org,
UserSecret: UserSecret,
};
};

View File

@@ -1,12 +0,0 @@
import { AppListModel, AppModel } from '../routes/app-manager/module/index.ts';
import { program, Command, close } from './common.ts';
const app = program.command('app');
const appList = new Command('list').action(async () => {
const list = await AppListModel.findAll();
console.log(list.map((item) => item.toJSON()));
close();
});
app.addCommand(appList);

View File

@@ -1,22 +0,0 @@
import { Client } from 'minio'
export async function createBucket(client: Client, bucketName: string) {
const exists = await client.bucketExists(bucketName)
if (!exists) {
await client.makeBucket(bucketName, '')
console.log(`Bucket "${bucketName}" created successfully.`)
} else {
console.log(`Bucket "${bucketName}" already exists.`)
}
}
const minioClient = new Client({
endPoint: 'minio.kevisual.cn',
port: 9000,
useSSL: false,
accessKey: 'admin',
secretKey: 'xiongxiao',
})
createBucket(minioClient, 'nocodb').catch((err) => {
console.error('Error creating bucket:', err)
})

View File

@@ -1,62 +0,0 @@
import { sequelize } from '../modules/sequelize.ts';
import { initUser } from '../scripts/common.ts';
import '../scripts/common-redis.ts';
import { useContextKey } from '@kevisual/context';
export const main = async () => {
const models = await initUser();
const username = 'root';
const orgname = 'admin';
const user = await models.User.findOne({ where: { username } });
const org = await models.User.findOne({ where: { username: orgname } });
console.log('user.id', user?.id);
console.log('org.id', org?.id);
// const userSecret1 = await models.UserSecret.createSecret(user?.id!);
// userSecret1.title = 'root secret';
// await userSecret1.save();
// await models.UserSecret.destroy({
// where: {
// orgId: '16a496d4-8cd6-4e02-b403-c2adc006a53d',
// },
// });
const userSecret2 = await models.UserSecret.createSecret(user?.id!, org?.id!);
userSecret2.title = 'root org secret';
await userSecret2.save();
const secretList = await models.UserSecret.findAll();
for (const secret of secretList) {
console.log(`\nSecret ID: ${secret.id}, User ID: ${secret.userId}, Org ID: ${secret.orgId}, Token: ${secret.token}, Expired Time: ${secret.expiredTime}`);
}
process.exit(0);
};
main();
export const dropTable = async () => {
await sequelize.query('DROP TABLE IF EXISTS "cf_user_secrets"');
console.log('UserSecret table dropped');
process.exit(0);
};
// dropTable()
const token1 = 'sk_tvwzgp5lky8iupawh0encvd52vji4o8argvd2x668gn15q83xpgo8fe10ny7wfsq';
const orgToken2 = 'sk_x37p8iifh6k18c3f121w49nmfy1sbjqpyol9fcsz0lmc5dz493wrfwvtxc4gi9od';
export const main2 = async () => {
const redis = useContextKey('redis');
if (!redis) {
console.error('Redis is not initialized');
return;
}
const models = await initUser();
const UserSecret = models.UserSecret;
const v = await models.UserSecret.verifyToken(token1);
console.log('verifyToken', v);
process.exit(0);
};
// main2();

View File

@@ -1,46 +0,0 @@
import { useContextKey } from '@kevisual/context';
import { sequelize } from '../modules/sequelize.ts';
import { MarkModel, syncMarkModel } from '../routes/mark/model.ts';
export const sequelize2 = useContextKey('sequelize', () => sequelize);
const main = async () => {
// 把所有markmodel的表的source字段的类型改为jsonb
// const marks = await MarkModel.findAll();
// const mark = marks[0];
// for (const mark of marks) {
// if (mark.source) {
// try {
// await MarkModel.update({ source: {} }, { where: { id: mark.id } });
// } catch (e) {
// console.error('update source error:', e);
// }
// }
// }
console.log('update source success');
// await MarkModel.sync({ alter: true, logging: true }).catch((e) => {
// console.error('MarkModel.sync error:', e);
// });
await syncMarkModel({ alter: true, logging: true, sync: true });
};
main();
const sql = `ALTER TABLE "micro_mark" ALTER COLUMN "source" DROP NOT NULL;ALTER TABLE "micro_mark" ALTER COLUMN "source" SET DEFAULT '{}';ALTER TABLE "micro_mark" ALTER COLUMN "source" TYPE JSONB ; COMMENT ON COLUMN "micro_mark"."source" IS '需要的数据的来源,作为一个备注使用。';`;
// sequelize
/**
* 失败
*/
const runSql = async () => {
sequelize
.query(sql)
.then(() => {
console.log('update source success');
})
.catch((e) => {
console.error('update source error:', e);
});
};
// runSql();

View File

@@ -1,48 +0,0 @@
import { sequelize } from '../modules/sequelize.ts';
import { User, UserInit, UserServices, Org, OrgInit } from '../auth/index.ts';
// User.sync({ alter: true, logging: true }).then(() => {
// console.log('sync user done');
// });
// class UserChange extends User {
// static async syncUser() {
// await UserChange.sync({ alter: true, logging: false });
// console.log('sync user done');
// }
// }
export const main = async () => {
await UserInit(sequelize, null, {
alter: true,
logging: false,
});
await OrgInit(sequelize, null, {
alter: true,
logging: false,
});
const user = await User.findAll({});
for (const u of user) {
console.log(u.username, u.type);
}
};
main();
export const changeRootPassword = async () => {
await OrgInit(sequelize, null, {
alter: true,
logging: false,
});
await UserInit(sequelize, null, {
alter: true,
logging: false,
});
const user = await User.findOne({ where: { username: 'root' } });
if (user) {
await user.createPassword('');
await user.save();
console.log('change root password done');
process.exit(0);
}
};
// changeRootPassword();

View File

@@ -1,10 +0,0 @@
import { sequelize } from '../modules/sequelize.ts';
console.log('sequelize');
// 获取所有表名
const [tables] = await sequelize.query(
"SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';"
);
console.log('tables', tables);