init: init proj
This commit is contained in:
116
server/src/routes/rules.ts
Normal file
116
server/src/routes/rules.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Hono } from 'hono';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { asc, eq } from 'drizzle-orm';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { z } from 'zod';
|
||||
import { ruleInputSchema, type RuleDto } from '@proxy-station/shared';
|
||||
import { db } from '../db/index.js';
|
||||
import { rules, rulesets } from '../db/schema.js';
|
||||
import { seedRulesFromTemplate } from '../db/seed.js';
|
||||
|
||||
function loadRules(): RuleDto[] {
|
||||
const rows = db.select().from(rules).orderBy(asc(rules.seq)).all();
|
||||
const sets = new Map(db.select().from(rulesets).all().map((r) => [r.id, r]));
|
||||
return rows.map((r) => {
|
||||
const rs = r.value ? sets.get(r.value) : undefined;
|
||||
return {
|
||||
...r,
|
||||
params: JSON.parse(r.params),
|
||||
rulesetName: rs?.name ?? null,
|
||||
rulesetUrl: rs?.surgeUrl ?? null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function nextSeq(): number {
|
||||
const rows = db.select({ seq: rules.seq }).from(rules).all();
|
||||
return rows.length ? Math.max(...rows.map((r) => r.seq)) + 1 : 0;
|
||||
}
|
||||
|
||||
export const rulesRoute = new Hono()
|
||||
.get('/', (c) => c.json(loadRules()))
|
||||
.post('/', zValidator('json', ruleInputSchema), (c) => {
|
||||
const input = c.req.valid('json');
|
||||
// 新自定义规则插到 FINAL 之前
|
||||
const finalRow = db.select().from(rules).where(eq(rules.type, 'FINAL')).get();
|
||||
let seq: number;
|
||||
if (finalRow) {
|
||||
seq = finalRow.seq;
|
||||
db.update(rules).set({ seq: finalRow.seq + 1 }).where(eq(rules.id, finalRow.id)).run();
|
||||
} else {
|
||||
seq = nextSeq();
|
||||
}
|
||||
const id = nanoid(10);
|
||||
db.insert(rules)
|
||||
.values({
|
||||
id,
|
||||
seq,
|
||||
type: input.type,
|
||||
value: input.value ?? null,
|
||||
policy: input.policy,
|
||||
params: JSON.stringify(input.params),
|
||||
enabled: input.enabled,
|
||||
source: 'custom',
|
||||
})
|
||||
.run();
|
||||
return c.json({ id }, 201);
|
||||
})
|
||||
.put('/reorder', zValidator('json', z.object({ ids: z.array(z.string()) })), (c) => {
|
||||
const { ids } = c.req.valid('json');
|
||||
ids.forEach((id, i) => db.update(rules).set({ seq: i }).where(eq(rules.id, id)).run());
|
||||
return c.json({ ok: true });
|
||||
})
|
||||
.post('/reset', (c) => {
|
||||
seedRulesFromTemplate(db, { keepCustom: true });
|
||||
return c.json({ ok: true, rules: loadRules().length });
|
||||
})
|
||||
.put('/:id', zValidator('json', ruleInputSchema), (c) => {
|
||||
const id = c.req.param('id');
|
||||
const existing = db.select().from(rules).where(eq(rules.id, id)).get();
|
||||
if (!existing) return c.json({ error: '规则不存在' }, 404);
|
||||
const input = c.req.valid('json');
|
||||
db.update(rules)
|
||||
.set({
|
||||
type: input.type,
|
||||
value: input.value ?? null,
|
||||
policy: input.policy,
|
||||
params: JSON.stringify(input.params),
|
||||
enabled: input.enabled,
|
||||
})
|
||||
.where(eq(rules.id, id))
|
||||
.run();
|
||||
return c.json({ ok: true });
|
||||
})
|
||||
.patch('/:id/enabled', zValidator('json', z.object({ enabled: z.boolean() })), (c) => {
|
||||
const res = db
|
||||
.update(rules)
|
||||
.set({ enabled: c.req.valid('json').enabled })
|
||||
.where(eq(rules.id, c.req.param('id')))
|
||||
.run();
|
||||
if (res.changes === 0) return c.json({ error: '规则不存在' }, 404);
|
||||
return c.json({ ok: true });
|
||||
})
|
||||
.delete('/:id', (c) => {
|
||||
const res = db.delete(rules).where(eq(rules.id, c.req.param('id'))).run();
|
||||
if (res.changes === 0) return c.json({ error: '规则不存在' }, 404);
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
export const rulesetsRoute = new Hono()
|
||||
.get('/', (c) => {
|
||||
const rows = db.select().from(rulesets).all();
|
||||
return c.json(rows);
|
||||
})
|
||||
.get('/:id/content', async (c) => {
|
||||
const rs = db.select().from(rulesets).where(eq(rulesets.id, c.req.param('id'))).get();
|
||||
if (!rs) return c.json({ error: '规则集不存在' }, 404);
|
||||
const refresh = c.req.query('refresh') === '1';
|
||||
try {
|
||||
const { fetchRulesetContent } = await import('../services/rulesets/fetch.js');
|
||||
const { content, fetchedAt, stale } = await fetchRulesetContent(rs.surgeUrl, { refresh });
|
||||
const lineCount = content.split(/\r?\n/).filter((l) => l.trim() && !l.trim().startsWith('#')).length;
|
||||
return c.json({ content, fetchedAt, stale, lineCount });
|
||||
} catch (e: any) {
|
||||
return c.json({ error: `拉取失败:${e.message}` }, 502);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user