web_plsql 1.0.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -13
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/frontend/assets/main-0oxRTIkL.js +147 -0
- package/dist/frontend/assets/main-0oxRTIkL.js.br +0 -0
- package/dist/frontend/assets/main-0oxRTIkL.js.gz +0 -0
- package/dist/frontend/assets/main-TYgLak6O.css +1 -0
- package/dist/frontend/assets/main-TYgLak6O.css.br +0 -0
- package/dist/frontend/assets/main-TYgLak6O.css.gz +0 -0
- package/dist/frontend/index.html +516 -0
- package/dist/frontend/index.html.br +0 -0
- package/dist/frontend/index.html.gz +0 -0
- package/dist/index.d.mts +646 -0
- package/dist/index.mjs +3548 -0
- package/dist/index.mjs.map +1 -0
- package/dist/oracledb-mock-Dn8aHtd7.mjs +68 -0
- package/dist/oracledb-mock-Dn8aHtd7.mjs.map +1 -0
- package/examples/config-apex.js +1 -1
- package/examples/config-docker.js +1 -2
- package/package.json +45 -32
- package/examples/config-native.js +0 -68
- package/src/admin/client/charts.ts +0 -299
- package/src/admin/client/main.js +0 -3
- package/src/admin/client/tailwind.css +0 -41
- package/src/admin/index.html +0 -315
- package/src/admin/js/api.ts +0 -95
- package/src/admin/js/app.ts +0 -306
- package/src/admin/js/eslint.config.js +0 -74
- package/src/admin/js/schemas.ts +0 -153
- package/src/admin/js/templates/config.ts +0 -146
- package/src/admin/js/templates/errorRow.ts +0 -18
- package/src/admin/js/templates/index.ts +0 -3
- package/src/admin/js/templates/poolCard.ts +0 -61
- package/src/admin/js/tsconfig.json +0 -24
- package/src/admin/js/types.ts +0 -223
- package/src/admin/js/ui/theme.ts +0 -93
- package/src/admin/js/ui/views.ts +0 -164
- package/src/admin/js/util/format.ts +0 -27
- package/src/admin/lib/assets/main-zpdhQ1gD.css +0 -1
- package/src/admin/lib/chart.bundle.js +0 -139
- package/src/admin/style.css +0 -1321
- package/src/bin/load-test.js +0 -202
- package/src/handler/handlerAdmin.js +0 -198
- package/src/handler/handlerLogger.js +0 -21
- package/src/handler/handlerMetrics.js +0 -68
- package/src/handler/handlerUpload.js +0 -26
- package/src/handler/plsql/cgi.js +0 -131
- package/src/handler/plsql/errorPage.js +0 -117
- package/src/handler/plsql/handlerPlSql.js +0 -90
- package/src/handler/plsql/parsePage.js +0 -213
- package/src/handler/plsql/procedure.js +0 -356
- package/src/handler/plsql/procedureError.js +0 -40
- package/src/handler/plsql/procedureNamed.js +0 -268
- package/src/handler/plsql/procedureSanitize.js +0 -262
- package/src/handler/plsql/procedureVariable.js +0 -54
- package/src/handler/plsql/request.js +0 -124
- package/src/handler/plsql/requestError.js +0 -23
- package/src/handler/plsql/sendResponse.js +0 -136
- package/src/handler/plsql/stream.js +0 -28
- package/src/handler/plsql/upload.js +0 -109
- package/src/index.js +0 -20
- package/src/server/config.js +0 -57
- package/src/server/connections.js +0 -40
- package/src/server/server.js +0 -289
- package/src/types.js +0 -150
- package/src/util/cache.js +0 -123
- package/src/util/errorToString.js +0 -24
- package/src/util/file.js +0 -91
- package/src/util/html.js +0 -53
- package/src/util/jsonLogger.js +0 -47
- package/src/util/oracle.js +0 -80
- package/src/util/shutdown.js +0 -44
- package/src/util/trace.js +0 -325
- package/src/util/type.js +0 -6
- package/src/util/util.js +0 -66
- package/src/version.js +0 -5
- package/types/handler/handlerAdmin.d.ts +0 -9
- package/types/handler/handlerLogger.d.ts +0 -2
- package/types/handler/handlerMetrics.d.ts +0 -25
- package/types/handler/handlerUpload.d.ts +0 -7
- package/types/handler/plsql/cgi.d.ts +0 -4
- package/types/handler/plsql/errorPage.d.ts +0 -11
- package/types/handler/plsql/handlerPlSql.d.ts +0 -13
- package/types/handler/plsql/parsePage.d.ts +0 -3
- package/types/handler/plsql/procedure.d.ts +0 -12
- package/types/handler/plsql/procedureError.d.ts +0 -23
- package/types/handler/plsql/procedureNamed.d.ts +0 -14
- package/types/handler/plsql/procedureSanitize.d.ts +0 -11
- package/types/handler/plsql/procedureVariable.d.ts +0 -10
- package/types/handler/plsql/request.d.ts +0 -9
- package/types/handler/plsql/requestError.d.ts +0 -8
- package/types/handler/plsql/sendResponse.d.ts +0 -6
- package/types/handler/plsql/upload.d.ts +0 -5
- package/types/index.d.ts +0 -11
- package/types/server/config.d.ts +0 -2
- package/types/server/server.d.ts +0 -74
- package/types/types.d.ts +0 -328
- package/types/util/cache.d.ts +0 -69
- package/types/util/errorToString.d.ts +0 -1
- package/types/util/file.d.ts +0 -6
- package/types/util/html.d.ts +0 -3
- package/types/util/jsonLogger.d.ts +0 -45
- package/types/util/oracle.d.ts +0 -5
- package/types/util/shutdown.d.ts +0 -2
- package/types/util/trace.d.ts +0 -22
- package/types/util/type.d.ts +0 -1
- package/types/util/util.d.ts +0 -3
- package/types/version.d.ts +0 -1
- /package/{src/admin/favicon.svg → dist/frontend/assets/favicon-mQAM4tVu.svg} +0 -0
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type {StatusResponse} from '../schemas.js';
|
|
2
|
-
|
|
3
|
-
type PoolInfo = StatusResponse['pools'][number];
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Pool card template.
|
|
7
|
-
*
|
|
8
|
-
* @param p - The pool data.
|
|
9
|
-
* @returns HTML string for the pool card.
|
|
10
|
-
*/
|
|
11
|
-
export function poolCard(p: PoolInfo): string {
|
|
12
|
-
const utilization = p.connectionsOpen > 0 ? Math.round((p.connectionsInUse / p.connectionsOpen) * 100) : 0;
|
|
13
|
-
const utilColor = utilization > 90 ? 'var(--danger)' : utilization > 70 ? 'var(--warning)' : 'var(--success)';
|
|
14
|
-
|
|
15
|
-
const stats = p.stats ?? undefined;
|
|
16
|
-
|
|
17
|
-
return `
|
|
18
|
-
<div class="card card-clickable transition-all duration-200">
|
|
19
|
-
<div class="card-header">
|
|
20
|
-
<span class="material-symbols-rounded">database</span>
|
|
21
|
-
<h3 class="text-lg font-bold text-bright">${p.name}</h3>
|
|
22
|
-
</div>
|
|
23
|
-
<div class="mb-6">
|
|
24
|
-
<div class="flex justify-between items-baseline mb-2">
|
|
25
|
-
<span class="text-3xl font-bold text-bright">${utilization}%</span>
|
|
26
|
-
<span class="text-main font-mono">${p.connectionsInUse} / ${p.connectionsOpen}</span>
|
|
27
|
-
</div>
|
|
28
|
-
<div class="progress-bar">
|
|
29
|
-
<div class="progress-bar-fill transition-all duration-300" style="width: ${utilization}%; background: ${utilColor}"></div>
|
|
30
|
-
</div>
|
|
31
|
-
<small class="description mt-2 block opacity-75">Pool Utilization (Active / Open)</small>
|
|
32
|
-
</div>
|
|
33
|
-
|
|
34
|
-
<div class="stat-row text-sm">
|
|
35
|
-
<span class="text-xs uppercase font-bold opacity-75">Connections Open</span>
|
|
36
|
-
<span class="font-mono text-accent font-bold">${p.connectionsOpen}</span>
|
|
37
|
-
</div>
|
|
38
|
-
<div class="stat-row text-sm">
|
|
39
|
-
<span class="text-xs uppercase font-bold opacity-75">Connections In Use</span>
|
|
40
|
-
<span class="font-mono text-accent font-bold">${p.connectionsInUse}</span>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
${
|
|
44
|
-
stats
|
|
45
|
-
? `
|
|
46
|
-
<div class="mt-4 pt-4 border-t">
|
|
47
|
-
<div class="stat-row text-sm">
|
|
48
|
-
<span>Total Requests</span>
|
|
49
|
-
<span class="font-mono text-accent font-bold">${stats.totalRequests.toLocaleString()}</span>
|
|
50
|
-
</div>
|
|
51
|
-
<div class="stat-row text-sm">
|
|
52
|
-
<span>Total Timeouts</span>
|
|
53
|
-
<span class="font-mono ${stats.totalTimeouts > 0 ? 'text-danger' : 'text-accent'} font-bold">${stats.totalTimeouts.toLocaleString()}</span>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
`
|
|
57
|
-
: ''
|
|
58
|
-
}
|
|
59
|
-
</div>
|
|
60
|
-
`;
|
|
61
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"noEmit": true,
|
|
4
|
-
"target": "es2020",
|
|
5
|
-
"module": "es2020",
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"allowSyntheticDefaultImports": true,
|
|
10
|
-
"allowJs": true,
|
|
11
|
-
"checkJs": true,
|
|
12
|
-
"strict": true,
|
|
13
|
-
"noImplicitAny": true,
|
|
14
|
-
"strictNullChecks": true,
|
|
15
|
-
"strictFunctionTypes": true,
|
|
16
|
-
"strictBindCallApply": true,
|
|
17
|
-
"noImplicitThis": true,
|
|
18
|
-
"useUnknownInCatchVariables": true,
|
|
19
|
-
"alwaysStrict": true,
|
|
20
|
-
"lib": ["es2020", "dom", "dom.iterable"]
|
|
21
|
-
},
|
|
22
|
-
"include": ["**/*.js", "**/*.ts", "**/*.d.ts"],
|
|
23
|
-
"exclude": ["lib/**"]
|
|
24
|
-
}
|
package/src/admin/js/types.ts
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cache statistics for a cache type.
|
|
3
|
-
*/
|
|
4
|
-
export type CacheStats = {
|
|
5
|
-
hits: number;
|
|
6
|
-
misses: number;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Cache information for a specific cache type.
|
|
11
|
-
*/
|
|
12
|
-
export type CacheInfo = {
|
|
13
|
-
size: number;
|
|
14
|
-
stats: CacheStats;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Cache data for a connection pool.
|
|
19
|
-
*/
|
|
20
|
-
export type CacheData = {
|
|
21
|
-
poolName: string;
|
|
22
|
-
procedureNameCache: CacheInfo;
|
|
23
|
-
argumentCache: CacheInfo;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Pool statistics.
|
|
28
|
-
*/
|
|
29
|
-
export type PoolStats = {
|
|
30
|
-
totalRequests: number;
|
|
31
|
-
totalTimeouts: number;
|
|
32
|
-
totalRequestsEnqueued: number | undefined;
|
|
33
|
-
totalRequestsDequeued: number | undefined;
|
|
34
|
-
totalRequestsFailed: number | undefined;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Pool information.
|
|
39
|
-
*/
|
|
40
|
-
export type PoolInfo = {
|
|
41
|
-
name: string;
|
|
42
|
-
connectionsInUse: number;
|
|
43
|
-
connectionsOpen: number;
|
|
44
|
-
stats: PoolStats | null;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Server metrics.
|
|
49
|
-
*/
|
|
50
|
-
export type Metrics = {
|
|
51
|
-
requestCount: number;
|
|
52
|
-
errorCount: number;
|
|
53
|
-
avgResponseTime: number;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Server configuration - route.
|
|
58
|
-
*/
|
|
59
|
-
export type RouteConfig = {
|
|
60
|
-
route: string;
|
|
61
|
-
// PL/SQL specific
|
|
62
|
-
user?: string;
|
|
63
|
-
password?: string;
|
|
64
|
-
connectString?: string;
|
|
65
|
-
defaultPage?: string;
|
|
66
|
-
pathAlias?: string;
|
|
67
|
-
pathAliasProcedure?: string;
|
|
68
|
-
documentTable?: string;
|
|
69
|
-
exclusionList?: string[];
|
|
70
|
-
requestValidationFunction?: string;
|
|
71
|
-
transactionMode?: unknown;
|
|
72
|
-
errorStyle?: string;
|
|
73
|
-
// Static specific
|
|
74
|
-
directoryPath?: string;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Server configuration.
|
|
79
|
-
*/
|
|
80
|
-
export type ServerConfig = {
|
|
81
|
-
port: number;
|
|
82
|
-
adminRoute?: string;
|
|
83
|
-
adminUser?: string;
|
|
84
|
-
adminPassword?: string;
|
|
85
|
-
loggerFilename: string;
|
|
86
|
-
uploadFileSizeLimit?: number;
|
|
87
|
-
routePlSql: RouteConfig[];
|
|
88
|
-
routeStatic: RouteConfig[];
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Server status response.
|
|
93
|
-
*/
|
|
94
|
-
export type Status = {
|
|
95
|
-
version: string;
|
|
96
|
-
status: 'running' | 'paused' | 'stopped';
|
|
97
|
-
uptime: number;
|
|
98
|
-
startTime: string;
|
|
99
|
-
metrics: Metrics;
|
|
100
|
-
pools: PoolInfo[];
|
|
101
|
-
config: Partial<ServerConfig>;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Historical data for charts.
|
|
106
|
-
*/
|
|
107
|
-
export type HistoryData = {
|
|
108
|
-
labels: string[];
|
|
109
|
-
requests: number[];
|
|
110
|
-
avgResponseTimes: number[];
|
|
111
|
-
poolUsage: Record<string, number[]>;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Chart grid options.
|
|
116
|
-
*/
|
|
117
|
-
export type ChartGridOptions = {
|
|
118
|
-
color: string;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Chart ticks options.
|
|
123
|
-
*/
|
|
124
|
-
export type ChartTicksOptions = {
|
|
125
|
-
color: string;
|
|
126
|
-
display?: boolean;
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Chart scale options.
|
|
131
|
-
*/
|
|
132
|
-
export type ChartScaleOptions = {
|
|
133
|
-
grid?: ChartGridOptions;
|
|
134
|
-
ticks?: ChartTicksOptions;
|
|
135
|
-
title?: {
|
|
136
|
-
display: boolean;
|
|
137
|
-
text: string;
|
|
138
|
-
color: string;
|
|
139
|
-
};
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Chart options.
|
|
144
|
-
*/
|
|
145
|
-
export type ChartOptions = {
|
|
146
|
-
scales?: {
|
|
147
|
-
x?: ChartScaleOptions;
|
|
148
|
-
y?: ChartScaleOptions;
|
|
149
|
-
y1?: ChartScaleOptions;
|
|
150
|
-
};
|
|
151
|
-
plugins?: {
|
|
152
|
-
legend?: {
|
|
153
|
-
labels?: {
|
|
154
|
-
color: string;
|
|
155
|
-
};
|
|
156
|
-
};
|
|
157
|
-
};
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Chart dataset.
|
|
162
|
-
*/
|
|
163
|
-
export type ChartDataset = {
|
|
164
|
-
label: string;
|
|
165
|
-
data: number[];
|
|
166
|
-
borderColor: string;
|
|
167
|
-
backgroundColor: string | undefined;
|
|
168
|
-
fill: boolean | undefined;
|
|
169
|
-
tension: number | undefined;
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Chart data.
|
|
174
|
-
*/
|
|
175
|
-
export type ChartData = {
|
|
176
|
-
labels: string[];
|
|
177
|
-
datasets: ChartDataset[];
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Chart instance interface.
|
|
182
|
-
*/
|
|
183
|
-
export type ChartInstance = {
|
|
184
|
-
data: ChartData;
|
|
185
|
-
options: ChartOptions;
|
|
186
|
-
update: () => void;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Error log entry.
|
|
191
|
-
*/
|
|
192
|
-
export type ErrorLog = {
|
|
193
|
-
timestamp: string;
|
|
194
|
-
req:
|
|
195
|
-
| {
|
|
196
|
-
method: string;
|
|
197
|
-
url: string;
|
|
198
|
-
}
|
|
199
|
-
| undefined;
|
|
200
|
-
message: string;
|
|
201
|
-
details:
|
|
202
|
-
| {
|
|
203
|
-
fullMessage: string;
|
|
204
|
-
}
|
|
205
|
-
| undefined;
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
import type {StatusResponse} from './schemas.js';
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Application state.
|
|
212
|
-
*/
|
|
213
|
-
export type State = {
|
|
214
|
-
currentView: string;
|
|
215
|
-
status: Partial<StatusResponse>;
|
|
216
|
-
maxHistoryPoints: number;
|
|
217
|
-
lastRequestCount: number;
|
|
218
|
-
lastErrorCount: number;
|
|
219
|
-
lastUpdateTime: number;
|
|
220
|
-
refreshTimer: ReturnType<typeof setInterval> | null;
|
|
221
|
-
history: HistoryData;
|
|
222
|
-
charts: Record<string, ChartInstance>;
|
|
223
|
-
};
|
package/src/admin/js/ui/theme.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import type {State, ChartInstance} from '../types.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Theme colors for charts based on theme mode.
|
|
5
|
-
*/
|
|
6
|
-
interface ChartColors {
|
|
7
|
-
gridColor: string;
|
|
8
|
-
textColor: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Get chart colors for the current theme.
|
|
13
|
-
* @param isDark - Whether dark mode is active.
|
|
14
|
-
* @returns Chart color configuration.
|
|
15
|
-
*/
|
|
16
|
-
function getChartColors(isDark: boolean): ChartColors {
|
|
17
|
-
return {
|
|
18
|
-
gridColor: isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)',
|
|
19
|
-
textColor: isDark ? '#94a3b8' : '#475569',
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Update chart options for theme change.
|
|
25
|
-
* @param chart - Chart instance to update.
|
|
26
|
-
* @param colors - Chart colors for the current theme.
|
|
27
|
-
*/
|
|
28
|
-
function updateChartForTheme(chart: ChartInstance, colors: ChartColors): void {
|
|
29
|
-
if (!chart.options.scales) return;
|
|
30
|
-
|
|
31
|
-
const scales = chart.options.scales;
|
|
32
|
-
|
|
33
|
-
if (scales.x) {
|
|
34
|
-
if (scales.x.grid) scales.x.grid.color = colors.gridColor;
|
|
35
|
-
if (scales.x.ticks) scales.x.ticks.color = colors.textColor;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (scales.y?.grid) {
|
|
39
|
-
scales.y.grid.color = colors.gridColor;
|
|
40
|
-
}
|
|
41
|
-
if (scales.y?.ticks) {
|
|
42
|
-
scales.y.ticks.color = colors.textColor;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (scales.y1?.grid) {
|
|
46
|
-
scales.y1.grid.color = colors.gridColor;
|
|
47
|
-
}
|
|
48
|
-
if (scales.y1?.ticks) {
|
|
49
|
-
scales.y1.ticks.color = colors.textColor;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (scales.y?.title) {
|
|
53
|
-
scales.y.title.color = colors.textColor;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (scales.y1?.title) {
|
|
57
|
-
scales.y1.title.color = colors.textColor;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (chart.options.plugins?.legend?.labels) {
|
|
61
|
-
chart.options.plugins.legend.labels.color = colors.textColor;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
chart.update();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Initialize theme management.
|
|
69
|
-
* @param state - Application state containing chart instances.
|
|
70
|
-
*/
|
|
71
|
-
export function initTheme(state: State): void {
|
|
72
|
-
const theme = localStorage.getItem('theme') ?? 'dark';
|
|
73
|
-
const btn = document.getElementById('theme-toggle-btn');
|
|
74
|
-
if (!btn) return;
|
|
75
|
-
|
|
76
|
-
document.body.className = theme;
|
|
77
|
-
|
|
78
|
-
btn.onclick = (): void => {
|
|
79
|
-
const isDark = document.body.classList.contains('dark');
|
|
80
|
-
const newTheme = isDark ? 'light' : 'dark';
|
|
81
|
-
document.body.className = newTheme;
|
|
82
|
-
localStorage.setItem('theme', newTheme);
|
|
83
|
-
|
|
84
|
-
const colors = getChartColors(newTheme === 'dark');
|
|
85
|
-
|
|
86
|
-
Object.values(state.charts).forEach((chartInstance): void => {
|
|
87
|
-
const chart = chartInstance;
|
|
88
|
-
if (chart?.options) {
|
|
89
|
-
updateChartForTheme(chart, colors);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
}
|
package/src/admin/js/ui/views.ts
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import {typedApi} from '../api.js';
|
|
2
|
-
import {formatDuration, formatDateTime} from '../util/format.js';
|
|
3
|
-
import {errorRow, poolCard} from '../templates/index.js';
|
|
4
|
-
import {renderConfig} from '../templates/config.js';
|
|
5
|
-
import type {State, ServerConfig} from '../types.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Refresh the error logs view.
|
|
9
|
-
*/
|
|
10
|
-
export async function refreshErrors(): Promise<void> {
|
|
11
|
-
const logs = await typedApi.getErrorLogs();
|
|
12
|
-
const tbody = document.querySelector('#errors-table tbody');
|
|
13
|
-
if (!tbody) return;
|
|
14
|
-
|
|
15
|
-
tbody.innerHTML = logs.map((l) => errorRow(l)).join('');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Refresh the access logs view.
|
|
20
|
-
*/
|
|
21
|
-
export async function refreshAccess(): Promise<void> {
|
|
22
|
-
const result = await typedApi.getAccessLogs();
|
|
23
|
-
const el = document.getElementById('access-log-view');
|
|
24
|
-
if (!el) return;
|
|
25
|
-
|
|
26
|
-
if (Array.isArray(result)) {
|
|
27
|
-
el.textContent = result.join('\n');
|
|
28
|
-
} else if (result && typeof result === 'object' && 'message' in result) {
|
|
29
|
-
el.textContent = result.message;
|
|
30
|
-
} else {
|
|
31
|
-
el.textContent = 'No logs available';
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (el.parentElement) {
|
|
35
|
-
el.parentElement.scrollTop = el.parentElement.scrollHeight;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Refresh the pools view.
|
|
41
|
-
*
|
|
42
|
-
* @param status - The status data.
|
|
43
|
-
*/
|
|
44
|
-
export function refreshPools(status: Partial<State['status']>): void {
|
|
45
|
-
const poolsCont = document.getElementById('pools-container');
|
|
46
|
-
if (!poolsCont || !status.pools) return;
|
|
47
|
-
|
|
48
|
-
poolsCont.innerHTML = status.pools.map((p) => poolCard(p)).join('');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Refresh the config view.
|
|
53
|
-
*
|
|
54
|
-
* @param state - The application state.
|
|
55
|
-
*/
|
|
56
|
-
export function refreshConfig(state: State): void {
|
|
57
|
-
const config = state.status.config;
|
|
58
|
-
if (!config) return;
|
|
59
|
-
|
|
60
|
-
const cont = document.getElementById('config-view');
|
|
61
|
-
if (!cont) return;
|
|
62
|
-
|
|
63
|
-
// Use type assertion to bypass exactOptionalPropertyTypes check if needed
|
|
64
|
-
cont.innerHTML = renderConfig(config as Partial<ServerConfig>);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Format bytes to human readable format.
|
|
69
|
-
*
|
|
70
|
-
* @param bytes - The number of bytes.
|
|
71
|
-
* @returns Formatted string.
|
|
72
|
-
*/
|
|
73
|
-
function formatBytes(bytes: number): string {
|
|
74
|
-
if (bytes === 0) return '0 B';
|
|
75
|
-
const k = 1024;
|
|
76
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
77
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
78
|
-
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Format microseconds to milliseconds.
|
|
83
|
-
*
|
|
84
|
-
* @param microseconds - CPU time in microseconds.
|
|
85
|
-
* @returns Formatted string.
|
|
86
|
-
*/
|
|
87
|
-
function formatCpuTime(microseconds: number): string {
|
|
88
|
-
const milliseconds = microseconds / 1000;
|
|
89
|
-
return `${milliseconds.toFixed(2)} ms`;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Refresh the system info view.
|
|
94
|
-
*
|
|
95
|
-
* @param status - The status data.
|
|
96
|
-
*/
|
|
97
|
-
export function refreshSystem(status: Partial<State['status']>): void {
|
|
98
|
-
const middlewareVersion = document.getElementById('middleware-version');
|
|
99
|
-
if (middlewareVersion && status.version) middlewareVersion.textContent = status.version;
|
|
100
|
-
|
|
101
|
-
const nodeVersion = document.getElementById('node-version');
|
|
102
|
-
if (nodeVersion && status.system) nodeVersion.textContent = status.system.nodeVersion;
|
|
103
|
-
|
|
104
|
-
const platform = document.getElementById('platform-info');
|
|
105
|
-
if (platform && status.system) platform.textContent = `${status.system.platform} (${status.system.arch})`;
|
|
106
|
-
|
|
107
|
-
const systemUptime = document.getElementById('system-uptime');
|
|
108
|
-
if (systemUptime && status.uptime) systemUptime.textContent = formatDuration(status.uptime);
|
|
109
|
-
|
|
110
|
-
const systemStartTime = document.getElementById('system-start-time');
|
|
111
|
-
if (systemStartTime && status.startTime) systemStartTime.textContent = formatDateTime(status.startTime);
|
|
112
|
-
|
|
113
|
-
const systemStatusText = document.getElementById('system-status-text');
|
|
114
|
-
if (systemStatusText) systemStatusText.textContent = status.status?.toUpperCase() ?? '-';
|
|
115
|
-
|
|
116
|
-
const totalRequests = document.getElementById('total-requests');
|
|
117
|
-
if (totalRequests && status.metrics) totalRequests.textContent = status.metrics.requestCount.toLocaleString();
|
|
118
|
-
|
|
119
|
-
const totalErrors = document.getElementById('total-errors');
|
|
120
|
-
if (totalErrors && status.metrics) totalErrors.textContent = status.metrics.errorCount.toLocaleString();
|
|
121
|
-
|
|
122
|
-
const activePools = document.getElementById('active-pools');
|
|
123
|
-
if (activePools && status.pools) activePools.textContent = status.pools.length.toString();
|
|
124
|
-
|
|
125
|
-
// Memory usage
|
|
126
|
-
if (status.system?.memory) {
|
|
127
|
-
const memHeapUsed = document.getElementById('memory-heap-used');
|
|
128
|
-
if (memHeapUsed) memHeapUsed.textContent = formatBytes(status.system.memory.heapUsed);
|
|
129
|
-
|
|
130
|
-
const memHeapTotal = document.getElementById('memory-heap-total');
|
|
131
|
-
if (memHeapTotal) memHeapTotal.textContent = formatBytes(status.system.memory.heapTotal);
|
|
132
|
-
|
|
133
|
-
const memRss = document.getElementById('memory-rss');
|
|
134
|
-
if (memRss) memRss.textContent = formatBytes(status.system.memory.rss);
|
|
135
|
-
|
|
136
|
-
const memExternal = document.getElementById('memory-external');
|
|
137
|
-
if (memExternal) memExternal.textContent = formatBytes(status.system.memory.external);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// CPU usage
|
|
141
|
-
if (status.system?.cpu) {
|
|
142
|
-
const cpuUser = document.getElementById('cpu-user');
|
|
143
|
-
if (cpuUser) cpuUser.textContent = formatCpuTime(status.system.cpu.user);
|
|
144
|
-
|
|
145
|
-
const cpuSystem = document.getElementById('cpu-system');
|
|
146
|
-
if (cpuSystem) cpuSystem.textContent = formatCpuTime(status.system.cpu.system);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const systemRefreshStatus = document.getElementById('system-refresh-status');
|
|
150
|
-
const autoRefreshToggle = document.getElementById('auto-refresh-toggle') as HTMLInputElement | null;
|
|
151
|
-
const refreshInterval = document.getElementById('refresh-interval') as HTMLSelectElement | null;
|
|
152
|
-
if (systemRefreshStatus && autoRefreshToggle && refreshInterval) {
|
|
153
|
-
if (autoRefreshToggle.checked) {
|
|
154
|
-
const intervalSec = parseInt(refreshInterval.value) / 1000;
|
|
155
|
-
systemRefreshStatus.textContent = `Active (${intervalSec}s)`;
|
|
156
|
-
systemRefreshStatus.classList.add('text-success');
|
|
157
|
-
systemRefreshStatus.classList.remove('text-accent');
|
|
158
|
-
} else {
|
|
159
|
-
systemRefreshStatus.textContent = 'Paused';
|
|
160
|
-
systemRefreshStatus.classList.remove('text-success');
|
|
161
|
-
systemRefreshStatus.classList.add('text-accent');
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Format a duration in seconds to a human-readable string.
|
|
3
|
-
* @param {number} seconds - Duration in seconds.
|
|
4
|
-
* @returns {string} Formatted duration string (e.g., "2d 3h 45m 30s").
|
|
5
|
-
*/
|
|
6
|
-
export function formatDuration(seconds: number): string {
|
|
7
|
-
const d = Math.floor(seconds / (3600 * 24));
|
|
8
|
-
const h = Math.floor((seconds % (3600 * 24)) / 3600);
|
|
9
|
-
const m = Math.floor((seconds % 3600) / 60);
|
|
10
|
-
const s = Math.floor(seconds % 60);
|
|
11
|
-
const parts: string[] = [];
|
|
12
|
-
if (d > 0) parts.push(`${d}d`);
|
|
13
|
-
if (h > 0) parts.push(`${h}h`);
|
|
14
|
-
if (m > 0) parts.push(`${m}m`);
|
|
15
|
-
parts.push(`${s}s`);
|
|
16
|
-
return parts.join(' ');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Format an ISO date string to a localized date time string.
|
|
21
|
-
* @param isoDate - ISO date string.
|
|
22
|
-
* @returns Formatted date time string or "-" if invalid.
|
|
23
|
-
*/
|
|
24
|
-
export function formatDateTime(isoDate: string): string {
|
|
25
|
-
if (!isoDate) return '-';
|
|
26
|
-
return new Date(isoDate).toLocaleString();
|
|
27
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-bold:700;--tracking-wider:.05em;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-success:#10b981;--color-danger:#ef4444}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.fixed{position:fixed}.static{position:static}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.table{display:table}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.items-baseline{align-items:baseline}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-3{gap:calc(var(--spacing)*3)}.gap-6{gap:calc(var(--spacing)*6)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.pt-4{padding-top:calc(var(--spacing)*4)}.font-mono{font-family:var(--font-mono)}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-all{word-break:break-all}.text-danger{color:var(--color-danger)}.text-success{color:var(--color-success)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.opacity-75{opacity:.75}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}@media(min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}body{-webkit-font-smoothing:antialiased;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}body.dark{--bg-main:#0f172a;--bg-side:#1e293b;--bg-card:#1e293b;--bg-hover:#334155;--text-main:#94a3b8;--text-bright:#f8fafc;--border:#334155}body:not(.dark){--bg-main:#f8fafc;--bg-side:#fff;--bg-card:#fff;--bg-hover:#f1f5f9;--text-main:#475569;--text-bright:#0f172a;--border:#e2e8f0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}
|