refactor: extract signals filter builder, add ESLint 10 config, fix low-hanging issues

This commit is contained in:
Sebastian Seedorf
2026-06-04 14:09:12 +02:00
parent cf9bb33ecb
commit 4b05f2968e
34 changed files with 2145 additions and 188 deletions

View File

@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
import pool from '@/lib/db';
import { type NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/apiHelpers';
import pool from '@/lib/db';
export const PUT = withAuth(async (req: NextRequest, { params }) => {
const { id } = await params;

View File

@@ -1,9 +1,11 @@
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
import type { AlertConfig, TriggeredAlert } from '@/lib/types';
import { withAuth } from '@/lib/apiHelpers';
import pool from '@/lib/db';
import { getServerLocaleMap } from '@/lib/localeServer';
import { resolveName } from '@/lib/localization';
import type { AlertConfig, TriggeredAlert } from '@/lib/types';
export const GET = withAuth(async () => {
const alertsResult = await pool.query<AlertConfig>(

View File

@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
import pool from '@/lib/db';
import { type NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/apiHelpers';
import pool from '@/lib/db';
export const GET = withAuth(async () => {
const result = await pool.query('SELECT * FROM alerts ORDER BY created_at DESC');

View File

@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
import pool from '@/lib/db';
import { type NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/apiHelpers';
import pool from '@/lib/db';
export const PUT = withAuth(async (req: NextRequest, { params }) => {
const { id } = await params;

View File

@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
import pool from '@/lib/db';
import { type NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/apiHelpers';
import pool from '@/lib/db';
export const GET = withAuth(async () => {
const result = await pool.query('SELECT * FROM charts ORDER BY pos_y ASC, pos_x ASC');

View File

@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
import pool from '@/lib/db';
import { type NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/apiHelpers';
import pool from '@/lib/db';
interface CircuitNetwork {
green: Record<string, number>;

View File

@@ -1,11 +1,14 @@
import { NextRequest, NextResponse } from 'next/server';
import { getSessionBoundaries } from '@/lib/sessions';
import { type NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/apiHelpers';
import { getSessionBoundaries } from '@/lib/sessions';
export const GET = withAuth(async (req: NextRequest) => {
const p = req.nextUrl.searchParams;
const from = p.get('from') ? new Date(p.get('from')!) : new Date(Date.now() - 86_400_000);
const to = p.get('to') ? new Date(p.get('to')!) : new Date();
const fromVal = p.get('from');
const toVal = p.get('to');
const from = fromVal ? new Date(fromVal) : new Date(Date.now() - 86_400_000);
const to = toVal ? new Date(toVal) : new Date();
const boundaries = await getSessionBoundaries(from, to);
return NextResponse.json(boundaries);
});

View File

@@ -1,8 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
import { type NextRequest, NextResponse } from 'next/server';
import { withAuth, buildItemFilter } from '@/lib/apiHelpers';
import pool from '@/lib/db';
import { withAuth } from '@/lib/apiHelpers';
import { getServerLocaleMap } from '@/lib/localeServer';
import { matchKeys } from '@/lib/localization';
export const GET = withAuth(async (req: NextRequest) => {
const p = req.nextUrl.searchParams;
@@ -14,59 +13,30 @@ export const GET = withAuth(async (req: NextRequest) => {
const to = p.get('to');
const useRegex = p.get('regex') === 'true';
const orderBy = p.get('order_by') ?? 'value_asc';
const limit = p.get('limit') ? parseInt(p.get('limit')!, 10) : null;
const limitStr = p.get('limit');
const limit = limitStr ? parseInt(limitStr, 10) : null;
const conditions: string[] = [];
const values: unknown[] = [];
let i = 1;
const param = { current: 1 };
if (combinators.length > 0) {
conditions.push(`combinator = ANY($${i++})`);
conditions.push(`combinator = ANY($${param.current++})`);
values.push(combinators);
}
if (itemsWhitelist.length > 0) {
if (useRegex) {
const localeMap = getServerLocaleMap();
const localeKeys = [...new Set(itemsWhitelist.flatMap((p) => matchKeys(p, localeMap)))];
const sqlPattern = itemsWhitelist.map((p) => `(${p})`).join('|');
const orConds = [`item_key ~* $${i++}`];
values.push(sqlPattern);
if (localeKeys.length > 0) {
orConds.push(`item_key = ANY($${i++})`);
values.push(localeKeys);
}
conditions.push(`(${orConds.join(' OR ')})`);
} else {
conditions.push(`item_key = ANY($${i++})`);
values.push(itemsWhitelist);
}
}
const wlClause = buildItemFilter(itemsWhitelist, useRegex, true, values, param);
if (wlClause) conditions.push(wlClause);
if (itemsBlacklist.length > 0) {
if (useRegex) {
const localeMap = getServerLocaleMap();
const localeKeys = [...new Set(itemsBlacklist.flatMap((p) => matchKeys(p, localeMap)))];
const sqlPattern = itemsBlacklist.map((p) => `(${p})`).join('|');
const andConds = [`item_key !~* $${i++}`];
values.push(sqlPattern);
if (localeKeys.length > 0) {
andConds.push(`item_key != ALL($${i++})`);
values.push(localeKeys);
}
conditions.push(`(${andConds.join(' AND ')})`);
} else {
conditions.push(`item_key != ALL($${i++})`);
values.push(itemsBlacklist);
}
}
const blClause = buildItemFilter(itemsBlacklist, useRegex, false, values, param);
if (blClause) conditions.push(blClause);
if (from) {
conditions.push(`real_time >= $${i++}`);
conditions.push(`real_time >= $${param.current++}`);
values.push(new Date(from));
}
if (to) {
conditions.push(`real_time <= $${i++}`);
conditions.push(`real_time <= $${param.current++}`);
values.push(new Date(to));
}
@@ -83,46 +53,18 @@ export const GET = withAuth(async (req: NextRequest) => {
if ((orderBy === 'delta_asc' || orderBy === 'delta_desc') && limit !== null) {
const baseConditions: string[] = [];
const baseValues: unknown[] = [];
let j = 1;
const baseParam = { current: 1 };
if (combinators.length > 0) {
baseConditions.push(`combinator = ANY($${j++})`);
baseConditions.push(`combinator = ANY($${baseParam.current++})`);
baseValues.push(combinators);
}
if (itemsWhitelist.length > 0) {
if (useRegex) {
const localeMap = getServerLocaleMap();
const localeKeys = [...new Set(itemsWhitelist.flatMap((p) => matchKeys(p, localeMap)))];
const sqlPattern = itemsWhitelist.map((p) => `(${p})`).join('|');
const orConds = [`item_key ~* $${j++}`];
baseValues.push(sqlPattern);
if (localeKeys.length > 0) {
orConds.push(`item_key = ANY($${j++})`);
baseValues.push(localeKeys);
}
baseConditions.push(`(${orConds.join(' OR ')})`);
} else {
baseConditions.push(`item_key = ANY($${j++})`);
baseValues.push(itemsWhitelist);
}
}
if (itemsBlacklist.length > 0) {
if (useRegex) {
const localeMap = getServerLocaleMap();
const localeKeys = [...new Set(itemsBlacklist.flatMap((p) => matchKeys(p, localeMap)))];
const sqlPattern = itemsBlacklist.map((p) => `(${p})`).join('|');
const andConds = [`item_key !~* $${j++}`];
baseValues.push(sqlPattern);
if (localeKeys.length > 0) {
andConds.push(`item_key != ALL($${j++})`);
baseValues.push(localeKeys);
}
baseConditions.push(`(${andConds.join(' AND ')})`);
} else {
baseConditions.push(`item_key != ALL($${j++})`);
baseValues.push(itemsBlacklist);
}
}
const wlBase = buildItemFilter(itemsWhitelist, useRegex, true, baseValues, baseParam);
if (wlBase) baseConditions.push(wlBase);
const blBase = buildItemFilter(itemsBlacklist, useRegex, false, baseValues, baseParam);
if (blBase) baseConditions.push(blBase);
const baseWhere = baseConditions.length > 0 ? `WHERE ${baseConditions.join(' AND ')}` : '';
const baseWhereAnd = baseConditions.length > 0 ? `AND ${baseConditions.join(' AND ')}` : '';
@@ -155,25 +97,27 @@ export const GET = withAuth(async (req: NextRequest) => {
SELECT combinator, item_key, delta
FROM deltas
ORDER BY delta ${orderBy === 'delta_asc' ? 'ASC' : 'DESC'}
LIMIT $${j}
LIMIT $${baseParam.current}
`;
const deltaResult = await pool.query<{ combinator: string; item_key: string; delta: number }>(
deltaQuery,
[...baseValues, limit],
);
const deltaResult = await pool.query<{
combinator: string;
item_key: string;
delta: number;
}>(deltaQuery, [...baseValues, limit]);
const top = deltaResult.rows;
if (top.length === 0) return NextResponse.json([]);
const seriesConditions = top.map(
(_, idx) => `(combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1})`,
(_, idx) =>
`(combinator = $${param.current + idx * 2} AND item_key = $${param.current + idx * 2 + 1})`,
);
const fullWhere = `${where ? where + ' AND' : 'WHERE'} (${seriesConditions.join(' OR ')})`;
const orderCase = top
.map(
(_, idx) =>
`WHEN combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1} THEN ${idx}`,
`WHEN combinator = $${param.current + idx * 2} AND item_key = $${param.current + idx * 2 + 1} THEN ${idx}`,
)
.join(' ');
const result = await pool.query(
@@ -187,7 +131,11 @@ export const GET = withAuth(async (req: NextRequest) => {
(orderBy === 'value_asc' || orderBy === 'value_desc' || orderBy === 'abs_desc') &&
limit !== null
) {
const latestVals = await pool.query<{ combinator: string; item_key: string; val: number }>(
const latestVals = await pool.query<{
combinator: string;
item_key: string;
val: number;
}>(
`SELECT DISTINCT ON (combinator, item_key)
combinator, item_key, ${valueCol} AS val
FROM signals ${where}
@@ -205,13 +153,14 @@ export const GET = withAuth(async (req: NextRequest) => {
if (top.length === 0) return NextResponse.json([]);
const seriesConditions = top.map(
(_, idx) => `(combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1})`,
(_, idx) =>
`(combinator = $${param.current + idx * 2} AND item_key = $${param.current + idx * 2 + 1})`,
);
const fullWhere = `${where ? where + ' AND' : 'WHERE'} (${seriesConditions.join(' OR ')})`;
const orderCase = top
.map(
(_, idx) =>
`WHEN combinator = $${i + idx * 2} AND item_key = $${i + idx * 2 + 1} THEN ${idx}`,
`WHEN combinator = $${param.current + idx * 2} AND item_key = $${param.current + idx * 2 + 1} THEN ${idx}`,
)
.join(' ');
const result = await pool.query(

View File

@@ -1,12 +1,15 @@
import { NextRequest, NextResponse } from 'next/server';
import pool from '@/lib/db';
import { type NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/apiHelpers';
import pool from '@/lib/db';
export const GET = withAuth(async (req: NextRequest) => {
const p = req.nextUrl.searchParams;
const combinator = p.get('combinator');
const from = p.get('from') ? new Date(p.get('from')!) : new Date(Date.now() - 86_400_000);
const to = p.get('to') ? new Date(p.get('to')!) : new Date();
const fromVal = p.get('from');
const toVal = p.get('to');
const from = fromVal ? new Date(fromVal) : new Date(Date.now() - 86_400_000);
const to = toVal ? new Date(toVal) : new Date();
const conditions = ['real_time BETWEEN $1 AND $2'];
const values: unknown[] = [from, to];