feat: 添加短链管理功能,包括创建、更新、删除和列表接口

This commit is contained in:
2026-03-10 19:46:50 +08:00
parent 91eaad04d7
commit 48425c6120
25 changed files with 728 additions and 15 deletions

78
scripts/import-data.ts Normal file
View File

@@ -0,0 +1,78 @@
/**
* 导入 short-link JSON 数据到数据库
* 运行: bun run scripts/import-data.ts
*/
import { drizzle } from 'drizzle-orm/node-postgres';
import { shortLink } from '@/db/schemas/n-code-schema.ts';
import { useConfig } from '@kevisual/use-config';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
const config = useConfig() as any;
const DATABASE_URL = config.DATABASE_URL || process.env.DATABASE_URL || '';
if (!DATABASE_URL) {
console.error('缺少 DATABASE_URL 配置');
process.exit(1);
}
const db = drizzle(DATABASE_URL);
// 读取 JSON 数据
const jsonPath = resolve(import.meta.dir, 'ncode-list.json');
const rawData = JSON.parse(readFileSync(jsonPath, 'utf-8')) as Array<{
code: string;
data: Record<string, any>;
description: string;
slug: string;
tags: string[];
title: string;
type: string;
userId: string;
version: string;
}>;
async function importData() {
console.log(`准备导入 ${rawData.length} 条 short-link 数据...`);
let inserted = 0;
let skipped = 0;
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
for (const item of rawData) {
const userId = item.userId && uuidRegex.test(item.userId) ? item.userId : null;
try {
await db
.insert(shortLink)
.values({
slug: item.slug,
code: item.code,
type: item.type || 'link',
version: item.version || '1.0',
title: item.title || '',
description: item.description || '',
tags: item.tags ?? [],
data: item.data ?? {},
userId: userId as any,
})
.onConflictDoNothing();
console.log(` ✓ 导入: slug=${item.slug}, code=${item.code}, title=${item.title}`);
inserted++;
} catch (err: any) {
const cause = err.cause || err;
console.warn(` ✗ 跳过: slug=${item.slug}, code=${item.code}${cause.message || err.message}`);
skipped++;
}
}
console.log(`\n完成: 成功 ${inserted} 条,跳过 ${skipped}`);
process.exit(0);
}
importData().catch((err) => {
console.error('导入失败:', err);
process.exit(1);
});

104
scripts/ncode-list.json Normal file
View File

@@ -0,0 +1,104 @@
[
{
"code": "anwjgg",
"data": {
"link": "https://kevisual.cn/root/name-card/"
},
"description": "这是一个测试码",
"slug": "pmzsq4gp4g",
"tags": [],
"title": "测试码",
"type": "link",
"userId": "0e700dc8-90dd-41b7-91dd-336ea51de3d2",
"version": "1.0"
},
{
"code": "0gmn12",
"data": {
"link": "https://kevisual.cn/root/v1/ha-api?path=ha&key=open-balcony-light",
"permission": {
"share": "public"
},
"useOwnerToken": true
},
"description": "test 阳台灯",
"slug": "3z1nbdogew",
"tags": [],
"title": "阳台灯",
"type": "link",
"userId": "0e700dc8-90dd-41b7-91dd-336ea51de3d2",
"version": "1.0"
},
{
"code": "abc111",
"data": {
"link": "https://kevisual.cn/root/nfc/",
"permission": {
"share": "public"
}
},
"description": "nfc link",
"slug": "0000000001",
"tags": [],
"title": "nfc link",
"type": "link",
"userId": "",
"version": "1.0"
},
{
"code": "ej73jm",
"data": {
"link": "https://kevisual.cn/root/nfc/",
"permission": {
"share": "public"
}
},
"description": "nfc link",
"slug": "001",
"tags": [],
"title": "nfc link",
"type": "link",
"userId": "",
"version": "1.0"
},
{
"code": "09dd42",
"data": {
"details": [
{
"description": "算法基于七卡瓦,自建",
"title": "beadskevisual.cn",
"url": "https://kevisual.cn/root/beads/"
},
{
"description": "算法很不错,图片转图纸很好",
"title": "拼豆七卡瓦(zippland.com)",
"url": "https://perlerbeads.zippland.com/"
},
{
"description": "功能偏向PS画板类像素类",
"title": "拼豆像素格子zwpyyds.com",
"url": "https://www.zwpyyds.com/"
},
{
"description": "编辑不错,拼豆社区",
"title": "我嘞个豆(ohmybead.cn)",
"url": "https://ohmybead.cn/"
}
],
"link": "https://kevisual.cn/root/nfc/way/",
"permission": {
"share": "public"
},
"title": "拼豆图纸自取方案",
"type": "html-render"
},
"description": "Pindou",
"slug": "pindou",
"tags": [],
"title": "Pindou",
"type": "link",
"userId": "",
"version": "1.0"
}
]