refactor: extract signals filter builder, add ESLint 10 config, fix low-hanging issues
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { isAuthorized, unauthorized } from './auth';
|
||||
import { getServerLocaleMap } from './localeServer';
|
||||
import { matchKeys } from './localization';
|
||||
|
||||
type RouteContext = { params: Promise<Record<string, string>> };
|
||||
type Handler = (req: NextRequest, ctx: RouteContext) => Promise<NextResponse>;
|
||||
@@ -24,3 +27,53 @@ export function withAuth(handler: Handler): Handler {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SQL WHERE clause fragment for item whitelist/blacklist filtering.
|
||||
*
|
||||
* - `isWhitelist=true`: `item_key ~* $N OR item_key = ANY($N+1)` (regex), `item_key = ANY($N)` (exact)
|
||||
* - `isWhitelist=false`: `item_key !~* $N AND item_key != ALL($N+1)` (regex), `item_key != ALL($N)` (exact)
|
||||
*
|
||||
* Mutates `values` and `param.current` to track parameter bindings.
|
||||
*/
|
||||
export function buildItemFilter(
|
||||
items: string[],
|
||||
useRegex: boolean,
|
||||
isWhitelist: boolean,
|
||||
values: unknown[],
|
||||
param: { current: number },
|
||||
): string | null {
|
||||
if (items.length === 0) return null;
|
||||
|
||||
if (useRegex) {
|
||||
const localeMap = getServerLocaleMap();
|
||||
const localeKeys = [...new Set(items.flatMap((p) => matchKeys(p, localeMap)))];
|
||||
const sqlPattern = items.map((p) => `(${p})`).join('|');
|
||||
|
||||
if (isWhitelist) {
|
||||
const orConds: string[] = [`item_key ~* $${param.current++}`];
|
||||
values.push(sqlPattern);
|
||||
if (localeKeys.length > 0) {
|
||||
orConds.push(`item_key = ANY($${param.current++})`);
|
||||
values.push(localeKeys);
|
||||
}
|
||||
return `(${orConds.join(' OR ')})`;
|
||||
} else {
|
||||
const andConds: string[] = [`item_key !~* $${param.current++}`];
|
||||
values.push(sqlPattern);
|
||||
if (localeKeys.length > 0) {
|
||||
andConds.push(`item_key != ALL($${param.current++})`);
|
||||
values.push(localeKeys);
|
||||
}
|
||||
return `(${andConds.join(' AND ')})`;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWhitelist) {
|
||||
values.push(items);
|
||||
return `item_key = ANY($${param.current++})`;
|
||||
} else {
|
||||
values.push(items);
|
||||
return `item_key != ALL($${param.current++})`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
/**
|
||||
* Returns true if the request carries a valid API token.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import type { TimeRange, TimeMode, TriggeredAlert } from './types';
|
||||
import { TIME_RANGE_MS } from './types';
|
||||
|
||||
import { checkAlerts } from './api';
|
||||
import { buildReverseMap } from './localization';
|
||||
import { TIME_RANGE_MS } from './types';
|
||||
|
||||
import type { LocaleMap, ReverseMap } from './localization';
|
||||
import type { TimeRange, TimeMode, TriggeredAlert } from './types';
|
||||
|
||||
interface AppContextValue {
|
||||
timeRange: TimeRange;
|
||||
@@ -22,14 +24,12 @@ interface AppContextValue {
|
||||
const AppContext = createContext<AppContextValue | null>(null);
|
||||
|
||||
export function AppProvider({
|
||||
token: _token,
|
||||
localeMap,
|
||||
children,
|
||||
}: {
|
||||
token: string;
|
||||
localeMap: LocaleMap;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
} & { token?: string }) {
|
||||
const [timeRange, setTimeRange] = useState<TimeRange>('6h');
|
||||
const [timeMode, setTimeMode] = useState<TimeMode>('real');
|
||||
const [triggeredAlerts, setTriggeredAlerts] = useState<TriggeredAlert[]>([]);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Pool } from 'pg';
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var __pgPool: Pool | undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { parseCsv } from './localization';
|
||||
|
||||
import type { LocaleMap } from './localization';
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import pool from '@/lib/db';
|
||||
import type { SessionBoundary } from '@/lib/types';
|
||||
|
||||
import pool from '@/lib/db';
|
||||
|
||||
/**
|
||||
* Returns session-start timestamps where any gap > 30 min exists
|
||||
* in the global tick_timing timeline.
|
||||
|
||||
Reference in New Issue
Block a user