chore: add prettier with config and format all files

This commit is contained in:
Sebastian Seedorf
2026-06-04 11:44:20 +02:00
parent d212ae3f30
commit cf9bb33ecb
50 changed files with 1290 additions and 714 deletions

View File

@@ -1,20 +1,39 @@
import type { ChartConfig, AlertConfig, TriggeredAlert, SignalRow, SessionBoundary, UpsRow, TimeMode } from './types';
import type {
ChartConfig,
AlertConfig,
TriggeredAlert,
SignalRow,
SessionBoundary,
UpsRow,
TimeMode,
} from './types';
let _token = '';
export function setToken(token: string) { _token = token; }
export function setToken(token: string) {
_token = token;
}
function url(path: string, params: Record<string, string | string[] | boolean | number | undefined> = {}) {
const u = new URL(path, typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000');
function url(
path: string,
params: Record<string, string | string[] | boolean | number | undefined> = {},
) {
const u = new URL(
path,
typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000',
);
u.searchParams.set('token', _token);
for (const [k, v] of Object.entries(params)) {
if (v === undefined) continue;
if (Array.isArray(v)) v.forEach(val => u.searchParams.append(k, String(val)));
if (Array.isArray(v)) v.forEach((val) => u.searchParams.append(k, String(val)));
else u.searchParams.set(k, String(v));
}
return u.toString();
}
async function get<T>(path: string, params?: Record<string, string | string[] | boolean | number | undefined>): Promise<T> {
async function get<T>(
path: string,
params?: Record<string, string | string[] | boolean | number | undefined>,
): Promise<T> {
const res = await fetch(url(path, params));
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
return res.json();
@@ -45,7 +64,11 @@ export function fetchSignals(params: {
return get('/api/signals', params as Record<string, string | string[]>);
}
export function fetchUps(params: { combinator?: string; from?: string; to?: string }): Promise<UpsRow[]> {
export function fetchUps(params: {
combinator?: string;
from?: string;
to?: string;
}): Promise<UpsRow[]> {
return get('/api/ups', params);
}
@@ -53,13 +76,31 @@ export function fetchSessions(from: string, to: string): Promise<SessionBoundary
return get('/api/sessions', { from, to });
}
export function fetchCharts(): Promise<ChartConfig[]> { return get('/api/charts'); }
export function createChart(body: Omit<ChartConfig, 'id'>): Promise<ChartConfig> { return mutate('POST', '/api/charts', body); }
export function updateChart(id: string, body: Partial<ChartConfig>): Promise<ChartConfig> { return mutate('PUT', `/api/charts/${id}`, body); }
export function deleteChart(id: string): Promise<{ ok: boolean }> { return mutate('DELETE', `/api/charts/${id}`); }
export function fetchCharts(): Promise<ChartConfig[]> {
return get('/api/charts');
}
export function createChart(body: Omit<ChartConfig, 'id'>): Promise<ChartConfig> {
return mutate('POST', '/api/charts', body);
}
export function updateChart(id: string, body: Partial<ChartConfig>): Promise<ChartConfig> {
return mutate('PUT', `/api/charts/${id}`, body);
}
export function deleteChart(id: string): Promise<{ ok: boolean }> {
return mutate('DELETE', `/api/charts/${id}`);
}
export function fetchAlerts(): Promise<AlertConfig[]> { return get('/api/alerts'); }
export function createAlert(body: Omit<AlertConfig, 'id' | 'active'>): Promise<AlertConfig> { return mutate('POST', '/api/alerts', body); }
export function updateAlert(id: string, body: Partial<AlertConfig>): Promise<AlertConfig> { return mutate('PUT', `/api/alerts/${id}`, body); }
export function deleteAlert(id: string): Promise<{ ok: boolean }> { return mutate('DELETE', `/api/alerts/${id}`); }
export function checkAlerts(): Promise<TriggeredAlert[]> { return get('/api/alerts/check'); }
export function fetchAlerts(): Promise<AlertConfig[]> {
return get('/api/alerts');
}
export function createAlert(body: Omit<AlertConfig, 'id' | 'active'>): Promise<AlertConfig> {
return mutate('POST', '/api/alerts', body);
}
export function updateAlert(id: string, body: Partial<AlertConfig>): Promise<AlertConfig> {
return mutate('PUT', `/api/alerts/${id}`, body);
}
export function deleteAlert(id: string): Promise<{ ok: boolean }> {
return mutate('DELETE', `/api/alerts/${id}`);
}
export function checkAlerts(): Promise<TriggeredAlert[]> {
return get('/api/alerts/check');
}

View File

@@ -23,4 +23,4 @@ export function withAuth(handler: Handler): Handler {
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
}
};
}
}

View File

@@ -19,4 +19,4 @@ export function isAuthorized(req: NextRequest): boolean {
export function unauthorized(): NextResponse {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
}

View File

@@ -1,6 +1,8 @@
export type ColorMap = Map<string, string>;
declare global { var __colorMapCache: ColorMap | undefined; }
declare global {
var __colorMapCache: ColorMap | undefined;
}
export function parseColorCsv(text: string): ColorMap {
const map: ColorMap = new Map();

View File

@@ -1,12 +1,6 @@
'use client';
import React, {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
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';
@@ -14,15 +8,15 @@ import { buildReverseMap } from './localization';
import type { LocaleMap, ReverseMap } from './localization';
interface AppContextValue {
timeRange: TimeRange;
setTimeRange: (r: TimeRange) => void;
timeMode: TimeMode;
setTimeMode: (m: TimeMode) => void;
timeRange: TimeRange;
setTimeRange: (r: TimeRange) => void;
timeMode: TimeMode;
setTimeMode: (m: TimeMode) => void;
triggeredAlerts: TriggeredAlert[];
refreshAlerts: () => Promise<void>;
getFromTo: () => { from: string; to: string };
localeMap: LocaleMap;
reverseMap: ReverseMap;
refreshAlerts: () => Promise<void>;
getFromTo: () => { from: string; to: string };
localeMap: LocaleMap;
reverseMap: ReverseMap;
}
const AppContext = createContext<AppContextValue | null>(null);
@@ -32,18 +26,18 @@ export function AppProvider({
localeMap,
children,
}: {
token: string;
token: string;
localeMap: LocaleMap;
children: React.ReactNode;
children: React.ReactNode;
}) {
const [timeRange, setTimeRange] = useState<TimeRange>('6h');
const [timeMode, setTimeMode] = useState<TimeMode>('real');
const [timeRange, setTimeRange] = useState<TimeRange>('6h');
const [timeMode, setTimeMode] = useState<TimeMode>('real');
const [triggeredAlerts, setTriggeredAlerts] = useState<TriggeredAlert[]>([]);
const reverseMap = buildReverseMap(localeMap);
const getFromTo = useCallback(() => {
const to = new Date();
const to = new Date();
const from = new Date(to.getTime() - TIME_RANGE_MS[timeRange]);
return { from: from.toISOString(), to: to.toISOString() };
}, [timeRange]);
@@ -54,20 +48,30 @@ export function AppProvider({
useEffect(() => {
let cancelled = false;
const poll = () => checkAlerts().then(a => { if (!cancelled) setTriggeredAlerts(a); });
const poll = () =>
checkAlerts().then((a) => {
if (!cancelled) setTriggeredAlerts(a);
});
poll();
const id = setInterval(poll, 30_000);
return () => { cancelled = true; clearInterval(id); };
return () => {
cancelled = true;
clearInterval(id);
};
}, []);
return (
<AppContext.Provider
value={{
timeRange, setTimeRange,
timeMode, setTimeMode,
triggeredAlerts, refreshAlerts,
timeRange,
setTimeRange,
timeMode,
setTimeMode,
triggeredAlerts,
refreshAlerts,
getFromTo,
localeMap, reverseMap,
localeMap,
reverseMap,
}}
>
{children}
@@ -80,4 +84,4 @@ export function useApp() {
const ctx = useContext(AppContext);
if (!ctx) throw new Error('useApp must be used within AppProvider');
return ctx;
}
}

View File

@@ -11,4 +11,4 @@ if (process.env.NODE_ENV !== 'production') {
globalThis.__pgPool = pool;
}
export default pool;
export default pool;

View File

@@ -3,7 +3,9 @@ import path from 'path';
import { parseCsv } from './localization';
import type { LocaleMap } from './localization';
declare global { var __serverLocaleCache: LocaleMap | undefined; }
declare global {
var __serverLocaleCache: LocaleMap | undefined;
}
/**
* Loads and merges EN + DE locale CSVs from the public directory.
@@ -23,7 +25,10 @@ export function getServerLocaleMap(): LocaleMap {
}
}
const merged: LocaleMap = new Map([...load('factorio_english_items.csv'), ...load('factorio_german_items.csv')]);
const merged: LocaleMap = new Map([
...load('factorio_english_items.csv'),
...load('factorio_german_items.csv'),
]);
globalThis.__serverLocaleCache = merged;
return merged;
}
}

View File

@@ -20,8 +20,10 @@ function splitCsvLine(line: string): string[] {
for (let i = 0; i < line.length; i++) {
const ch = line[i];
if (ch === '"') {
if (inQuote && line[i + 1] === '"') { cur += '"'; i++; }
else inQuote = !inQuote;
if (inQuote && line[i + 1] === '"') {
cur += '"';
i++;
} else inQuote = !inQuote;
} else if (ch === ',' && !inQuote) {
cols.push(cur);
cur = '';
@@ -33,7 +35,9 @@ function splitCsvLine(line: string): string[] {
return cols;
}
declare global { var __localeCache: LocaleMap | undefined; }
declare global {
var __localeCache: LocaleMap | undefined;
}
async function loadCsv(path: string): Promise<LocaleMap> {
try {
@@ -100,4 +104,4 @@ export function matchKeys(pattern: string, map: LocaleMap): string[] {
if (re.test(key) || re.test(name)) result.add(key);
}
return [...result];
}
}

View File

@@ -5,10 +5,7 @@ import type { SessionBoundary } from '@/lib/types';
* Returns session-start timestamps where any gap > 30 min exists
* in the global tick_timing timeline.
*/
export async function getSessionBoundaries(
from: Date,
to: Date,
): Promise<SessionBoundary[]> {
export async function getSessionBoundaries(from: Date, to: Date): Promise<SessionBoundary[]> {
const result = await pool.query<{ real_time: Date; game_tick: string }>(
`SELECT real_time, game_tick
FROM tick_timing
@@ -20,10 +17,12 @@ export async function getSessionBoundaries(
const rows = result.rows;
if (rows.length === 0) return [];
const boundaries: SessionBoundary[] = [{
real_time: rows[0].real_time.toISOString(),
game_tick: parseInt(rows[0].game_tick, 10),
}];
const boundaries: SessionBoundary[] = [
{
real_time: rows[0].real_time.toISOString(),
game_tick: parseInt(rows[0].game_tick, 10),
},
];
for (let i = 1; i < rows.length; i++) {
if (rows[i].real_time.getTime() - rows[i - 1].real_time.getTime() > 30 * 60 * 1000) {
@@ -35,4 +34,4 @@ export async function getSessionBoundaries(
}
return boundaries;
}
}

View File

@@ -31,7 +31,7 @@ export interface AlertConfig {
}
export interface TriggeredAlert extends AlertConfig {
current_value: number;
current_value: number;
combinator_match: string;
/** Actual matched item_key — differs from item_key when item_key_is_regex=true */
matched_item_key: string;
@@ -63,9 +63,9 @@ export type TimeRange = '30m' | '1h' | '6h' | '24h' | '7d' | '30d';
export const TIME_RANGE_MS: Record<TimeRange, number> = {
'30m': 30 * 60 * 1000,
'1h': 60 * 60 * 1000,
'6h': 6 * 60 * 60 * 1000,
'1h': 60 * 60 * 1000,
'6h': 6 * 60 * 60 * 1000,
'24h': 24 * 60 * 60 * 1000,
'7d': 7 * 24 * 60 * 60 * 1000,
'7d': 7 * 24 * 60 * 60 * 1000,
'30d': 30 * 24 * 60 * 60 * 1000,
};
};