feat(filters, recursive-routes): enhance configuration history and live data handling

- Introduced configuration history management in Filters and Recursive Routes pages, allowing users to view and restore previous configurations.
- Updated state management to handle live data loading and error states more effectively, improving user experience during data fetching.
- Added new components for displaying configuration history and integrated them into existing pages.
- Enhanced API interactions to support fetching and applying configuration revisions, ensuring data consistency across the application.
- Updated tests to cover new functionalities and ensure reliability.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-09-11 12:15:36 +07:00
co-authored by Cursor
parent 9c0ee7940e
commit b4a3c3a925
18 changed files with 1394 additions and 661 deletions
+16
View File
@@ -163,6 +163,22 @@ if (!(await withPgOrSkip())) {
await dbQuery(`DELETE FROM servers WHERE name = 'pg-wipe-idempotent'`)
}
{
const mig = await dbQuery<{ id: string }>(
`SELECT id FROM schema_migrations WHERE id = '0006_config_revisions'`,
)
assert.equal(mig.rows.length, 1, "0006 применена")
const { rows } = await dbQuery<{ column_name: string; udt_name: string }>(`
SELECT column_name, udt_name FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'config_revisions'
`)
const by = Object.fromEntries(rows.map((r) => [r.column_name, r.udt_name]))
assert.equal(by.payload, "jsonb")
assert.equal(by.fingerprint, "text")
assert.equal(by.section, "text")
}
{
const marker = await dbQuery<{ sqlite_imported_at: string | null }>(
`SELECT sqlite_imported_at FROM data_migration WHERE id = 1`,
+14
View File
@@ -93,6 +93,19 @@ export const filterRules = pgTable("filter_rules", {
index("idx_filter_rules_server_sort").on(t.serverId, t.sortOrder),
])
export const configRevisions = pgTable("config_revisions", {
id: text("id").primaryKey(),
serverId: intPkRef().references(() => servers.id, { onDelete: "cascade" }),
section: text("section", { enum: ["filters", "recursive-routes"] }).notNull(),
source: text("source", { enum: ["apply", "rollback", "observed", "copy"] }).notNull(),
fingerprint: text("fingerprint").notNull(),
payload: jsonb("payload").$type<unknown[]>().notNull().default(sql`'[]'::jsonb`),
note: text("note"),
createdAt: ts("created_at").notNull().defaultNow(),
}, (t) => [
index("idx_config_revisions_server_section_created").on(t.serverId, t.section, t.createdAt),
])
export const recursiveRoutes = pgTable("recursive_routes", {
id: idIdentity().primaryKey(),
serverId: intPkRef().references(() => servers.id, { onDelete: "cascade" }),
@@ -733,6 +746,7 @@ export type ServerInsert = typeof servers.$inferInsert
export type Snapshot = typeof serverSnapshots.$inferSelect
export type SnapshotInsert = typeof serverSnapshots.$inferInsert
export type FilterRuleRow = typeof filterRules.$inferSelect
export type ConfigRevisionRow = typeof configRevisions.$inferSelect
export type RecursiveRouteRow = typeof recursiveRoutes.$inferSelect
export type TrafficSettingsRow = typeof trafficSettings.$inferSelect
export type TrafficFlowSettingsRow = typeof trafficFlowSettings.$inferSelect