fix(netflow): изолировать коллектор IPFIX и срезать раздувание базы
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 2m23s
Docker images / frontend-image (push) Successful in 3m16s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 57s
Docker images / publish-release (push) Successful in 12s
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 2m23s
Docker images / frontend-image (push) Successful in 3m16s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 57s
Docker images / publish-release (push) Successful in 12s
Co-authored-by: Cursor <[email protected]>
This commit is contained in:
+62
-6
@@ -7,11 +7,23 @@ import { drizzle } from "drizzle-orm/better-sqlite3"
|
||||
import { env } from "../config.js"
|
||||
import * as schema from "./schema.js"
|
||||
|
||||
const sqlite = new Database(env.DATABASE_PATH)
|
||||
export const SQLITE_BUSY_TIMEOUT_MS = 5000
|
||||
|
||||
export function applySqlitePragmas(handle: SqliteHandle): void {
|
||||
handle.pragma("journal_mode = WAL")
|
||||
handle.pragma("foreign_keys = ON")
|
||||
handle.pragma(`busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS}`)
|
||||
handle.pragma("synchronous = NORMAL")
|
||||
}
|
||||
|
||||
function openSqlite(): SqliteHandle {
|
||||
const handle = new Database(env.DATABASE_PATH)
|
||||
applySqlitePragmas(handle)
|
||||
return handle
|
||||
}
|
||||
|
||||
let sqlite = openSqlite()
|
||||
|
||||
// WAL mode for better concurrent read performance
|
||||
sqlite.pragma("journal_mode = WAL")
|
||||
sqlite.pragma("foreign_keys = ON")
|
||||
sqlite.exec(`
|
||||
CREATE TABLE IF NOT EXISTS servers (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -162,6 +174,39 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_flow_buckets_unique
|
||||
CREATE INDEX IF NOT EXISTS idx_flow_buckets_server_time
|
||||
ON flow_buckets(server_id, bucket_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS flow_minute_stats (
|
||||
server_id INTEGER NOT NULL,
|
||||
bucket_at TEXT NOT NULL,
|
||||
bytes INTEGER NOT NULL DEFAULT 0,
|
||||
packets INTEGER NOT NULL DEFAULT 0,
|
||||
unique_src INTEGER NOT NULL DEFAULT 0,
|
||||
unique_dst INTEGER NOT NULL DEFAULT 0,
|
||||
conversations INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (server_id, bucket_at)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS flow_minute_dims (
|
||||
server_id INTEGER NOT NULL,
|
||||
bucket_at TEXT NOT NULL,
|
||||
dim TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
bytes INTEGER NOT NULL DEFAULT 0,
|
||||
packets INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (server_id, bucket_at, dim, key)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_flow_minute_dims_time ON flow_minute_dims(bucket_at, dim);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS flow_daily_dims (
|
||||
server_id INTEGER NOT NULL,
|
||||
day TEXT NOT NULL,
|
||||
dim TEXT NOT NULL,
|
||||
key TEXT NOT NULL,
|
||||
bytes INTEGER NOT NULL DEFAULT 0,
|
||||
packets INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (server_id, day, dim, key)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_flow_daily_dims_day ON flow_daily_dims(day, dim);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS flow_ip_meta (
|
||||
prefix TEXT PRIMARY KEY,
|
||||
asn INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -902,7 +947,18 @@ if (backupEntryCount.c === 0) {
|
||||
}
|
||||
}
|
||||
|
||||
export const db = drizzle(sqlite, { schema })
|
||||
export let db = drizzle(sqlite, { schema })
|
||||
|
||||
/** Прямой доступ к better-sqlite3 для сложных read-only запросов (напр. /api/alerts). */
|
||||
export const sqliteDatabase: SqliteHandle = sqlite
|
||||
export let sqliteDatabase: SqliteHandle = sqlite
|
||||
|
||||
export function reopenSqlite(): void {
|
||||
try {
|
||||
sqlite.close()
|
||||
} catch {
|
||||
/* already closed */
|
||||
}
|
||||
sqlite = openSqlite()
|
||||
sqliteDatabase = sqlite
|
||||
db = drizzle(sqlite, { schema })
|
||||
}
|
||||
|
||||
@@ -182,6 +182,40 @@ export const trafficFlowSettings = sqliteTable("traffic_flow_settings", {
|
||||
updatedAt: text("updated_at").notNull().default(sql`(datetime('now'))`),
|
||||
})
|
||||
|
||||
export const flowMinuteStats = sqliteTable("flow_minute_stats", {
|
||||
serverId: integer("server_id").notNull(),
|
||||
bucketAt: text("bucket_at").notNull(),
|
||||
bytes: integer("bytes").notNull().default(0),
|
||||
packets: integer("packets").notNull().default(0),
|
||||
uniqueSrc: integer("unique_src").notNull().default(0),
|
||||
uniqueDst: integer("unique_dst").notNull().default(0),
|
||||
conversations: integer("conversations").notNull().default(0),
|
||||
}, (t) => [
|
||||
uniqueIndex("idx_flow_minute_stats_pk").on(t.serverId, t.bucketAt),
|
||||
])
|
||||
|
||||
export const flowMinuteDims = sqliteTable("flow_minute_dims", {
|
||||
serverId: integer("server_id").notNull(),
|
||||
bucketAt: text("bucket_at").notNull(),
|
||||
dim: text("dim").notNull(),
|
||||
key: text("key").notNull(),
|
||||
bytes: integer("bytes").notNull().default(0),
|
||||
packets: integer("packets").notNull().default(0),
|
||||
}, (t) => [
|
||||
uniqueIndex("idx_flow_minute_dims_pk").on(t.serverId, t.bucketAt, t.dim, t.key),
|
||||
])
|
||||
|
||||
export const flowDailyDims = sqliteTable("flow_daily_dims", {
|
||||
serverId: integer("server_id").notNull(),
|
||||
day: text("day").notNull(),
|
||||
dim: text("dim").notNull(),
|
||||
key: text("key").notNull(),
|
||||
bytes: integer("bytes").notNull().default(0),
|
||||
packets: integer("packets").notNull().default(0),
|
||||
}, (t) => [
|
||||
uniqueIndex("idx_flow_daily_dims_pk").on(t.serverId, t.day, t.dim, t.key),
|
||||
])
|
||||
|
||||
export const flowBuckets = sqliteTable("flow_buckets", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
serverId: integer("server_id")
|
||||
|
||||
Reference in New Issue
Block a user