fix: 解决问题
This commit is contained in:
12
drizzle.config.ts
Normal file
12
drizzle.config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { Config } from 'drizzle-kit';
|
||||
import 'dotenv/config';
|
||||
const url = process.env.DATABASE_URL!;
|
||||
|
||||
export default {
|
||||
schema: './src/db/schema.ts',
|
||||
out: './src/db/drizzle',
|
||||
dialect: 'postgresql',
|
||||
dbCredentials: {
|
||||
url,
|
||||
},
|
||||
} satisfies Config;
|
||||
27
package.json
27
package.json
@@ -34,6 +34,9 @@
|
||||
"ssl": "ssh -L 5432:localhost:5432 light",
|
||||
"ssl:redis": "ssh -L 6379:localhost:6379 light",
|
||||
"ssl:minio": "ssh -L 9000:localhost:9000 light",
|
||||
"studio": "npx drizzle-kit studio",
|
||||
"drizzle:migrate": "npx drizzle-kit migrate",
|
||||
"drizzle:push": "npx drizzle-kit push",
|
||||
"pub": "envision pack -p -u -c"
|
||||
},
|
||||
"keywords": [],
|
||||
@@ -48,10 +51,10 @@
|
||||
"@types/busboy": "^1.5.4",
|
||||
"@types/send": "^1.2.1",
|
||||
"@types/ws": "^8.18.1",
|
||||
"bullmq": "^5.66.2",
|
||||
"bullmq": "^5.66.3",
|
||||
"busboy": "^1.6.0",
|
||||
"commander": "^14.0.2",
|
||||
"cookie": "^1.1.1",
|
||||
"drizzle-kit": "^0.31.8",
|
||||
"drizzle-orm": "^0.45.1",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"ioredis": "^5.8.2",
|
||||
@@ -59,7 +62,9 @@
|
||||
"pg": "^8.16.3",
|
||||
"pm2": "^6.0.14",
|
||||
"send": "^1.2.1",
|
||||
"sequelize": "^6.37.7"
|
||||
"sequelize": "^6.37.7",
|
||||
"ws": "npm:@kevisual/ws",
|
||||
"xml2js": "^0.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kevisual/code-center-module": "0.0.24",
|
||||
@@ -69,7 +74,7 @@
|
||||
"@kevisual/logger": "^0.0.4",
|
||||
"@kevisual/oss": "0.0.13",
|
||||
"@kevisual/permission": "^0.0.3",
|
||||
"@kevisual/router": "0.0.48",
|
||||
"@kevisual/router": "0.0.50",
|
||||
"@kevisual/types": "^0.0.10",
|
||||
"@kevisual/use-config": "^1.0.21",
|
||||
"@types/archiver": "^7.0.0",
|
||||
@@ -79,10 +84,13 @@
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^25.0.3",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@types/xml2js": "^0.4.14",
|
||||
"archiver": "^7.0.1",
|
||||
"better-sqlite3": "^12.5.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.19",
|
||||
"dotenv": "^17.2.3",
|
||||
"es-toolkit": "^1.43.0",
|
||||
"ioredis": "^5.8.2",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"lodash-es": "^4.17.22",
|
||||
@@ -94,10 +102,6 @@
|
||||
"pm2": "^6.0.14",
|
||||
"semver": "^7.7.3",
|
||||
"sequelize": "^6.37.7",
|
||||
"socket.io": "^4.8.1",
|
||||
"strip-ansi": "^7.1.2",
|
||||
"tar": "^7.5.2",
|
||||
"ws": "npm:@kevisual/ws",
|
||||
"zod": "^4.2.1"
|
||||
},
|
||||
"resolutions": {
|
||||
@@ -107,11 +111,8 @@
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"esbuild",
|
||||
"sqlite3"
|
||||
],
|
||||
"ignoredBuiltDependencies": [
|
||||
"msgpackr-extract"
|
||||
"better-sqlite3"
|
||||
]
|
||||
},
|
||||
"packageManager": "pnpm@10.26.1"
|
||||
"packageManager": "pnpm@10.26.2"
|
||||
}
|
||||
957
pnpm-lock.yaml
generated
957
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,2 @@
|
||||
packages:
|
||||
- 'wxmsg/*'
|
||||
- 'wxmsg'
|
||||
|
||||
155
scripts/clear.sql
Normal file
155
scripts/clear.sql
Normal file
@@ -0,0 +1,155 @@
|
||||
-- =====================================================
|
||||
-- 删除 cf_orgs 表中所有带数字的重复唯一约束
|
||||
-- 保留 cf_orgs_username_key(不带数字的)
|
||||
-- =====================================================
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
constraint_record RECORD;
|
||||
BEGIN
|
||||
FOR constraint_record IN
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'cf_orgs'::regclass
|
||||
AND conname LIKE 'cf_orgs_username_key%'
|
||||
AND conname != 'cf_orgs_username_key'
|
||||
AND conname ~ '[0-9]' -- 包含数字的
|
||||
ORDER BY conname
|
||||
LOOP
|
||||
EXECUTE format('ALTER TABLE cf_orgs DROP CONSTRAINT IF EXISTS %I', constraint_record.conname);
|
||||
RAISE NOTICE '已删除约束: %', constraint_record.conname;
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
-- =====================================================
|
||||
-- 删除 cf_user 表中所有带数字的重复唯一约束
|
||||
-- 保留 cf_user_username_key(不带数字的)
|
||||
-- =====================================================
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
constraint_record RECORD;
|
||||
BEGIN
|
||||
FOR constraint_record IN
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'cf_user'::regclass
|
||||
AND conname LIKE 'cf_user_username_key%'
|
||||
AND conname != 'cf_user_username_key'
|
||||
AND conname ~ '[0-9]' -- 包含数字的
|
||||
ORDER BY conname
|
||||
LOOP
|
||||
EXECUTE format('ALTER TABLE cf_user DROP CONSTRAINT IF EXISTS %I', constraint_record.conname);
|
||||
RAISE NOTICE '已删除约束: %', constraint_record.conname;
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
-- =====================================================
|
||||
-- 验证删除结果
|
||||
-- =====================================================
|
||||
|
||||
-- 查看 cf_orgs 剩余的 username 约束
|
||||
SELECT 'cf_orgs 表剩余的 username 约束:' AS info;
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'cf_orgs'::regclass
|
||||
AND conname LIKE 'cf_orgs_username_key%'
|
||||
ORDER BY conname;
|
||||
|
||||
-- 查看 cf_user 剩余的 username 约束
|
||||
SELECT 'cf_user 表剩余的 username 约束:' AS info;
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'cf_user'::regclass
|
||||
AND conname LIKE 'cf_user_username_key%'
|
||||
ORDER BY conname;
|
||||
-- =====================================================
|
||||
-- 删除 kv_app_domain 表中所有带数字的重复唯一约束
|
||||
-- 保留 kv_app_domain_domain_key(不带数字的)
|
||||
-- =====================================================
|
||||
DO $$
|
||||
DECLARE
|
||||
constraint_record RECORD;
|
||||
BEGIN
|
||||
FOR constraint_record IN
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'kv_app_domain'::regclass
|
||||
AND conname LIKE 'kv_app_domain_domain_key%'
|
||||
AND conname != 'kv_app_domain_domain_key'
|
||||
AND conname ~ '[0-9]' -- 包含数字的
|
||||
ORDER BY conname
|
||||
LOOP
|
||||
EXECUTE format('ALTER TABLE kv_app_domain DROP CONSTRAINT IF EXISTS %I', constraint_record.conname);
|
||||
RAISE NOTICE '已删除约束: %', constraint_record.conname;
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
-- =====================================================
|
||||
-- 删除 prompts 表中所有带数字的重复唯一约束
|
||||
-- 保留 prompts_key_key(不带数字的)
|
||||
-- =====================================================
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
constraint_record RECORD;
|
||||
BEGIN
|
||||
FOR constraint_record IN
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'prompts'::regclass
|
||||
AND conname LIKE 'prompts_key_key%'
|
||||
AND conname != 'prompts_key_key'
|
||||
AND conname ~ '[0-9]' -- 包含数字的
|
||||
ORDER BY conname
|
||||
LOOP
|
||||
EXECUTE format('ALTER TABLE prompts DROP CONSTRAINT IF EXISTS %I', constraint_record.conname);
|
||||
RAISE NOTICE '已删除约束: %', constraint_record.conname;
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
-- =====================================================
|
||||
-- 删除 apps_trades 表中所有带数字的重复唯一约束
|
||||
-- 保留 apps_trades_out_trade_no_key(不带数字的)
|
||||
-- =====================================================
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
constraint_record RECORD;
|
||||
BEGIN
|
||||
FOR constraint_record IN
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'apps_trades'::regclass
|
||||
AND conname LIKE 'apps_trades_out_trade_no_key%'
|
||||
AND conname != 'apps_trades_out_trade_no_key'
|
||||
AND conname ~ '[0-9]' -- 包含数字的
|
||||
ORDER BY conname
|
||||
LOOP
|
||||
EXECUTE format('ALTER TABLE apps_trades DROP CONSTRAINT IF EXISTS %I', constraint_record.conname);
|
||||
RAISE NOTICE '已删除约束: %', constraint_record.conname;
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
-- =====================================================
|
||||
-- 删除 ai_agent 表中所有带数字的重复唯一约束
|
||||
-- 保留 ai_agent_key_key(不带数字的)
|
||||
-- =====================================================
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
constraint_record RECORD;
|
||||
BEGIN
|
||||
FOR constraint_record IN
|
||||
SELECT conname
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'ai_agent'::regclass
|
||||
AND conname LIKE 'ai_agent_key_key%'
|
||||
AND conname != 'ai_agent_key_key'
|
||||
AND conname ~ '[0-9]' -- 包含数字的
|
||||
ORDER BY conname
|
||||
LOOP
|
||||
EXECUTE format('ALTER TABLE ai_agent DROP CONSTRAINT IF EXISTS %I', constraint_record.conname);
|
||||
RAISE NOTICE '已删除约束: %', constraint_record.conname;
|
||||
END LOOP;
|
||||
END $$;
|
||||
6987
src/db/drizzle/0000_groovy_red_skull.sql
Normal file
6987
src/db/drizzle/0000_groovy_red_skull.sql
Normal file
File diff suppressed because it is too large
Load Diff
48566
src/db/drizzle/meta/0000_snapshot.json
Normal file
48566
src/db/drizzle/meta/0000_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
13
src/db/drizzle/meta/_journal.json
Normal file
13
src/db/drizzle/meta/_journal.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1766803308366,
|
||||
"tag": "0000_groovy_red_skull",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
3
src/db/drizzle/relations.ts
Normal file
3
src/db/drizzle/relations.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { relations } from "drizzle-orm/relations";
|
||||
import { } from "./schema";
|
||||
|
||||
400
src/db/drizzle/schema.ts
Normal file
400
src/db/drizzle/schema.ts
Normal file
@@ -0,0 +1,400 @@
|
||||
import { pgTable, serial, text, jsonb, varchar, timestamp, unique, uuid, doublePrecision, json, integer, boolean, index, uniqueIndex, pgEnum } from "drizzle-orm/pg-core"
|
||||
import { sql } from "drizzle-orm"
|
||||
|
||||
export const enumCfRouterCodeType = pgEnum("enum_cf_router_code_type", ['route', 'middleware'])
|
||||
|
||||
|
||||
export const testPromptTools = pgTable("TestPromptTools", {
|
||||
id: serial().primaryKey().notNull(),
|
||||
template: text().notNull(),
|
||||
args: jsonb().notNull(),
|
||||
process: jsonb().notNull(),
|
||||
type: varchar({ length: 255 }).notNull(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
});
|
||||
|
||||
export const aiAgent = pgTable("ai_agent", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
type: varchar({ length: 255 }).notNull(),
|
||||
baseUrl: varchar({ length: 255 }).notNull(),
|
||||
apiKey: varchar({ length: 255 }).notNull(),
|
||||
temperature: doublePrecision(),
|
||||
cache: varchar({ length: 255 }),
|
||||
cacheName: varchar({ length: 255 }),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
model: varchar({ length: 255 }).notNull(),
|
||||
data: json().default({}),
|
||||
status: varchar({ length: 255 }).default('open'),
|
||||
key: varchar({ length: 255 }).notNull(),
|
||||
description: text(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
}, (table) => [
|
||||
unique("ai_agent_key_key").on(table.key),
|
||||
]);
|
||||
|
||||
export const appsTrades = pgTable("apps_trades", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
outTradeNo: varchar("out_trade_no", { length: 255 }).notNull(),
|
||||
money: integer().notNull(),
|
||||
subject: text().notNull(),
|
||||
status: varchar({ length: 255 }).default('WAIT_BUYER_PAY').notNull(),
|
||||
type: varchar({ length: 255 }).default('alipay').notNull(),
|
||||
data: jsonb().default({"list":[]}),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
}, (table) => [
|
||||
unique("apps_trades_out_trade_no_key").on(table.outTradeNo),
|
||||
]);
|
||||
|
||||
export const cfOrgs = pgTable("cf_orgs", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
username: varchar({ length: 255 }).notNull(),
|
||||
users: jsonb().default([]),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
description: varchar({ length: 255 }),
|
||||
}, (table) => [
|
||||
unique("cf_orgs_username_key").on(table.username),
|
||||
]);
|
||||
|
||||
export const cfRouterCode = pgTable("cf_router_code", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
path: varchar({ length: 255 }).notNull(),
|
||||
key: varchar({ length: 255 }).notNull(),
|
||||
active: boolean().default(false),
|
||||
project: varchar({ length: 255 }).default('default'),
|
||||
code: text().default('),
|
||||
type: enumCfRouterCodeType().default('route'),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
middleware: varchar({ length: 255 }).array().default(["RRAY[]::character varying[])::character varying(25"]),
|
||||
next: varchar({ length: 255 }).default('),
|
||||
exec: text().default('),
|
||||
data: json().default({}),
|
||||
validator: json().default({}),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
});
|
||||
|
||||
export const cfUser = pgTable("cf_user", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
username: varchar({ length: 255 }).notNull(),
|
||||
password: varchar({ length: 255 }),
|
||||
salt: varchar({ length: 255 }),
|
||||
needChangePassword: boolean().default(false),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
description: text(),
|
||||
data: jsonb().default({}),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
type: varchar({ length: 255 }).default('user'),
|
||||
owner: uuid(),
|
||||
orgId: uuid(),
|
||||
email: varchar({ length: 255 }),
|
||||
avatar: text(),
|
||||
nickname: text(),
|
||||
}, (table) => [
|
||||
unique("cf_user_username_key").on(table.username),
|
||||
]);
|
||||
|
||||
export const cfUserSecrets = pgTable("cf_user_secrets", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
description: text(),
|
||||
status: varchar({ length: 255 }).default('active'),
|
||||
title: text(),
|
||||
expiredTime: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
token: varchar({ length: 255 }).default(').notNull(),
|
||||
userId: uuid(),
|
||||
data: jsonb().default({}),
|
||||
orgId: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
});
|
||||
|
||||
export const chatHistories = pgTable("chat_histories", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
data: json(),
|
||||
chatId: uuid(),
|
||||
chatPromptId: uuid(),
|
||||
root: boolean().default(false),
|
||||
show: boolean().default(true),
|
||||
uid: varchar({ length: 255 }),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
role: varchar({ length: 255 }).default('user'),
|
||||
});
|
||||
|
||||
export const chatPrompts = pgTable("chat_prompts", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: varchar({ length: 255 }).notNull(),
|
||||
description: text(),
|
||||
data: json(),
|
||||
key: varchar({ length: 255 }).default(').notNull(),
|
||||
uid: varchar({ length: 255 }),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
});
|
||||
|
||||
export const chatSessions = pgTable("chat_sessions", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
data: json().default({}),
|
||||
chatPromptId: uuid(),
|
||||
type: varchar({ length: 255 }).default('production'),
|
||||
uid: varchar({ length: 255 }),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
title: varchar({ length: 255 }).default('),
|
||||
key: varchar({ length: 255 }),
|
||||
});
|
||||
|
||||
export const fileSync = pgTable("file_sync", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
name: varchar({ length: 255 }),
|
||||
hash: varchar({ length: 255 }),
|
||||
stat: jsonb().default({}),
|
||||
data: jsonb().default({}),
|
||||
checkedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
}, (table) => [
|
||||
index("file_sync_name_idx").using("btree", table.name.asc().nullsLast().op("text_ops")),
|
||||
]);
|
||||
|
||||
export const kvAiChatHistory = pgTable("kv_ai_chat_history", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
username: varchar({ length: 255 }).default(').notNull(),
|
||||
model: varchar({ length: 255 }).default(').notNull(),
|
||||
group: varchar({ length: 255 }).default(').notNull(),
|
||||
title: varchar({ length: 255 }).default(').notNull(),
|
||||
messages: jsonb().default([]).notNull(),
|
||||
promptTokens: integer("prompt_tokens").default(0),
|
||||
totalTokens: integer("total_tokens").default(0),
|
||||
completionTokens: integer("completion_tokens").default(0),
|
||||
data: jsonb().default({}),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
version: integer().default(0),
|
||||
type: varchar({ length: 255 }).default('keep').notNull(),
|
||||
});
|
||||
|
||||
export const kvApp = pgTable("kv_app", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
data: jsonb().default({}),
|
||||
version: varchar({ length: 255 }).default('),
|
||||
key: varchar({ length: 255 }),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
title: varchar({ length: 255 }).default('),
|
||||
description: varchar({ length: 255 }).default('),
|
||||
user: varchar({ length: 255 }),
|
||||
status: varchar({ length: 255 }).default('running'),
|
||||
pid: uuid(),
|
||||
proxy: boolean().default(false),
|
||||
}, (table) => [
|
||||
uniqueIndex("kv_app_key_uid").using("btree", table.key.asc().nullsLast().op("text_ops"), table.uid.asc().nullsLast().op("text_ops")),
|
||||
unique("key_uid_unique").on(table.key, table.uid),
|
||||
]);
|
||||
|
||||
export const kvAppDomain = pgTable("kv_app_domain", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
domain: varchar({ length: 255 }).notNull(),
|
||||
appId: varchar({ length: 255 }),
|
||||
uid: varchar({ length: 255 }),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
data: jsonb(),
|
||||
status: varchar({ length: 255 }).default('running').notNull(),
|
||||
}, (table) => [
|
||||
unique("kv_app_domain_domain_key").on(table.domain),
|
||||
]);
|
||||
|
||||
export const kvAppList = pgTable("kv_app_list", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
data: json().default({}),
|
||||
version: varchar({ length: 255 }).default('),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
key: varchar({ length: 255 }),
|
||||
status: varchar({ length: 255 }).default('running'),
|
||||
});
|
||||
|
||||
export const kvConfig = pgTable("kv_config", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: text().default('),
|
||||
key: text().default('),
|
||||
description: text().default('),
|
||||
tags: jsonb().default([]),
|
||||
data: jsonb().default({}),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
hash: text().default('),
|
||||
});
|
||||
|
||||
export const kvContainer = pgTable("kv_container", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: text().default('),
|
||||
description: text().default('),
|
||||
type: varchar({ length: 255 }).default('render-js'),
|
||||
code: text().default('),
|
||||
data: json().default({}),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
uid: uuid(),
|
||||
publish: json().default({}),
|
||||
tags: json().default([]),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
hash: text().default('),
|
||||
});
|
||||
|
||||
export const kvGithub = pgTable("kv_github", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: varchar({ length: 255 }).default('),
|
||||
githubToken: varchar({ length: 255 }).default('),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
});
|
||||
|
||||
export const kvPackages = pgTable("kv_packages", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: text().default('),
|
||||
description: text().default('),
|
||||
tags: jsonb().default([]),
|
||||
data: jsonb().default({}),
|
||||
publish: jsonb().default({}),
|
||||
expand: jsonb().default({}),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
});
|
||||
|
||||
export const kvPage = pgTable("kv_page", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: varchar({ length: 255 }).default('),
|
||||
description: text().default('),
|
||||
type: varchar({ length: 255 }).default('),
|
||||
data: json().default({}),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
publish: json().default({}),
|
||||
});
|
||||
|
||||
export const kvResource = pgTable("kv_resource", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
name: varchar({ length: 255 }).default('),
|
||||
description: text().default('),
|
||||
source: varchar({ length: 255 }).default('),
|
||||
sourceId: varchar({ length: 255 }).default('),
|
||||
version: varchar({ length: 255 }).default('0.0.0'),
|
||||
data: json().default({}),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
});
|
||||
|
||||
export const kvVip = pgTable("kv_vip", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
userId: uuid().notNull(),
|
||||
level: varchar({ length: 255 }).default('free'),
|
||||
category: varchar({ length: 255 }).notNull(),
|
||||
startDate: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
endDate: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
data: jsonb().default({}),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
title: text().default(').notNull(),
|
||||
description: text().default(').notNull(),
|
||||
});
|
||||
|
||||
export const microAppsUpload = pgTable("micro_apps_upload", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: varchar({ length: 255 }).default('),
|
||||
description: varchar({ length: 255 }).default('),
|
||||
tags: jsonb().default([]),
|
||||
type: varchar({ length: 255 }).default('),
|
||||
source: varchar({ length: 255 }).default('),
|
||||
data: jsonb().default({}),
|
||||
share: boolean().default(false),
|
||||
uname: varchar({ length: 255 }).default('),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
});
|
||||
|
||||
export const microMark = pgTable("micro_mark", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: text().default('),
|
||||
description: text().default('),
|
||||
tags: jsonb().default([]),
|
||||
data: jsonb().default({}),
|
||||
uname: varchar({ length: 255 }).default('),
|
||||
uid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
cover: text().default('),
|
||||
thumbnail: text().default('),
|
||||
link: text().default('),
|
||||
summary: text().default('),
|
||||
markType: text().default('md'),
|
||||
config: jsonb().default({}),
|
||||
puid: uuid(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
version: integer().default(1),
|
||||
fileList: jsonb().default([]),
|
||||
key: text().default('),
|
||||
});
|
||||
|
||||
export const prompts = pgTable("prompts", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: varchar({ length: 255 }).notNull(),
|
||||
description: text(),
|
||||
presetData: json(),
|
||||
key: varchar({ length: 255 }).notNull(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
}, (table) => [
|
||||
unique("prompts_key_key").on(table.key),
|
||||
]);
|
||||
|
||||
export const workShareMark = pgTable("work_share_mark", {
|
||||
id: uuid().primaryKey().notNull(),
|
||||
title: text().default('),
|
||||
key: text().default('),
|
||||
markType: text().default('md'),
|
||||
description: text().default('),
|
||||
cover: text().default('),
|
||||
link: text().default('),
|
||||
tags: jsonb().default([]),
|
||||
summary: text().default('),
|
||||
config: jsonb().default({}),
|
||||
data: jsonb().default({}),
|
||||
fileList: jsonb().default([]),
|
||||
uname: varchar({ length: 255 }).default('),
|
||||
version: integer().default(1),
|
||||
markedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
uid: uuid(),
|
||||
puid: uuid(),
|
||||
createdAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
updatedAt: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
deletedAt: timestamp({ withTimezone: true, mode: 'string' }),
|
||||
});
|
||||
14
src/db/schema.ts
Normal file
14
src/db/schema.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { pgTable, serial, text, varchar, uuid, boolean, jsonb, timestamp } from "drizzle-orm/pg-core";
|
||||
import { InferSelectModel, InferInsertModel } from "drizzle-orm";
|
||||
|
||||
export const aiUsages = pgTable('cf_ai_usage_cache', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
owner: uuid('owner'),
|
||||
data: jsonb('data').notNull().default({}),
|
||||
createdAt: timestamp('createdAt').notNull().defaultNow(),
|
||||
updatedAt: timestamp('updatedAt').notNull().defaultNow(),
|
||||
deletedAt: timestamp('deletedAt'),
|
||||
});
|
||||
|
||||
// 类型推断
|
||||
export type AiUsage = InferSelectModel<typeof aiUsages>;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { User } from '@/models/user.ts';
|
||||
import http from 'http';
|
||||
import cookie from 'cookie';
|
||||
import * as cookie from '@kevisual/router/src/server/cookie.ts';
|
||||
import { logger } from './logger.ts';
|
||||
export const error = (msg: string, code = 500) => {
|
||||
return JSON.stringify({ code, message: msg });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { User } from '@/models/user.ts';
|
||||
import http from 'http';
|
||||
import cookie from 'cookie';
|
||||
import { parse } from '@kevisual/router/src/server/cookie.ts';
|
||||
export const error = (msg: string, code = 500) => {
|
||||
return JSON.stringify({ code, message: msg });
|
||||
};
|
||||
@@ -16,7 +16,7 @@ export const checkAuth = async (req: http.IncomingMessage, res: http.ServerRespo
|
||||
token = url.searchParams.get('token') || '';
|
||||
}
|
||||
if (!token) {
|
||||
const parsedCookies = cookie.parse(req.headers.cookie || '');
|
||||
const parsedCookies = parse(req.headers.cookie || '');
|
||||
token = parsedCookies.token || '';
|
||||
}
|
||||
if (!token) {
|
||||
@@ -44,7 +44,7 @@ export const getLoginUser = async (req: http.IncomingMessage) => {
|
||||
token = url.searchParams.get('token') || '';
|
||||
}
|
||||
if (!token) {
|
||||
const parsedCookies = cookie.parse(req.headers.cookie || '');
|
||||
const parsedCookies = parse(req.headers.cookie || '');
|
||||
token = parsedCookies.token || '';
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export {
|
||||
}
|
||||
export const config = useConfig();
|
||||
|
||||
export const token = config.TOKEN || '';
|
||||
export const token = config.KEVISUAL_TOKEN || '';
|
||||
|
||||
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
export const showRes = (res, ...args) => {
|
||||
|
||||
@@ -23,13 +23,13 @@
|
||||
],
|
||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@10.24.0",
|
||||
"packageManager": "pnpm@10.26.2",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@kevisual/context": "^0.0.4",
|
||||
"@kevisual/query": "^0.0.29",
|
||||
"@kevisual/router": "0.0.33",
|
||||
"@types/node": "^24.10.1",
|
||||
"@kevisual/query": "^0.0.33",
|
||||
"@kevisual/router": "0.0.50",
|
||||
"@types/node": "^25.0.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"xml2js": "^0.6.2"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { SimpleRouter } from '@kevisual/router/simple';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import xml2js from 'xml2js';
|
||||
import { useContextKey } from '@kevisual/context';
|
||||
import { Redis } from 'ioredis';
|
||||
import http from 'node:http';
|
||||
@@ -8,6 +7,7 @@ import { Wx, WxMsgEvent, parseWxMessage } from './wx/index.ts';
|
||||
import { contextConfig as config } from './modules/config.ts';
|
||||
import { loginByTicket } from './wx/login-by-ticket.ts';
|
||||
import { Queue } from 'bullmq';
|
||||
import { parseXml } from './utils/get-json-from-xml.ts';
|
||||
export const simpleRouter: SimpleRouter = await useContextKey('router');
|
||||
export const redis: Redis = await useContextKey('redis');
|
||||
export const wxmsgQueue = useContextKey<Queue>('wxmsgQueue', () => {
|
||||
@@ -36,40 +36,9 @@ simpleRouter.get('/api/wxmsg', async (req: http.IncomingMessage, res: http.Serve
|
||||
}
|
||||
});
|
||||
|
||||
export const getJsonFromXml = async (req: http.IncomingMessage): Promise<any> => {
|
||||
return await new Promise((resolve) => {
|
||||
// 读取请求数据
|
||||
let data = '';
|
||||
req.setEncoding('utf8');
|
||||
// 监听data事件,接收数据片段
|
||||
req.on('data', (chunk: string) => {
|
||||
data += chunk;
|
||||
});
|
||||
// 当请求结束时处理数据
|
||||
req.on('end', () => {
|
||||
try {
|
||||
// 使用xml2js解析XML
|
||||
xml2js.parseString(data, function (err, result) {
|
||||
if (err) {
|
||||
console.error('XML解析错误:', err);
|
||||
resolve(null);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('处理请求时出错:', error);
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
simpleRouter.post('/api/wxmsg', async (req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
try {
|
||||
const xml = await getJsonFromXml(req);
|
||||
const xml = await parseXml(req);
|
||||
const msgOrigin = xml?.xml;
|
||||
if (!msgOrigin) {
|
||||
console.error('No message received');
|
||||
|
||||
52
wxmsg/src/utils/get-json-from-xml.ts
Normal file
52
wxmsg/src/utils/get-json-from-xml.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import xml2js from 'xml2js';
|
||||
import { isBun } from '../utils/is-engine.ts';
|
||||
import http from 'http';
|
||||
export const xms2jsParser = async (data: string): Promise<any> => {
|
||||
try {
|
||||
// 使用xml2js解析XML
|
||||
const xml = await xml2js.parseStringPromise(data);
|
||||
return xml;
|
||||
} catch (error) {
|
||||
console.error('XML解析错误:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
export const parseXml = async (req: http.IncomingMessage): Promise<any> => {
|
||||
if (isBun) {
|
||||
// @ts-ignore
|
||||
const body = req.body;
|
||||
let xmlString = '';
|
||||
if (body) {
|
||||
xmlString = body;
|
||||
}
|
||||
if (!xmlString) {
|
||||
// @ts-ignore
|
||||
xmlString = await req.bun?.request?.text?.();
|
||||
}
|
||||
if (xmlString) {
|
||||
return await xms2jsParser(xmlString)
|
||||
}
|
||||
console.error('没有读取到请求体');
|
||||
return null;
|
||||
}
|
||||
return await new Promise((resolve) => {
|
||||
// 读取请求数据
|
||||
let data = '';
|
||||
req.setEncoding('utf8');
|
||||
// 监听data事件,接收数据片段
|
||||
req.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
// 当请求结束时处理数据
|
||||
req.on('end', () => {
|
||||
try {
|
||||
xms2jsParser(data).then((result) => {
|
||||
resolve(result);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('处理请求时出错:', error);
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
19
wxmsg/src/utils/is-engine.ts
Normal file
19
wxmsg/src/utils/is-engine.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
||||
export const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof document.createElement === 'function';
|
||||
// @ts-ignore
|
||||
export const isDeno = typeof Deno !== 'undefined' && typeof Deno.version === 'object' && typeof Deno.version.deno === 'string';
|
||||
// @ts-ignore
|
||||
export const isBun = typeof Bun !== 'undefined' && typeof Bun.version === 'string';
|
||||
|
||||
export const getEngine = () => {
|
||||
if (isBun) {
|
||||
return 'bun';
|
||||
} else if (isNode) {
|
||||
return 'node';
|
||||
} else if (isBrowser) {
|
||||
return 'browser';
|
||||
} else if (isDeno) {
|
||||
return 'deno';
|
||||
}
|
||||
return 'unknown';
|
||||
};
|
||||
Reference in New Issue
Block a user