v1.0.19
This commit is contained in:
1 parent
4b430340db
commit
9c451aedf9
120 files changed
+7777
-6046
No files matched your search
+34
-40
@@ -7,7 +7,6 @@
|
||||
|
||||
import {
|
||||
db,
|
||||
rawClient,
|
||||
isPostgres,
|
||||
isSqlite,
|
||||
eq,
|
||||
@@ -200,6 +199,10 @@ export async function deleteEnvironment(id: number): Promise<boolean> {
|
||||
const env = await getEnvironment(id);
|
||||
if (!env) return false;
|
||||
|
||||
// Clean up in-memory metrics
|
||||
const { clearEnvironmentMetrics } = await import('./metrics-store.js');
|
||||
clearEnvironmentMetrics(id);
|
||||
|
||||
// Clean up related records that don't have cascade delete defined
|
||||
try {
|
||||
await db.delete(hostMetrics).where(eq(hostMetrics.environmentId, id));
|
||||
@@ -576,45 +579,28 @@ export async function saveHostMetric(
|
||||
memoryPercent: number,
|
||||
memoryUsed: number,
|
||||
memoryTotal: number,
|
||||
environmentId?: number
|
||||
environmentId?: number,
|
||||
_skipEnvCheck = false
|
||||
): Promise<void> {
|
||||
// Verify environment exists before inserting (avoids FK violations on deleted envs)
|
||||
if (environmentId) {
|
||||
const env = await getEnvironment(environmentId);
|
||||
if (!env) return;
|
||||
}
|
||||
|
||||
await db.insert(hostMetrics).values({
|
||||
environmentId: environmentId || null,
|
||||
cpuPercent,
|
||||
memoryPercent,
|
||||
memoryUsed,
|
||||
memoryTotal
|
||||
});
|
||||
|
||||
// Cleanup old metrics (keep last 24 hours)
|
||||
const cutoff24h = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
||||
await db.delete(hostMetrics).where(sql`timestamp < ${cutoff24h}`);
|
||||
// Delegated to in-memory ring buffer (no DB writes)
|
||||
if (!environmentId) return;
|
||||
const { pushMetric } = await import('./metrics-store.js');
|
||||
pushMetric(environmentId, cpuPercent, memoryPercent, memoryUsed, memoryTotal);
|
||||
}
|
||||
|
||||
export async function getHostMetrics(limit = 60, environmentId?: number): Promise<HostMetric[]> {
|
||||
if (environmentId) {
|
||||
return db.select().from(hostMetrics)
|
||||
.where(eq(hostMetrics.environmentId, environmentId))
|
||||
.orderBy(desc(hostMetrics.timestamp))
|
||||
.limit(limit);
|
||||
const { getMetricsHistory } = await import('./metrics-store.js');
|
||||
// getMetricsHistory returns oldest-first, but callers expect newest-first
|
||||
return getMetricsHistory(environmentId, limit).reverse();
|
||||
}
|
||||
return db.select().from(hostMetrics)
|
||||
.orderBy(desc(hostMetrics.timestamp))
|
||||
.limit(limit);
|
||||
const { getAllMetrics } = await import('./metrics-store.js');
|
||||
return getAllMetrics(limit);
|
||||
}
|
||||
|
||||
export async function getLatestHostMetrics(environmentId: number): Promise<HostMetric | null> {
|
||||
const results = await db.select().from(hostMetrics)
|
||||
.where(eq(hostMetrics.environmentId, environmentId))
|
||||
.orderBy(desc(hostMetrics.timestamp))
|
||||
.limit(1);
|
||||
return results[0] ?? null;
|
||||
const { getLatestMetric } = await import('./metrics-store.js');
|
||||
return getLatestMetric(environmentId);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -3269,20 +3255,23 @@ export interface ContainerEventResult {
|
||||
offset: number;
|
||||
}
|
||||
|
||||
export async function logContainerEvent(data: ContainerEventCreateData): Promise<ContainerEventData> {
|
||||
// Timestamp is already an ISO-8601 string from event-subprocess
|
||||
// Both SQLite and PostgreSQL schemas use mode: 'string' so we pass it directly
|
||||
const result = await db.insert(containerEvents).values({
|
||||
export async function logContainerEvent(
|
||||
data: ContainerEventCreateData
|
||||
): Promise<ContainerEventData> {
|
||||
const attrs = data.actorAttributes ? JSON.stringify(data.actorAttributes) : null;
|
||||
|
||||
const [inserted] = await db.insert(containerEvents).values({
|
||||
environmentId: data.environmentId ?? null,
|
||||
containerId: data.containerId,
|
||||
containerName: data.containerName ?? null,
|
||||
image: data.image ?? null,
|
||||
action: data.action,
|
||||
actorAttributes: data.actorAttributes ? JSON.stringify(data.actorAttributes) : null,
|
||||
actorAttributes: attrs,
|
||||
timestamp: data.timestamp
|
||||
}).returning();
|
||||
}).returning({ id: containerEvents.id });
|
||||
|
||||
return getContainerEvent(result[0].id) as Promise<ContainerEventData>;
|
||||
const event = await getContainerEvent(inserted.id);
|
||||
return event!;
|
||||
}
|
||||
|
||||
export async function getContainerEvent(id: number): Promise<ContainerEventData | undefined> {
|
||||
@@ -4502,11 +4491,16 @@ export async function setStackEnvVars(
|
||||
));
|
||||
}
|
||||
|
||||
// Insert new vars
|
||||
// Insert new vars (deduplicate by key - last entry wins)
|
||||
if (variables.length > 0) {
|
||||
const seen = new Map<string, { key: string; value: string; isSecret?: boolean }>();
|
||||
for (const v of variables) {
|
||||
seen.set(v.key, v);
|
||||
}
|
||||
const deduped = Array.from(seen.values());
|
||||
const now = new Date().toISOString();
|
||||
await db.insert(stackEnvironmentVariables).values(
|
||||
variables.map(v => ({
|
||||
deduped.map(v => ({
|
||||
stackName,
|
||||
environmentId,
|
||||
key: v.key,
|
||||
|
||||
Reference in new issue
Block a user