@mgsoftwarebv/mg-dashboard-mcp 7.0.6 → 7.0.8
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/dist/index.js +717 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { sql } from 'drizzle-orm';
|
|
|
20
20
|
import { once } from 'events';
|
|
21
21
|
import { lookup } from 'dns/promises';
|
|
22
22
|
import { connect } from 'tls';
|
|
23
|
+
import { pgTable, timestamp, jsonb, uuid, boolean, text, pgEnum, integer, uniqueIndex, index, bigint } from 'drizzle-orm/pg-core';
|
|
23
24
|
import { tasks } from '@trigger.dev/sdk/v3';
|
|
24
25
|
import { HeadObjectCommand, S3Client, ListObjectsV2Command, DeleteObjectsCommand, DeleteObjectCommand, CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand, AbortMultipartUploadCommand, PutObjectCommand, GetObjectCommand, CopyObjectCommand } from '@aws-sdk/client-s3';
|
|
25
26
|
|
|
@@ -618,6 +619,12 @@ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
|
618
619
|
"INTERRUPTED",
|
|
619
620
|
"EXPIRED"
|
|
620
621
|
]);
|
|
622
|
+
var EXECUTE_PIPELINE_TASK = "execute-pipeline";
|
|
623
|
+
function validateExecutePipelineTestPayload(payload) {
|
|
624
|
+
const stepIds = payload.stepIds;
|
|
625
|
+
if (Array.isArray(stepIds) && stepIds.length > 0) return null;
|
|
626
|
+
return 'execute-pipeline requires real stepIds from a release row. Use a lightweight task (e.g. "hello-world") for connectivity checks, or pass a payload with releaseId (UUID) and non-empty stepIds.';
|
|
627
|
+
}
|
|
621
628
|
var TRIGGER_SERVER_ID = "03659d55-e194-400d-b82a-bf6457371ded";
|
|
622
629
|
var COMPOSE_PROJECT = "mg-dashboard-supabase-trigger";
|
|
623
630
|
var PG_CONTAINER = `${COMPOSE_PROJECT}-postgres-1`;
|
|
@@ -677,10 +684,10 @@ var TRIGGER_TOOL_MODULE_MAP = {
|
|
|
677
684
|
"trigger-run": "ci_cd"
|
|
678
685
|
};
|
|
679
686
|
async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
|
|
680
|
-
const
|
|
687
|
+
const sql29 = `SELECT re.\\"apiKey\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
|
|
681
688
|
const cmd = [
|
|
682
689
|
`PORT=$(docker port "${WA_CONTAINER}" 3000/tcp 2>/dev/null | head -1 | sed 's/.*://')`,
|
|
683
|
-
`KEY=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
690
|
+
`KEY=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql29}" 2>/dev/null | tr -d '[:space:]')`,
|
|
684
691
|
'echo "$PORT|$KEY"'
|
|
685
692
|
].join(" && ");
|
|
686
693
|
const result = await sshExec2(conn, cmd, proxy);
|
|
@@ -701,8 +708,8 @@ async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
|
|
|
701
708
|
return { port, apiKey: apiKey2 };
|
|
702
709
|
}
|
|
703
710
|
async function fetchRunLogs(runId, conn, proxy, sshExec2) {
|
|
704
|
-
const
|
|
705
|
-
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
711
|
+
const sql29 = `SELECT level, message, \\"isError\\", \\"createdAt\\" FROM \\"TaskEvent\\" WHERE \\"runId\\" = '${runId}' AND level IN ('INFO','WARN','ERROR','DEBUG','LOG','TRACE') ORDER BY \\"startTime\\" ASC LIMIT 200`;
|
|
712
|
+
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql29}" 2>/dev/null`;
|
|
706
713
|
const result = await sshExec2(conn, cmd, proxy);
|
|
707
714
|
const output = result.stdout.trim();
|
|
708
715
|
if (!output) return "";
|
|
@@ -784,8 +791,8 @@ async function handleTriggerTool(name, args2, deps) {
|
|
|
784
791
|
switch (name) {
|
|
785
792
|
// -----------------------------------------------------------------
|
|
786
793
|
case "trigger-list": {
|
|
787
|
-
const
|
|
788
|
-
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
794
|
+
const sql29 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
|
|
795
|
+
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql29}" 2>/dev/null`;
|
|
789
796
|
const result = await sshExec2(conn, cmd, proxy);
|
|
790
797
|
const output = result.stdout.trim();
|
|
791
798
|
if (!output) {
|
|
@@ -866,8 +873,15 @@ ${rawJson.substring(0, 500)}` }] };
|
|
|
866
873
|
throw new Error(`Invalid JSON payload: ${String(args2.payload).substring(0, 200)}`);
|
|
867
874
|
}
|
|
868
875
|
}
|
|
876
|
+
const parsedPayload = JSON.parse(payload);
|
|
877
|
+
if (taskId === EXECUTE_PIPELINE_TASK) {
|
|
878
|
+
const validationError = validateExecutePipelineTestPayload(parsedPayload);
|
|
879
|
+
if (validationError) {
|
|
880
|
+
return { content: [{ type: "text", text: `Error: ${validationError}` }] };
|
|
881
|
+
}
|
|
882
|
+
}
|
|
869
883
|
const triggerBody = JSON.stringify({
|
|
870
|
-
payload:
|
|
884
|
+
payload: parsedPayload,
|
|
871
885
|
options: { tags: ["mcp-test"], test: true }
|
|
872
886
|
});
|
|
873
887
|
const triggerJson = await triggerApi(
|
|
@@ -943,9 +957,9 @@ async function fetchAndFormatRun(conn, proxy, sshExec2, instance, runId) {
|
|
|
943
957
|
return { content: [{ type: "text", text: `Invalid API response:
|
|
944
958
|
${rawJson.substring(0, 500)}` }] };
|
|
945
959
|
}
|
|
946
|
-
let
|
|
947
|
-
if (logs)
|
|
948
|
-
return { content: [{ type: "text", text }] };
|
|
960
|
+
let text7 = formatRunDetail(run);
|
|
961
|
+
if (logs) text7 += "\n\n--- Logs ---\n" + logs;
|
|
962
|
+
return { content: [{ type: "text", text: text7 }] };
|
|
949
963
|
}
|
|
950
964
|
async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSeconds) {
|
|
951
965
|
const pollInterval = 3e3;
|
|
@@ -967,10 +981,10 @@ async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSec
|
|
|
967
981
|
continue;
|
|
968
982
|
}
|
|
969
983
|
if (TERMINAL_STATUSES.has(run.status)) {
|
|
970
|
-
let
|
|
984
|
+
let text7 = formatRunDetail(run);
|
|
971
985
|
const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
|
|
972
|
-
if (logs)
|
|
973
|
-
return { content: [{ type: "text", text }] };
|
|
986
|
+
if (logs) text7 += "\n\n--- Logs ---\n" + logs;
|
|
987
|
+
return { content: [{ type: "text", text: text7 }] };
|
|
974
988
|
}
|
|
975
989
|
}
|
|
976
990
|
return {
|
|
@@ -3657,10 +3671,10 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
3657
3671
|
// }) as any;
|
|
3658
3672
|
// return merged;
|
|
3659
3673
|
// }
|
|
3660
|
-
catchall(
|
|
3674
|
+
catchall(index5) {
|
|
3661
3675
|
return new _ZodObject({
|
|
3662
3676
|
...this._def,
|
|
3663
|
-
catchall:
|
|
3677
|
+
catchall: index5
|
|
3664
3678
|
});
|
|
3665
3679
|
}
|
|
3666
3680
|
pick(mask) {
|
|
@@ -3978,9 +3992,9 @@ function mergeValues(a, b) {
|
|
|
3978
3992
|
return { valid: false };
|
|
3979
3993
|
}
|
|
3980
3994
|
const newArray = [];
|
|
3981
|
-
for (let
|
|
3982
|
-
const itemA = a[
|
|
3983
|
-
const itemB = b[
|
|
3995
|
+
for (let index5 = 0; index5 < a.length; index5++) {
|
|
3996
|
+
const itemA = a[index5];
|
|
3997
|
+
const itemB = b[index5];
|
|
3984
3998
|
const sharedValue = mergeValues(itemA, itemB);
|
|
3985
3999
|
if (!sharedValue.valid) {
|
|
3986
4000
|
return { valid: false };
|
|
@@ -4186,10 +4200,10 @@ var ZodMap = class extends ZodType {
|
|
|
4186
4200
|
}
|
|
4187
4201
|
const keyType = this._def.keyType;
|
|
4188
4202
|
const valueType = this._def.valueType;
|
|
4189
|
-
const pairs = [...ctx.data.entries()].map(([key, value],
|
|
4203
|
+
const pairs = [...ctx.data.entries()].map(([key, value], index5) => {
|
|
4190
4204
|
return {
|
|
4191
|
-
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [
|
|
4192
|
-
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [
|
|
4205
|
+
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index5, "key"])),
|
|
4206
|
+
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index5, "value"]))
|
|
4193
4207
|
};
|
|
4194
4208
|
});
|
|
4195
4209
|
if (ctx.common.async) {
|
|
@@ -5103,7 +5117,642 @@ external_exports.array(LitespeedVhostMappingSchema);
|
|
|
5103
5117
|
function normalizeVhostDomain(value) {
|
|
5104
5118
|
return value.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/\.+$/, "").toLowerCase();
|
|
5105
5119
|
}
|
|
5120
|
+
var users = pgTable("user", {
|
|
5121
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5122
|
+
email: text("email").notNull().unique(),
|
|
5123
|
+
fullName: text("full_name"),
|
|
5124
|
+
avatarUrl: text("avatar_url"),
|
|
5125
|
+
emailVerified: boolean("email_verified").notNull().default(false),
|
|
5126
|
+
roleId: uuid("role_id"),
|
|
5127
|
+
permissions: jsonb("permissions"),
|
|
5128
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5129
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5130
|
+
});
|
|
5131
|
+
pgTable("session", {
|
|
5132
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5133
|
+
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
5134
|
+
token: text("token").notNull().unique(),
|
|
5135
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
5136
|
+
ipAddress: text("ip_address"),
|
|
5137
|
+
userAgent: text("user_agent"),
|
|
5138
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5139
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5140
|
+
});
|
|
5141
|
+
pgTable("account", {
|
|
5142
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5143
|
+
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
5144
|
+
providerId: text("provider_id").notNull(),
|
|
5145
|
+
accountId: text("account_id").notNull(),
|
|
5146
|
+
password: text("password"),
|
|
5147
|
+
accessToken: text("access_token"),
|
|
5148
|
+
refreshToken: text("refresh_token"),
|
|
5149
|
+
accessTokenExpiresAt: timestamp("access_token_expires_at", {
|
|
5150
|
+
withTimezone: true
|
|
5151
|
+
}),
|
|
5152
|
+
refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
|
|
5153
|
+
withTimezone: true
|
|
5154
|
+
}),
|
|
5155
|
+
scope: text("scope"),
|
|
5156
|
+
idToken: text("id_token"),
|
|
5157
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5158
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5159
|
+
});
|
|
5160
|
+
pgTable("verification", {
|
|
5161
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5162
|
+
identifier: text("identifier").notNull(),
|
|
5163
|
+
value: text("value").notNull(),
|
|
5164
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
5165
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5166
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5167
|
+
});
|
|
5168
|
+
pgTable("two_factor", {
|
|
5169
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5170
|
+
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
5171
|
+
secret: text("secret").notNull(),
|
|
5172
|
+
backupCodes: text("backup_codes")
|
|
5173
|
+
});
|
|
5174
|
+
var managedServerOs = pgEnum("managed_server_os", [
|
|
5175
|
+
"linux",
|
|
5176
|
+
"windows",
|
|
5177
|
+
"unknown"
|
|
5178
|
+
]);
|
|
5179
|
+
var serverAgentStatus = pgEnum("server_agent_status", [
|
|
5180
|
+
"not_installed",
|
|
5181
|
+
"installing",
|
|
5182
|
+
"online",
|
|
5183
|
+
"degraded",
|
|
5184
|
+
"offline",
|
|
5185
|
+
"error"
|
|
5186
|
+
]);
|
|
5187
|
+
var resourceKind = pgEnum("resource_kind", [
|
|
5188
|
+
"wordpress",
|
|
5189
|
+
"prestashop",
|
|
5190
|
+
"postgres_cluster",
|
|
5191
|
+
"postgres_database",
|
|
5192
|
+
"mysql_database",
|
|
5193
|
+
"mariadb_database",
|
|
5194
|
+
"boiler_project",
|
|
5195
|
+
"boiler_web_project",
|
|
5196
|
+
"static_site",
|
|
5197
|
+
"docker_app",
|
|
5198
|
+
"domain",
|
|
5199
|
+
"backup_policy",
|
|
5200
|
+
"server_service"
|
|
5201
|
+
]);
|
|
5202
|
+
var resourceStatus = pgEnum("resource_status", [
|
|
5203
|
+
"unknown",
|
|
5204
|
+
"healthy",
|
|
5205
|
+
"degraded",
|
|
5206
|
+
"failed",
|
|
5207
|
+
"missing",
|
|
5208
|
+
"provisioning",
|
|
5209
|
+
"disabled"
|
|
5210
|
+
]);
|
|
5211
|
+
var resourceOwnership = pgEnum("resource_ownership", [
|
|
5212
|
+
"managed",
|
|
5213
|
+
"discovered",
|
|
5214
|
+
"external"
|
|
5215
|
+
]);
|
|
5216
|
+
var operationStatus = pgEnum("operation_status", [
|
|
5217
|
+
"queued",
|
|
5218
|
+
"running",
|
|
5219
|
+
"succeeded",
|
|
5220
|
+
"failed",
|
|
5221
|
+
"cancelled"
|
|
5222
|
+
]);
|
|
5223
|
+
var alertSeverity = pgEnum("alert_severity", [
|
|
5224
|
+
"info",
|
|
5225
|
+
"warning",
|
|
5226
|
+
"critical"
|
|
5227
|
+
]);
|
|
5228
|
+
var managedServer = pgTable(
|
|
5229
|
+
"managed_server",
|
|
5230
|
+
{
|
|
5231
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5232
|
+
name: text("name").notNull(),
|
|
5233
|
+
hostname: text("hostname").notNull(),
|
|
5234
|
+
port: integer("port").notNull().default(22),
|
|
5235
|
+
username: text("username").notNull(),
|
|
5236
|
+
authMethod: text("auth_method").notNull().default("ssh_key"),
|
|
5237
|
+
os: managedServerOs("os").notNull().default("unknown"),
|
|
5238
|
+
provider: text("provider"),
|
|
5239
|
+
region: text("region"),
|
|
5240
|
+
tags: jsonb("tags").$type().notNull().default([]),
|
|
5241
|
+
agentStatus: serverAgentStatus("agent_status").notNull().default("not_installed"),
|
|
5242
|
+
agentVersion: text("agent_version"),
|
|
5243
|
+
lastSeenAt: timestamp("last_seen_at", { withTimezone: true }),
|
|
5244
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5245
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5246
|
+
},
|
|
5247
|
+
(table) => [
|
|
5248
|
+
uniqueIndex("managed_server_hostname_port_uidx").on(
|
|
5249
|
+
table.hostname,
|
|
5250
|
+
table.port
|
|
5251
|
+
),
|
|
5252
|
+
index("managed_server_agent_status_idx").on(table.agentStatus)
|
|
5253
|
+
]
|
|
5254
|
+
);
|
|
5255
|
+
pgTable("server_credential", {
|
|
5256
|
+
serverId: uuid("server_id").primaryKey().references(() => managedServer.id, { onDelete: "cascade" }),
|
|
5257
|
+
passwordEncrypted: text("password_encrypted"),
|
|
5258
|
+
sshKeyEncrypted: text("ssh_key_encrypted"),
|
|
5259
|
+
sshKeyPassphraseEncrypted: text("ssh_key_passphrase_encrypted"),
|
|
5260
|
+
dbRootPasswordEncrypted: text("db_root_password_encrypted"),
|
|
5261
|
+
monitoringApiKeyEncrypted: text("monitoring_api_key_encrypted"),
|
|
5262
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5263
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5264
|
+
});
|
|
5265
|
+
pgTable(
|
|
5266
|
+
"server_connection_log",
|
|
5267
|
+
{
|
|
5268
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5269
|
+
serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
|
|
5270
|
+
userId: uuid("user_id").notNull(),
|
|
5271
|
+
connectedAt: timestamp("connected_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5272
|
+
disconnectedAt: timestamp("disconnected_at", { withTimezone: true }),
|
|
5273
|
+
durationSeconds: integer("duration_seconds"),
|
|
5274
|
+
status: text("status").notNull(),
|
|
5275
|
+
errorMessage: text("error_message"),
|
|
5276
|
+
clientIp: text("client_ip"),
|
|
5277
|
+
terminalCols: integer("terminal_cols"),
|
|
5278
|
+
terminalRows: integer("terminal_rows"),
|
|
5279
|
+
outputSizeBytes: bigint("output_size_bytes", { mode: "number" }).notNull().default(0),
|
|
5280
|
+
inputSizeBytes: bigint("input_size_bytes", { mode: "number" }).notNull().default(0),
|
|
5281
|
+
commandCount: integer("command_count").notNull().default(0),
|
|
5282
|
+
connectionType: text("connection_type").notNull().default("terminal"),
|
|
5283
|
+
action: text("action"),
|
|
5284
|
+
rawOutput: text("raw_output"),
|
|
5285
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5286
|
+
},
|
|
5287
|
+
(table) => [
|
|
5288
|
+
index("server_connection_log_server_idx").on(table.serverId),
|
|
5289
|
+
index("server_connection_log_user_idx").on(table.userId),
|
|
5290
|
+
index("server_connection_log_connected_idx").on(table.connectedAt),
|
|
5291
|
+
index("server_connection_log_status_idx").on(table.status)
|
|
5292
|
+
]
|
|
5293
|
+
);
|
|
5294
|
+
pgTable(
|
|
5295
|
+
"mcp_audit_log",
|
|
5296
|
+
{
|
|
5297
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5298
|
+
apiKeyId: uuid("api_key_id"),
|
|
5299
|
+
userId: uuid("user_id"),
|
|
5300
|
+
toolName: text("tool_name"),
|
|
5301
|
+
arguments: jsonb("arguments"),
|
|
5302
|
+
ipAddress: text("ip_address"),
|
|
5303
|
+
serverId: uuid("server_id"),
|
|
5304
|
+
resultStatus: text("result_status").notNull().default("success"),
|
|
5305
|
+
errorMessage: text("error_message"),
|
|
5306
|
+
durationMs: integer("duration_ms"),
|
|
5307
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5308
|
+
},
|
|
5309
|
+
(table) => [
|
|
5310
|
+
index("mcp_audit_log_created_at_idx").on(table.createdAt),
|
|
5311
|
+
index("mcp_audit_log_user_idx").on(table.userId),
|
|
5312
|
+
index("mcp_audit_log_tool_name_idx").on(table.toolName),
|
|
5313
|
+
index("mcp_audit_log_server_idx").on(table.serverId),
|
|
5314
|
+
index("mcp_audit_log_api_key_idx").on(table.apiKeyId),
|
|
5315
|
+
index("mcp_audit_log_result_status_idx").on(table.resultStatus)
|
|
5316
|
+
]
|
|
5317
|
+
);
|
|
5318
|
+
var resource = pgTable(
|
|
5319
|
+
"resource",
|
|
5320
|
+
{
|
|
5321
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5322
|
+
serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
|
|
5323
|
+
parentResourceId: uuid("parent_resource_id"),
|
|
5324
|
+
kind: resourceKind("kind").notNull(),
|
|
5325
|
+
slug: text("slug").notNull(),
|
|
5326
|
+
displayName: text("display_name").notNull(),
|
|
5327
|
+
spec: jsonb("spec").$type().notNull().default({}),
|
|
5328
|
+
state: jsonb("state").$type().notNull().default({}),
|
|
5329
|
+
status: resourceStatus("status").notNull().default("unknown"),
|
|
5330
|
+
ownership: resourceOwnership("ownership").notNull().default("managed"),
|
|
5331
|
+
lastDetectedAt: timestamp("last_detected_at", { withTimezone: true }),
|
|
5332
|
+
lastEventId: uuid("last_event_id"),
|
|
5333
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5334
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5335
|
+
},
|
|
5336
|
+
(table) => [
|
|
5337
|
+
uniqueIndex("resource_server_kind_slug_uidx").on(
|
|
5338
|
+
table.serverId,
|
|
5339
|
+
table.kind,
|
|
5340
|
+
table.slug
|
|
5341
|
+
),
|
|
5342
|
+
index("resource_server_idx").on(table.serverId),
|
|
5343
|
+
index("resource_parent_idx").on(table.parentResourceId),
|
|
5344
|
+
index("resource_kind_status_idx").on(table.kind, table.status)
|
|
5345
|
+
]
|
|
5346
|
+
);
|
|
5347
|
+
pgTable(
|
|
5348
|
+
"resource_event",
|
|
5349
|
+
{
|
|
5350
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5351
|
+
resourceId: uuid("resource_id").notNull().references(() => resource.id, { onDelete: "cascade" }),
|
|
5352
|
+
kind: text("kind").notNull(),
|
|
5353
|
+
before: jsonb("before").$type(),
|
|
5354
|
+
after: jsonb("after").$type(),
|
|
5355
|
+
actorUserId: text("actor_user_id"),
|
|
5356
|
+
operationId: uuid("operation_id"),
|
|
5357
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5358
|
+
},
|
|
5359
|
+
(table) => [
|
|
5360
|
+
index("resource_event_resource_created_idx").on(
|
|
5361
|
+
table.resourceId,
|
|
5362
|
+
table.createdAt
|
|
5363
|
+
),
|
|
5364
|
+
index("resource_event_operation_idx").on(table.operationId)
|
|
5365
|
+
]
|
|
5366
|
+
);
|
|
5367
|
+
var operation = pgTable(
|
|
5368
|
+
"operation",
|
|
5369
|
+
{
|
|
5370
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5371
|
+
kind: text("kind").notNull(),
|
|
5372
|
+
resourceId: uuid("resource_id").references(() => resource.id, {
|
|
5373
|
+
onDelete: "set null"
|
|
5374
|
+
}),
|
|
5375
|
+
serverId: uuid("server_id").references(() => managedServer.id, {
|
|
5376
|
+
onDelete: "set null"
|
|
5377
|
+
}),
|
|
5378
|
+
status: operationStatus("status").notNull().default("queued"),
|
|
5379
|
+
steps: jsonb("steps").$type().notNull().default([]),
|
|
5380
|
+
idempotencyKey: text("idempotency_key").notNull(),
|
|
5381
|
+
triggerRunId: text("trigger_run_id"),
|
|
5382
|
+
logsR2Key: text("logs_r2_key"),
|
|
5383
|
+
logs: text("logs"),
|
|
5384
|
+
startedBy: text("started_by"),
|
|
5385
|
+
startedAt: timestamp("started_at", { withTimezone: true }),
|
|
5386
|
+
endedAt: timestamp("ended_at", { withTimezone: true }),
|
|
5387
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5388
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5389
|
+
},
|
|
5390
|
+
(table) => [
|
|
5391
|
+
uniqueIndex("operation_idempotency_uidx").on(table.idempotencyKey),
|
|
5392
|
+
index("operation_resource_idx").on(table.resourceId),
|
|
5393
|
+
index("operation_server_idx").on(table.serverId),
|
|
5394
|
+
index("operation_status_created_idx").on(table.status, table.createdAt)
|
|
5395
|
+
]
|
|
5396
|
+
);
|
|
5397
|
+
pgTable(
|
|
5398
|
+
"agent_installation",
|
|
5399
|
+
{
|
|
5400
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5401
|
+
serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
|
|
5402
|
+
agentId: text("agent_id"),
|
|
5403
|
+
version: text("version").notNull(),
|
|
5404
|
+
installPath: text("install_path").notNull().default("/usr/local/bin/mg-agent"),
|
|
5405
|
+
configHash: text("config_hash").notNull(),
|
|
5406
|
+
status: serverAgentStatus("status").notNull().default("installing"),
|
|
5407
|
+
lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }),
|
|
5408
|
+
lastError: text("last_error"),
|
|
5409
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5410
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5411
|
+
},
|
|
5412
|
+
(table) => [
|
|
5413
|
+
uniqueIndex("agent_installation_server_uidx").on(table.serverId),
|
|
5414
|
+
index("agent_installation_status_idx").on(table.status)
|
|
5415
|
+
]
|
|
5416
|
+
);
|
|
5417
|
+
pgTable(
|
|
5418
|
+
"monitoring_sample",
|
|
5419
|
+
{
|
|
5420
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5421
|
+
serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
|
|
5422
|
+
resourceId: uuid("resource_id").references(() => resource.id, {
|
|
5423
|
+
onDelete: "cascade"
|
|
5424
|
+
}),
|
|
5425
|
+
metric: text("metric").notNull(),
|
|
5426
|
+
value: integer("value").notNull(),
|
|
5427
|
+
unit: text("unit").notNull(),
|
|
5428
|
+
tags: jsonb("tags").$type().notNull().default({}),
|
|
5429
|
+
sampledAt: timestamp("sampled_at", { withTimezone: true }).notNull().defaultNow()
|
|
5430
|
+
},
|
|
5431
|
+
(table) => [
|
|
5432
|
+
index("monitoring_sample_server_metric_time_idx").on(
|
|
5433
|
+
table.serverId,
|
|
5434
|
+
table.metric,
|
|
5435
|
+
table.sampledAt
|
|
5436
|
+
),
|
|
5437
|
+
// Descending on sampled_at so "latest metric per (server, metric)" lookups
|
|
5438
|
+
// (DISTINCT ON ... ORDER BY sampled_at DESC) are served by an index-only
|
|
5439
|
+
// scan instead of a full-table sort. The migration also adds
|
|
5440
|
+
// INCLUDE (value, unit) to make it covering for the overview snapshot query
|
|
5441
|
+
// (Drizzle 0.44 can't express INCLUDE here). See migration 20260529210000.
|
|
5442
|
+
index("monitoring_sample_server_metric_time_desc_idx").on(
|
|
5443
|
+
table.serverId,
|
|
5444
|
+
table.metric,
|
|
5445
|
+
table.sampledAt.desc()
|
|
5446
|
+
),
|
|
5447
|
+
index("monitoring_sample_resource_metric_time_idx").on(
|
|
5448
|
+
table.resourceId,
|
|
5449
|
+
table.metric,
|
|
5450
|
+
table.sampledAt
|
|
5451
|
+
)
|
|
5452
|
+
]
|
|
5453
|
+
);
|
|
5454
|
+
var appLogSource = pgTable(
|
|
5455
|
+
"app_log_source",
|
|
5456
|
+
{
|
|
5457
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5458
|
+
serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
|
|
5459
|
+
resourceId: uuid("resource_id").references(() => resource.id, {
|
|
5460
|
+
onDelete: "set null"
|
|
5461
|
+
}),
|
|
5462
|
+
sourceType: text("source_type").notNull(),
|
|
5463
|
+
sourceKey: text("source_key").notNull(),
|
|
5464
|
+
displayName: text("display_name").notNull(),
|
|
5465
|
+
path: text("path").notNull(),
|
|
5466
|
+
enabled: boolean("enabled").notNull().default(true),
|
|
5467
|
+
mutedUntil: timestamp("muted_until", { withTimezone: true }),
|
|
5468
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
5469
|
+
lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5470
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5471
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5472
|
+
},
|
|
5473
|
+
(table) => [
|
|
5474
|
+
uniqueIndex("app_log_source_server_type_key_uidx").on(
|
|
5475
|
+
table.serverId,
|
|
5476
|
+
table.sourceType,
|
|
5477
|
+
table.sourceKey
|
|
5478
|
+
),
|
|
5479
|
+
index("app_log_source_server_idx").on(table.serverId),
|
|
5480
|
+
index("app_log_source_resource_idx").on(table.resourceId),
|
|
5481
|
+
index("app_log_source_type_seen_idx").on(
|
|
5482
|
+
table.sourceType,
|
|
5483
|
+
table.lastSeenAt
|
|
5484
|
+
)
|
|
5485
|
+
]
|
|
5486
|
+
);
|
|
5487
|
+
pgTable(
|
|
5488
|
+
"app_error_event",
|
|
5489
|
+
{
|
|
5490
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5491
|
+
serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
|
|
5492
|
+
resourceId: uuid("resource_id").references(() => resource.id, {
|
|
5493
|
+
onDelete: "set null"
|
|
5494
|
+
}),
|
|
5495
|
+
logSourceId: uuid("log_source_id").references(() => appLogSource.id, {
|
|
5496
|
+
onDelete: "set null"
|
|
5497
|
+
}),
|
|
5498
|
+
fingerprint: text("fingerprint").notNull(),
|
|
5499
|
+
severity: text("severity").notNull().default("critical"),
|
|
5500
|
+
category: text("category").notNull(),
|
|
5501
|
+
sourceType: text("source_type").notNull(),
|
|
5502
|
+
sourcePath: text("source_path").notNull(),
|
|
5503
|
+
sampleMessage: text("sample_message").notNull(),
|
|
5504
|
+
sampleLines: jsonb("sample_lines").$type().notNull().default([]),
|
|
5505
|
+
signals: jsonb("signals").$type().notNull().default({}),
|
|
5506
|
+
occurrences: integer("occurrences").notNull().default(1),
|
|
5507
|
+
firstSeenAt: timestamp("first_seen_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5508
|
+
lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5509
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5510
|
+
},
|
|
5511
|
+
(table) => [
|
|
5512
|
+
index("app_error_event_fingerprint_created_idx").on(
|
|
5513
|
+
table.fingerprint,
|
|
5514
|
+
table.createdAt
|
|
5515
|
+
),
|
|
5516
|
+
index("app_error_event_server_created_idx").on(
|
|
5517
|
+
table.serverId,
|
|
5518
|
+
table.createdAt
|
|
5519
|
+
),
|
|
5520
|
+
index("app_error_event_resource_created_idx").on(
|
|
5521
|
+
table.resourceId,
|
|
5522
|
+
table.createdAt
|
|
5523
|
+
),
|
|
5524
|
+
index("app_error_event_severity_created_idx").on(
|
|
5525
|
+
table.severity,
|
|
5526
|
+
table.createdAt
|
|
5527
|
+
)
|
|
5528
|
+
]
|
|
5529
|
+
);
|
|
5530
|
+
pgTable(
|
|
5531
|
+
"app_error_alert",
|
|
5532
|
+
{
|
|
5533
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5534
|
+
fingerprint: text("fingerprint").notNull(),
|
|
5535
|
+
serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
|
|
5536
|
+
resourceId: uuid("resource_id").references(() => resource.id, {
|
|
5537
|
+
onDelete: "set null"
|
|
5538
|
+
}),
|
|
5539
|
+
logSourceId: uuid("log_source_id").references(() => appLogSource.id, {
|
|
5540
|
+
onDelete: "set null"
|
|
5541
|
+
}),
|
|
5542
|
+
severity: text("severity").notNull().default("critical"),
|
|
5543
|
+
status: text("status").notNull().default("open"),
|
|
5544
|
+
category: text("category").notNull(),
|
|
5545
|
+
title: text("title").notNull(),
|
|
5546
|
+
lastError: text("last_error").notNull(),
|
|
5547
|
+
sampleLines: jsonb("sample_lines").$type().notNull().default([]),
|
|
5548
|
+
signals: jsonb("signals").$type().notNull().default({}),
|
|
5549
|
+
failureCount: integer("failure_count").notNull().default(1),
|
|
5550
|
+
firstSeenAt: timestamp("first_seen_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5551
|
+
lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5552
|
+
lastAlertAt: timestamp("last_alert_at", { withTimezone: true }),
|
|
5553
|
+
resolvedAt: timestamp("resolved_at", { withTimezone: true }),
|
|
5554
|
+
mutedUntil: timestamp("muted_until", { withTimezone: true }),
|
|
5555
|
+
cursorPrompt: text("cursor_prompt"),
|
|
5556
|
+
telegramChatId: text("telegram_chat_id"),
|
|
5557
|
+
telegramMessageId: bigint("telegram_message_id", { mode: "number" }),
|
|
5558
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5559
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5560
|
+
},
|
|
5561
|
+
(table) => [
|
|
5562
|
+
uniqueIndex("app_error_alert_fingerprint_uidx").on(table.fingerprint),
|
|
5563
|
+
index("app_error_alert_status_seen_idx").on(
|
|
5564
|
+
table.status,
|
|
5565
|
+
table.lastSeenAt
|
|
5566
|
+
),
|
|
5567
|
+
index("app_error_alert_server_seen_idx").on(
|
|
5568
|
+
table.serverId,
|
|
5569
|
+
table.lastSeenAt
|
|
5570
|
+
),
|
|
5571
|
+
index("app_error_alert_resource_seen_idx").on(
|
|
5572
|
+
table.resourceId,
|
|
5573
|
+
table.lastSeenAt
|
|
5574
|
+
),
|
|
5575
|
+
index("app_error_alert_alerted_idx").on(table.lastAlertAt)
|
|
5576
|
+
]
|
|
5577
|
+
);
|
|
5578
|
+
pgTable(
|
|
5579
|
+
"alert_rule",
|
|
5580
|
+
{
|
|
5581
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5582
|
+
serverId: uuid("server_id").references(() => managedServer.id, {
|
|
5583
|
+
onDelete: "cascade"
|
|
5584
|
+
}),
|
|
5585
|
+
resourceId: uuid("resource_id").references(() => resource.id, {
|
|
5586
|
+
onDelete: "cascade"
|
|
5587
|
+
}),
|
|
5588
|
+
metric: text("metric").notNull(),
|
|
5589
|
+
expression: text("expression").notNull(),
|
|
5590
|
+
severity: alertSeverity("severity").notNull().default("warning"),
|
|
5591
|
+
cooldownSeconds: integer("cooldown_seconds").notNull().default(900),
|
|
5592
|
+
enabled: boolean("enabled").notNull().default(true),
|
|
5593
|
+
routes: jsonb("routes").$type().notNull().default({}),
|
|
5594
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5595
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5596
|
+
},
|
|
5597
|
+
(table) => [
|
|
5598
|
+
index("alert_rule_server_idx").on(table.serverId),
|
|
5599
|
+
index("alert_rule_resource_idx").on(table.resourceId),
|
|
5600
|
+
index("alert_rule_enabled_severity_idx").on(table.enabled, table.severity)
|
|
5601
|
+
]
|
|
5602
|
+
);
|
|
5603
|
+
pgTable(
|
|
5604
|
+
"deployment_project_server",
|
|
5605
|
+
{
|
|
5606
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5607
|
+
releaseProfileStageId: uuid("release_profile_stage_id").notNull(),
|
|
5608
|
+
sshServerId: uuid("ssh_server_id").notNull(),
|
|
5609
|
+
deployPath: text("deploy_path").notNull(),
|
|
5610
|
+
deployPathNormalized: text("deploy_path_normalized"),
|
|
5611
|
+
pm2PortBase: integer("pm2_port_base").notNull(),
|
|
5612
|
+
postgresHost: text("postgres_host"),
|
|
5613
|
+
postgresPort: integer("postgres_port"),
|
|
5614
|
+
buildFilters: text("build_filters"),
|
|
5615
|
+
envSymlinkDirs: jsonb("env_symlink_dirs"),
|
|
5616
|
+
litespeedVhosts: jsonb("litespeed_vhosts"),
|
|
5617
|
+
litespeedVhostsCheckedAt: timestamp("litespeed_vhosts_checked_at", {
|
|
5618
|
+
withTimezone: true
|
|
5619
|
+
}),
|
|
5620
|
+
litespeedVhostsCheckOk: boolean("litespeed_vhosts_check_ok"),
|
|
5621
|
+
ecosystemConfig: text("ecosystem_config").notNull().default("ecosystem.config.cjs"),
|
|
5622
|
+
usePipelineDeploy: boolean("use_pipeline_deploy").notNull().default(true),
|
|
5623
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5624
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5625
|
+
},
|
|
5626
|
+
(table) => [
|
|
5627
|
+
uniqueIndex("deployment_project_server_stage_server_uidx").on(
|
|
5628
|
+
table.releaseProfileStageId,
|
|
5629
|
+
table.sshServerId
|
|
5630
|
+
),
|
|
5631
|
+
uniqueIndex("deployment_project_server_server_path_uidx").on(
|
|
5632
|
+
table.sshServerId,
|
|
5633
|
+
table.deployPathNormalized
|
|
5634
|
+
),
|
|
5635
|
+
uniqueIndex("deployment_project_server_server_port_uidx").on(
|
|
5636
|
+
table.sshServerId,
|
|
5637
|
+
table.pm2PortBase
|
|
5638
|
+
)
|
|
5639
|
+
]
|
|
5640
|
+
);
|
|
5641
|
+
var dnsMigrationStatus = pgEnum("dns_migration_status", [
|
|
5642
|
+
"pending",
|
|
5643
|
+
"scanned",
|
|
5644
|
+
"imported",
|
|
5645
|
+
"verified"
|
|
5646
|
+
]);
|
|
5647
|
+
var dnsMigration = pgTable(
|
|
5648
|
+
"dns_migration",
|
|
5649
|
+
{
|
|
5650
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5651
|
+
domain: text("domain").notNull(),
|
|
5652
|
+
sourceProvider: text("source_provider").notNull().default("hostnet"),
|
|
5653
|
+
status: dnsMigrationStatus("status").notNull().default("pending"),
|
|
5654
|
+
scannedRecords: jsonb("scanned_records").$type().notNull().default([]),
|
|
5655
|
+
sourceNameservers: jsonb("source_nameservers").$type().notNull().default([]),
|
|
5656
|
+
currentNameservers: jsonb("current_nameservers").$type().notNull().default([]),
|
|
5657
|
+
importResult: jsonb("import_result").$type(),
|
|
5658
|
+
emailChecklist: jsonb("email_checklist").$type().notNull().default({}),
|
|
5659
|
+
directadminHost: text("directadmin_host"),
|
|
5660
|
+
directadminUsername: text("directadmin_username"),
|
|
5661
|
+
directadminPasswordEncrypted: text("directadmin_password_encrypted"),
|
|
5662
|
+
syncServerId: uuid("sync_server_id").references(() => managedServer.id, {
|
|
5663
|
+
onDelete: "set null"
|
|
5664
|
+
}),
|
|
5665
|
+
syncOperationId: uuid("sync_operation_id").references(() => operation.id, {
|
|
5666
|
+
onDelete: "set null"
|
|
5667
|
+
}),
|
|
5668
|
+
mailRecordsImportedAt: timestamp("mail_records_imported_at", {
|
|
5669
|
+
withTimezone: true
|
|
5670
|
+
}),
|
|
5671
|
+
notes: text("notes"),
|
|
5672
|
+
lastError: text("last_error"),
|
|
5673
|
+
lastScannedAt: timestamp("last_scanned_at", { withTimezone: true }),
|
|
5674
|
+
importedAt: timestamp("imported_at", { withTimezone: true }),
|
|
5675
|
+
verifiedAt: timestamp("verified_at", { withTimezone: true }),
|
|
5676
|
+
createdBy: text("created_by"),
|
|
5677
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5678
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5679
|
+
},
|
|
5680
|
+
(table) => [
|
|
5681
|
+
index("dns_migration_domain_idx").on(table.domain),
|
|
5682
|
+
index("dns_migration_status_created_idx").on(
|
|
5683
|
+
table.status,
|
|
5684
|
+
table.createdAt
|
|
5685
|
+
)
|
|
5686
|
+
]
|
|
5687
|
+
);
|
|
5688
|
+
var dnsMigrationMailboxStatus = pgEnum(
|
|
5689
|
+
"dns_migration_mailbox_status",
|
|
5690
|
+
["pending", "provisioned", "syncing", "synced", "delta_synced", "failed"]
|
|
5691
|
+
);
|
|
5692
|
+
pgTable(
|
|
5693
|
+
"dns_migration_mailbox",
|
|
5694
|
+
{
|
|
5695
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5696
|
+
migrationId: uuid("migration_id").notNull().references(() => dnsMigration.id, { onDelete: "cascade" }),
|
|
5697
|
+
address: text("address").notNull(),
|
|
5698
|
+
sourcePasswordEncrypted: text("source_password_encrypted"),
|
|
5699
|
+
destPasswordEncrypted: text("dest_password_encrypted"),
|
|
5700
|
+
status: dnsMigrationMailboxStatus("status").notNull().default("pending"),
|
|
5701
|
+
syncStats: jsonb("sync_stats").$type(),
|
|
5702
|
+
lastError: text("last_error"),
|
|
5703
|
+
provisionedAt: timestamp("provisioned_at", { withTimezone: true }),
|
|
5704
|
+
lastSyncedAt: timestamp("last_synced_at", { withTimezone: true }),
|
|
5705
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5706
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5707
|
+
},
|
|
5708
|
+
(table) => [
|
|
5709
|
+
uniqueIndex("dns_migration_mailbox_migration_address_uidx").on(
|
|
5710
|
+
table.migrationId,
|
|
5711
|
+
table.address
|
|
5712
|
+
),
|
|
5713
|
+
index("dns_migration_mailbox_migration_idx").on(table.migrationId)
|
|
5714
|
+
]
|
|
5715
|
+
);
|
|
5716
|
+
pgTable(
|
|
5717
|
+
"github_token",
|
|
5718
|
+
{
|
|
5719
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5720
|
+
label: text("label").notNull(),
|
|
5721
|
+
owner: text("owner"),
|
|
5722
|
+
notes: text("notes"),
|
|
5723
|
+
tokenEncrypted: text("token_encrypted").notNull(),
|
|
5724
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5725
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5726
|
+
},
|
|
5727
|
+
(table) => [
|
|
5728
|
+
index("github_token_label_idx").on(table.label),
|
|
5729
|
+
index("github_token_owner_idx").on(table.owner)
|
|
5730
|
+
]
|
|
5731
|
+
);
|
|
5732
|
+
pgTable(
|
|
5733
|
+
"github_webhook_secret",
|
|
5734
|
+
{
|
|
5735
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5736
|
+
label: text("label").notNull(),
|
|
5737
|
+
owner: text("owner"),
|
|
5738
|
+
notes: text("notes"),
|
|
5739
|
+
secretEncrypted: text("secret_encrypted").notNull(),
|
|
5740
|
+
enabled: boolean("enabled").notNull().default(true),
|
|
5741
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5742
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5743
|
+
},
|
|
5744
|
+
(table) => [
|
|
5745
|
+
index("github_webhook_secret_label_idx").on(table.label),
|
|
5746
|
+
index("github_webhook_secret_owner_idx").on(table.owner)
|
|
5747
|
+
]
|
|
5748
|
+
);
|
|
5749
|
+
function assertPipelinePayload(payload) {
|
|
5750
|
+
if (!payload.stepIds?.length) {
|
|
5751
|
+
throw new Error("Cannot trigger execute-pipeline: stepIds is empty");
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5106
5754
|
async function triggerPipeline(payload) {
|
|
5755
|
+
assertPipelinePayload(payload);
|
|
5107
5756
|
return tasks.trigger(
|
|
5108
5757
|
"execute-pipeline",
|
|
5109
5758
|
payload
|
|
@@ -5555,6 +6204,24 @@ async function createReleaseForStage(userId, params) {
|
|
|
5555
6204
|
if (!triggerAuthorName && userId) {
|
|
5556
6205
|
triggerAuthorName = await resolveUserDisplayName(userId);
|
|
5557
6206
|
}
|
|
6207
|
+
const enabledSteps = buildStepList(
|
|
6208
|
+
stage,
|
|
6209
|
+
profile,
|
|
6210
|
+
await resolveBuildStepListOptions({
|
|
6211
|
+
profile,
|
|
6212
|
+
triggerSha,
|
|
6213
|
+
githubToken: token,
|
|
6214
|
+
isManual: triggerType === "manual" || triggerType === "mcp",
|
|
6215
|
+
stageId: params.stageId,
|
|
6216
|
+
deployRetryScripts: stage.deploy_retry_scripts
|
|
6217
|
+
})
|
|
6218
|
+
);
|
|
6219
|
+
if (enabledSteps.length === 0) {
|
|
6220
|
+
throw new CreateReleaseError(
|
|
6221
|
+
"No pipeline steps configured for this stage (enable a PM2/Docker stage_app or deployment_project_server)",
|
|
6222
|
+
"PRECONDITION_FAILED"
|
|
6223
|
+
);
|
|
6224
|
+
}
|
|
5558
6225
|
const initialStatus = "running";
|
|
5559
6226
|
let release;
|
|
5560
6227
|
try {
|
|
@@ -5588,18 +6255,6 @@ async function createReleaseForStage(userId, params) {
|
|
|
5588
6255
|
"INTERNAL_SERVER_ERROR"
|
|
5589
6256
|
);
|
|
5590
6257
|
}
|
|
5591
|
-
const enabledSteps = buildStepList(
|
|
5592
|
-
stage,
|
|
5593
|
-
profile,
|
|
5594
|
-
await resolveBuildStepListOptions({
|
|
5595
|
-
profile,
|
|
5596
|
-
triggerSha,
|
|
5597
|
-
githubToken: token,
|
|
5598
|
-
isManual: triggerType === "manual" || triggerType === "mcp",
|
|
5599
|
-
stageId: params.stageId,
|
|
5600
|
-
deployRetryScripts: stage.deploy_retry_scripts
|
|
5601
|
-
})
|
|
5602
|
-
);
|
|
5603
6258
|
const stepIds = [];
|
|
5604
6259
|
try {
|
|
5605
6260
|
stepIds.push(...await insertReleaseSteps(release.id, enabledSteps));
|
|
@@ -5667,10 +6322,10 @@ function inferDeployMethod(folderName, hasNextConfig, hasDockerfile, hasPm2Ecosy
|
|
|
5667
6322
|
function toLabel(folderName) {
|
|
5668
6323
|
return folderName.charAt(0).toUpperCase() + folderName.slice(1);
|
|
5669
6324
|
}
|
|
5670
|
-
async function scanRepoForApps(repoFullName,
|
|
6325
|
+
async function scanRepoForApps(repoFullName, githubToken2, ref = "main") {
|
|
5671
6326
|
const headers = {
|
|
5672
6327
|
Accept: "application/vnd.github.v3+json",
|
|
5673
|
-
Authorization: `Bearer ${
|
|
6328
|
+
Authorization: `Bearer ${githubToken2}`,
|
|
5674
6329
|
"Content-Type": "application/json"
|
|
5675
6330
|
};
|
|
5676
6331
|
async function fetchContents(path) {
|
|
@@ -5816,10 +6471,10 @@ function toStageApps(apps) {
|
|
|
5816
6471
|
enabled: a.enabled
|
|
5817
6472
|
}));
|
|
5818
6473
|
}
|
|
5819
|
-
async function ensureGitHubBranchExists(repoFullName, branchName, fromBranch,
|
|
6474
|
+
async function ensureGitHubBranchExists(repoFullName, branchName, fromBranch, githubToken2) {
|
|
5820
6475
|
const headers = {
|
|
5821
6476
|
Accept: "application/vnd.github.v3+json",
|
|
5822
|
-
Authorization: `Bearer ${
|
|
6477
|
+
Authorization: `Bearer ${githubToken2}`,
|
|
5823
6478
|
"Content-Type": "application/json"
|
|
5824
6479
|
};
|
|
5825
6480
|
const checkRes = await fetch(
|
|
@@ -6822,7 +7477,7 @@ function getEncryptionKey() {
|
|
|
6822
7477
|
throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
|
|
6823
7478
|
return buf;
|
|
6824
7479
|
}
|
|
6825
|
-
function encrypt(
|
|
7480
|
+
function encrypt(text7) {
|
|
6826
7481
|
const key = getEncryptionKey();
|
|
6827
7482
|
const iv = randomBytes(ENC_IV_LENGTH);
|
|
6828
7483
|
const cipher = createCipheriv(
|
|
@@ -6830,7 +7485,7 @@ function encrypt(text) {
|
|
|
6830
7485
|
new Uint8Array(key),
|
|
6831
7486
|
new Uint8Array(iv)
|
|
6832
7487
|
);
|
|
6833
|
-
let encrypted = cipher.update(
|
|
7488
|
+
let encrypted = cipher.update(text7, "utf8", "hex");
|
|
6834
7489
|
encrypted += cipher.final("hex");
|
|
6835
7490
|
const authTag = cipher.getAuthTag();
|
|
6836
7491
|
return Buffer.concat([
|
|
@@ -7550,10 +8205,10 @@ async function r2GetObjectRange(bucket, key, range) {
|
|
|
7550
8205
|
const body = result.Body;
|
|
7551
8206
|
if (!body?.transformToString)
|
|
7552
8207
|
throw new Error("R2 returned no readable body");
|
|
7553
|
-
const
|
|
8208
|
+
const text7 = await body.transformToString();
|
|
7554
8209
|
const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
|
|
7555
8210
|
return `${header}
|
|
7556
|
-
${
|
|
8211
|
+
${text7}`;
|
|
7557
8212
|
} catch (e) {
|
|
7558
8213
|
throw r2WrapError(bucket, key, e);
|
|
7559
8214
|
}
|
|
@@ -7916,15 +8571,15 @@ async function sftpRead(opts, filePath, proxy, options) {
|
|
|
7916
8571
|
clearTimeout(timer);
|
|
7917
8572
|
cleanup?.();
|
|
7918
8573
|
cleanup = void 0;
|
|
7919
|
-
const
|
|
8574
|
+
const text7 = Buffer.concat(
|
|
7920
8575
|
chunks.map((ch) => new Uint8Array(ch))
|
|
7921
8576
|
).toString("utf-8");
|
|
7922
8577
|
if (!isWholeFileRequest) {
|
|
7923
8578
|
const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
|
|
7924
8579
|
resolve(`${header}
|
|
7925
|
-
${
|
|
8580
|
+
${text7}`);
|
|
7926
8581
|
} else {
|
|
7927
|
-
resolve(
|
|
8582
|
+
resolve(text7);
|
|
7928
8583
|
}
|
|
7929
8584
|
});
|
|
7930
8585
|
rs.on("error", (e) => {
|
|
@@ -8054,11 +8709,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
|
|
|
8054
8709
|
if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
|
|
8055
8710
|
return e.names;
|
|
8056
8711
|
}
|
|
8057
|
-
function truncateForLLM(
|
|
8058
|
-
const totalBytes = Buffer.byteLength(
|
|
8712
|
+
function truncateForLLM(text7, maxBytes) {
|
|
8713
|
+
const totalBytes = Buffer.byteLength(text7, "utf8");
|
|
8059
8714
|
if (totalBytes <= maxBytes)
|
|
8060
|
-
return { text, truncated: false, totalBytes, shownBytes: totalBytes };
|
|
8061
|
-
const buf = Buffer.from(
|
|
8715
|
+
return { text: text7, truncated: false, totalBytes, shownBytes: totalBytes };
|
|
8716
|
+
const buf = Buffer.from(text7, "utf8");
|
|
8062
8717
|
let cut = maxBytes;
|
|
8063
8718
|
while (cut > 0 && (buf[cut] & 192) === 128) cut--;
|
|
8064
8719
|
const head = buf.subarray(0, cut).toString("utf8");
|
|
@@ -8088,10 +8743,10 @@ function isTransientSshError(stderr, exitCode) {
|
|
|
8088
8743
|
function postprocessResult(result, meta) {
|
|
8089
8744
|
if (!result.content?.length) return result;
|
|
8090
8745
|
const block = result.content[0];
|
|
8091
|
-
let
|
|
8092
|
-
const trunc = truncateForLLM(
|
|
8746
|
+
let text7 = String(block.text ?? "");
|
|
8747
|
+
const trunc = truncateForLLM(text7, RESPONSE_MAX_BYTES);
|
|
8093
8748
|
if (trunc.truncated) {
|
|
8094
|
-
|
|
8749
|
+
text7 = trunc.text + "\n\n... " + buildTruncationHint(
|
|
8095
8750
|
meta.toolName,
|
|
8096
8751
|
meta.args,
|
|
8097
8752
|
trunc.totalBytes,
|
|
@@ -8105,11 +8760,11 @@ function postprocessResult(result, meta) {
|
|
|
8105
8760
|
const parts = [`took ${tookStr}`, sizeStr];
|
|
8106
8761
|
if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
|
|
8107
8762
|
if (meta.cached) parts.push("cached");
|
|
8108
|
-
|
|
8763
|
+
text7 = `${text7}
|
|
8109
8764
|
|
|
8110
8765
|
[${parts.join(", ")}]`;
|
|
8111
8766
|
}
|
|
8112
|
-
return { ...result, content: [{ ...block, text }] };
|
|
8767
|
+
return { ...result, content: [{ ...block, text: text7 }] };
|
|
8113
8768
|
}
|
|
8114
8769
|
function buildPipelineScript(commands, shell, marker, stopOnError) {
|
|
8115
8770
|
if (shell === "powershell") {
|
|
@@ -8895,11 +9550,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
|
|
|
8895
9550
|
applied_by TEXT
|
|
8896
9551
|
);
|
|
8897
9552
|
`.trim();
|
|
8898
|
-
function normaliseMigrationSql(
|
|
8899
|
-
return
|
|
9553
|
+
function normaliseMigrationSql(sql29) {
|
|
9554
|
+
return sql29.replace(/\r\n/g, "\n").trim() + "\n";
|
|
8900
9555
|
}
|
|
8901
|
-
function migrationSha256(
|
|
8902
|
-
return createHash("sha256").update(normaliseMigrationSql(
|
|
9556
|
+
function migrationSha256(sql29) {
|
|
9557
|
+
return createHash("sha256").update(normaliseMigrationSql(sql29), "utf8").digest("hex");
|
|
8903
9558
|
}
|
|
8904
9559
|
function dollarQuoteTag(value) {
|
|
8905
9560
|
let tag = "_mcp";
|
|
@@ -11164,8 +11819,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11164
11819
|
};
|
|
11165
11820
|
const filtered = sortRows(applyFilter(only.rows));
|
|
11166
11821
|
if (format === "json") {
|
|
11167
|
-
const
|
|
11168
|
-
return { content: [{ type: "text", text:
|
|
11822
|
+
const text8 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
|
|
11823
|
+
return { content: [{ type: "text", text: text8 }] };
|
|
11169
11824
|
}
|
|
11170
11825
|
if (groupByProject) {
|
|
11171
11826
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -11192,8 +11847,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11192
11847
|
}
|
|
11193
11848
|
const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
|
|
11194
11849
|
const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
|
|
11195
|
-
const
|
|
11196
|
-
return { content: [{ type: "text", text }] };
|
|
11850
|
+
const text7 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
|
|
11851
|
+
return { content: [{ type: "text", text: text7 }] };
|
|
11197
11852
|
}
|
|
11198
11853
|
if (format === "json") {
|
|
11199
11854
|
const lines = [];
|
|
@@ -12766,7 +13421,7 @@ ${lines.join("\n\n")}`
|
|
|
12766
13421
|
// ----- Domains (mijn.host) -----
|
|
12767
13422
|
case "domain-list": {
|
|
12768
13423
|
const res = await mijnhostFetch(
|
|
12769
|
-
"/domains"
|
|
13424
|
+
"/domains/"
|
|
12770
13425
|
);
|
|
12771
13426
|
const domains = res.data.domains;
|
|
12772
13427
|
if (!domains.length) {
|