chore: add prettier with config and format all files
This commit is contained in:
@@ -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');
|
||||
}
|
||||
|
||||
@@ -23,4 +23,4 @@ export function withAuth(handler: Handler): Handler {
|
||||
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@ export function isAuthorized(req: NextRequest): boolean {
|
||||
|
||||
export function unauthorized(): NextResponse {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
globalThis.__pgPool = pool;
|
||||
}
|
||||
|
||||
export default pool;
|
||||
export default pool;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user