@harborclient/team-hub 0.2.2 → 0.2.3
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/cli.js +491 -24
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -431,7 +431,8 @@ function mapFirestoreCollection(id, data) {
|
|
|
431
431
|
createdAt: data.createdAt,
|
|
432
432
|
updatedAt: data.updatedAt ?? data.createdAt,
|
|
433
433
|
createdByUserId: data.createdByUserId ?? null,
|
|
434
|
-
updatedByUserId: data.updatedByUserId ?? null
|
|
434
|
+
updatedByUserId: data.updatedByUserId ?? null,
|
|
435
|
+
deletionLocked: data.deletionLocked ?? false
|
|
435
436
|
};
|
|
436
437
|
}
|
|
437
438
|
function mapFirestoreEnvironment(id, data) {
|
|
@@ -442,7 +443,8 @@ function mapFirestoreEnvironment(id, data) {
|
|
|
442
443
|
createdAt: data.createdAt,
|
|
443
444
|
updatedAt: data.updatedAt ?? data.createdAt,
|
|
444
445
|
createdByUserId: data.createdByUserId ?? null,
|
|
445
|
-
updatedByUserId: data.updatedByUserId ?? null
|
|
446
|
+
updatedByUserId: data.updatedByUserId ?? null,
|
|
447
|
+
deletionLocked: data.deletionLocked ?? false
|
|
446
448
|
};
|
|
447
449
|
}
|
|
448
450
|
function mapFirestoreFolder(id, data) {
|
|
@@ -988,7 +990,8 @@ var FirestoreDatabase = class _FirestoreDatabase {
|
|
|
988
990
|
createdAt: now,
|
|
989
991
|
updatedAt: now,
|
|
990
992
|
createdByUserId: actingUserId,
|
|
991
|
-
updatedByUserId: actingUserId
|
|
993
|
+
updatedByUserId: actingUserId,
|
|
994
|
+
deletionLocked: false
|
|
992
995
|
};
|
|
993
996
|
await this.requireClient().collection(COLLECTIONS_COLLECTION).doc(id).set(data);
|
|
994
997
|
await this.recordAuditEntry(actingUserId, "create", "collection", id);
|
|
@@ -1050,6 +1053,46 @@ var FirestoreDatabase = class _FirestoreDatabase {
|
|
|
1050
1053
|
];
|
|
1051
1054
|
await this.commitBatchedDeletes(refs);
|
|
1052
1055
|
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Finds a collection by stable identifier.
|
|
1058
|
+
*
|
|
1059
|
+
* @param id - Collection ID to look up.
|
|
1060
|
+
*/
|
|
1061
|
+
async findCollectionById(id) {
|
|
1062
|
+
const snapshot = await this.requireClient().collection(COLLECTIONS_COLLECTION).doc(id).get();
|
|
1063
|
+
if (!snapshot.exists) {
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
return mapFirestoreCollection(id, snapshot.data());
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Updates whether non-admin users may delete a collection.
|
|
1070
|
+
*
|
|
1071
|
+
* @param id - Collection ID to update.
|
|
1072
|
+
* @param deletionLocked - When true, user-role tokens cannot delete the collection.
|
|
1073
|
+
* @param actingUserId - Admin user performing the update.
|
|
1074
|
+
*/
|
|
1075
|
+
async setCollectionDeletionLocked(id, deletionLocked, actingUserId) {
|
|
1076
|
+
const docRef = this.requireClient().collection(COLLECTIONS_COLLECTION).doc(id);
|
|
1077
|
+
const snapshot = await docRef.get();
|
|
1078
|
+
if (!snapshot.exists) {
|
|
1079
|
+
throw new Error("Collection not found");
|
|
1080
|
+
}
|
|
1081
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
1082
|
+
await docRef.update({
|
|
1083
|
+
deletionLocked,
|
|
1084
|
+
updatedAt,
|
|
1085
|
+
updatedByUserId: actingUserId
|
|
1086
|
+
});
|
|
1087
|
+
await this.recordAuditEntry(actingUserId, "update", "collection", id);
|
|
1088
|
+
const existing = snapshot.data();
|
|
1089
|
+
return mapFirestoreCollection(id, {
|
|
1090
|
+
...existing,
|
|
1091
|
+
deletionLocked,
|
|
1092
|
+
updatedAt,
|
|
1093
|
+
updatedByUserId: actingUserId
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1053
1096
|
/**
|
|
1054
1097
|
* Lists all environments ordered by name.
|
|
1055
1098
|
*/
|
|
@@ -1075,7 +1118,8 @@ var FirestoreDatabase = class _FirestoreDatabase {
|
|
|
1075
1118
|
createdAt: now,
|
|
1076
1119
|
updatedAt: now,
|
|
1077
1120
|
createdByUserId: actingUserId,
|
|
1078
|
-
updatedByUserId: actingUserId
|
|
1121
|
+
updatedByUserId: actingUserId,
|
|
1122
|
+
deletionLocked: false
|
|
1079
1123
|
};
|
|
1080
1124
|
await this.requireClient().collection(ENVIRONMENTS_COLLECTION).doc(id).set(data);
|
|
1081
1125
|
await this.recordAuditEntry(actingUserId, "create", "environment", id);
|
|
@@ -1121,6 +1165,46 @@ var FirestoreDatabase = class _FirestoreDatabase {
|
|
|
1121
1165
|
await this.recordAuditEntry(actingUserId, "delete", "environment", id);
|
|
1122
1166
|
await this.requireClient().collection(ENVIRONMENTS_COLLECTION).doc(id).delete();
|
|
1123
1167
|
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Finds an environment by stable identifier.
|
|
1170
|
+
*
|
|
1171
|
+
* @param id - Environment ID to look up.
|
|
1172
|
+
*/
|
|
1173
|
+
async findEnvironmentById(id) {
|
|
1174
|
+
const snapshot = await this.requireClient().collection(ENVIRONMENTS_COLLECTION).doc(id).get();
|
|
1175
|
+
if (!snapshot.exists) {
|
|
1176
|
+
return null;
|
|
1177
|
+
}
|
|
1178
|
+
return mapFirestoreEnvironment(id, snapshot.data());
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Updates whether non-admin users may delete an environment.
|
|
1182
|
+
*
|
|
1183
|
+
* @param id - Environment ID to update.
|
|
1184
|
+
* @param deletionLocked - When true, user-role tokens cannot delete the environment.
|
|
1185
|
+
* @param actingUserId - Admin user performing the update.
|
|
1186
|
+
*/
|
|
1187
|
+
async setEnvironmentDeletionLocked(id, deletionLocked, actingUserId) {
|
|
1188
|
+
const docRef = this.requireClient().collection(ENVIRONMENTS_COLLECTION).doc(id);
|
|
1189
|
+
const snapshot = await docRef.get();
|
|
1190
|
+
if (!snapshot.exists) {
|
|
1191
|
+
throw new Error("Environment not found");
|
|
1192
|
+
}
|
|
1193
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
1194
|
+
await docRef.update({
|
|
1195
|
+
deletionLocked,
|
|
1196
|
+
updatedAt,
|
|
1197
|
+
updatedByUserId: actingUserId
|
|
1198
|
+
});
|
|
1199
|
+
await this.recordAuditEntry(actingUserId, "update", "environment", id);
|
|
1200
|
+
const existing = snapshot.data();
|
|
1201
|
+
return mapFirestoreEnvironment(id, {
|
|
1202
|
+
...existing,
|
|
1203
|
+
deletionLocked,
|
|
1204
|
+
updatedAt,
|
|
1205
|
+
updatedByUserId: actingUserId
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1124
1208
|
/**
|
|
1125
1209
|
* Lists all saved requests in a collection.
|
|
1126
1210
|
*
|
|
@@ -1731,7 +1815,8 @@ function mapCollectionSqlRow(row) {
|
|
|
1731
1815
|
createdAt: row.created_at,
|
|
1732
1816
|
updatedAt: row.updated_at ?? row.created_at,
|
|
1733
1817
|
createdByUserId: row.created_by_user_id ?? null,
|
|
1734
|
-
updatedByUserId: row.updated_by_user_id ?? null
|
|
1818
|
+
updatedByUserId: row.updated_by_user_id ?? null,
|
|
1819
|
+
deletionLocked: Boolean(row.deletion_locked)
|
|
1735
1820
|
};
|
|
1736
1821
|
}
|
|
1737
1822
|
function mapEnvironmentSqlRow(row) {
|
|
@@ -1742,7 +1827,8 @@ function mapEnvironmentSqlRow(row) {
|
|
|
1742
1827
|
createdAt: row.created_at,
|
|
1743
1828
|
updatedAt: row.updated_at ?? row.created_at,
|
|
1744
1829
|
createdByUserId: row.created_by_user_id ?? null,
|
|
1745
|
-
updatedByUserId: row.updated_by_user_id ?? null
|
|
1830
|
+
updatedByUserId: row.updated_by_user_id ?? null,
|
|
1831
|
+
deletionLocked: Boolean(row.deletion_locked)
|
|
1746
1832
|
};
|
|
1747
1833
|
}
|
|
1748
1834
|
function mapFolderSqlRow(row) {
|
|
@@ -1985,6 +2071,14 @@ CREATE TABLE IF NOT EXISTS llm_usage_log (
|
|
|
1985
2071
|
)
|
|
1986
2072
|
`.trim();
|
|
1987
2073
|
var MYSQL_DEFAULT_AUTH_JSON = DEFAULT_AUTH_JSON;
|
|
2074
|
+
var COLLECTIONS_DELETION_LOCKED_MIGRATION_SQL = `
|
|
2075
|
+
ALTER TABLE collections
|
|
2076
|
+
ADD COLUMN IF NOT EXISTS deletion_locked TINYINT(1) NOT NULL DEFAULT 0;
|
|
2077
|
+
`.trim();
|
|
2078
|
+
var ENVIRONMENTS_DELETION_LOCKED_MIGRATION_SQL = `
|
|
2079
|
+
ALTER TABLE environments
|
|
2080
|
+
ADD COLUMN IF NOT EXISTS deletion_locked TINYINT(1) NOT NULL DEFAULT 0;
|
|
2081
|
+
`.trim();
|
|
1988
2082
|
var MYSQL_MIGRATIONS = [
|
|
1989
2083
|
USERS_MIGRATION_SQL,
|
|
1990
2084
|
API_TOKENS_MIGRATION_SQL,
|
|
@@ -2005,7 +2099,9 @@ var MYSQL_MIGRATIONS = [
|
|
|
2005
2099
|
FOLDERS_BACKFILL_UPDATED_AT_SQL,
|
|
2006
2100
|
USERS_LLM_MIGRATION_SQL,
|
|
2007
2101
|
LLM_USAGE_MIGRATION_SQL,
|
|
2008
|
-
LLM_USAGE_LOG_MIGRATION_SQL
|
|
2102
|
+
LLM_USAGE_LOG_MIGRATION_SQL,
|
|
2103
|
+
COLLECTIONS_DELETION_LOCKED_MIGRATION_SQL,
|
|
2104
|
+
ENVIRONMENTS_DELETION_LOCKED_MIGRATION_SQL
|
|
2009
2105
|
];
|
|
2010
2106
|
|
|
2011
2107
|
// src/db/mysql/schemas.ts
|
|
@@ -2062,8 +2158,8 @@ function serializeAccessList(access) {
|
|
|
2062
2158
|
return JSON.stringify(access);
|
|
2063
2159
|
}
|
|
2064
2160
|
var USER_SELECT_COLUMNS = `id, name, role, collection_access, environment_access, llm_access, llm_models, llm_monthly_token_limit, created_at, updated_at, created_by_user_id, updated_by_user_id`;
|
|
2065
|
-
var COLLECTION_SELECT_COLUMNS = `id, name, variables, headers, auth, pre_request_script, post_request_script, created_at, updated_at, created_by_user_id, updated_by_user_id`;
|
|
2066
|
-
var ENVIRONMENT_SELECT_COLUMNS = `id, name, variables, created_at, updated_at, created_by_user_id, updated_by_user_id`;
|
|
2161
|
+
var COLLECTION_SELECT_COLUMNS = `id, name, variables, headers, auth, pre_request_script, post_request_script, created_at, updated_at, created_by_user_id, updated_by_user_id, deletion_locked`;
|
|
2162
|
+
var ENVIRONMENT_SELECT_COLUMNS = `id, name, variables, created_at, updated_at, created_by_user_id, updated_by_user_id, deletion_locked`;
|
|
2067
2163
|
var FOLDER_SELECT_COLUMNS = `id, collection_id, name, sort_order, created_at, updated_at, created_by_user_id, updated_by_user_id`;
|
|
2068
2164
|
var REQUEST_SELECT_COLUMNS = `id, collection_id, folder_id, name, method, url, headers, params, auth, body, body_type, pre_request_script, post_request_script, comment, sort_order, created_at, updated_at, created_by_user_id, updated_by_user_id`;
|
|
2069
2165
|
var API_TOKEN_SELECT_COLUMNS = `id, user_id, name, token_hash, token_prefix, created_at, last_used_at, revoked_at, created_by_user_id, updated_by_user_id`;
|
|
@@ -2655,6 +2751,50 @@ var MysqlDatabase = class _MysqlDatabase {
|
|
|
2655
2751
|
await this.recordAuditEntry(actingUserId, "delete", "collection", id);
|
|
2656
2752
|
await this.executeStatement("DELETE FROM collections WHERE id = ?", [id]);
|
|
2657
2753
|
}
|
|
2754
|
+
/**
|
|
2755
|
+
* Finds a collection by stable identifier.
|
|
2756
|
+
*
|
|
2757
|
+
* @param id - Collection ID to look up.
|
|
2758
|
+
*/
|
|
2759
|
+
async findCollectionById(id) {
|
|
2760
|
+
const rows = await this.queryRows(
|
|
2761
|
+
`${COLLECTION_SELECT} WHERE id = ?`,
|
|
2762
|
+
[id]
|
|
2763
|
+
);
|
|
2764
|
+
const row = rows[0];
|
|
2765
|
+
return row ? mapCollectionSqlRow(row) : null;
|
|
2766
|
+
}
|
|
2767
|
+
/**
|
|
2768
|
+
* Updates whether non-admin users may delete a collection.
|
|
2769
|
+
*
|
|
2770
|
+
* @param id - Collection ID to update.
|
|
2771
|
+
* @param deletionLocked - When true, user-role tokens cannot delete the collection.
|
|
2772
|
+
* @param actingUserId - Admin user performing the update.
|
|
2773
|
+
*/
|
|
2774
|
+
async setCollectionDeletionLocked(id, deletionLocked, actingUserId) {
|
|
2775
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
2776
|
+
const result = await this.executeStatement(
|
|
2777
|
+
`UPDATE collections
|
|
2778
|
+
SET deletion_locked = ?,
|
|
2779
|
+
updated_at = ?,
|
|
2780
|
+
updated_by_user_id = ?
|
|
2781
|
+
WHERE id = ?`,
|
|
2782
|
+
[deletionLocked ? 1 : 0, updatedAt, actingUserId, id]
|
|
2783
|
+
);
|
|
2784
|
+
if ((result.affectedRows ?? 0) === 0) {
|
|
2785
|
+
throw new Error("Collection not found");
|
|
2786
|
+
}
|
|
2787
|
+
await this.recordAuditEntry(actingUserId, "update", "collection", id);
|
|
2788
|
+
const rows = await this.queryRows(
|
|
2789
|
+
`${COLLECTION_SELECT} WHERE id = ?`,
|
|
2790
|
+
[id]
|
|
2791
|
+
);
|
|
2792
|
+
const row = rows[0];
|
|
2793
|
+
if (!row) {
|
|
2794
|
+
throw new Error("Collection not found");
|
|
2795
|
+
}
|
|
2796
|
+
return mapCollectionSqlRow(row);
|
|
2797
|
+
}
|
|
2658
2798
|
/**
|
|
2659
2799
|
* Lists all environments ordered by name.
|
|
2660
2800
|
*/
|
|
@@ -2738,6 +2878,50 @@ var MysqlDatabase = class _MysqlDatabase {
|
|
|
2738
2878
|
await this.recordAuditEntry(actingUserId, "delete", "environment", id);
|
|
2739
2879
|
await this.executeStatement("DELETE FROM environments WHERE id = ?", [id]);
|
|
2740
2880
|
}
|
|
2881
|
+
/**
|
|
2882
|
+
* Finds an environment by stable identifier.
|
|
2883
|
+
*
|
|
2884
|
+
* @param id - Environment ID to look up.
|
|
2885
|
+
*/
|
|
2886
|
+
async findEnvironmentById(id) {
|
|
2887
|
+
const rows = await this.queryRows(
|
|
2888
|
+
`${ENVIRONMENT_SELECT} WHERE id = ?`,
|
|
2889
|
+
[id]
|
|
2890
|
+
);
|
|
2891
|
+
const row = rows[0];
|
|
2892
|
+
return row ? mapEnvironmentSqlRow(row) : null;
|
|
2893
|
+
}
|
|
2894
|
+
/**
|
|
2895
|
+
* Updates whether non-admin users may delete an environment.
|
|
2896
|
+
*
|
|
2897
|
+
* @param id - Environment ID to update.
|
|
2898
|
+
* @param deletionLocked - When true, user-role tokens cannot delete the environment.
|
|
2899
|
+
* @param actingUserId - Admin user performing the update.
|
|
2900
|
+
*/
|
|
2901
|
+
async setEnvironmentDeletionLocked(id, deletionLocked, actingUserId) {
|
|
2902
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
2903
|
+
const result = await this.executeStatement(
|
|
2904
|
+
`UPDATE environments
|
|
2905
|
+
SET deletion_locked = ?,
|
|
2906
|
+
updated_at = ?,
|
|
2907
|
+
updated_by_user_id = ?
|
|
2908
|
+
WHERE id = ?`,
|
|
2909
|
+
[deletionLocked ? 1 : 0, updatedAt, actingUserId, id]
|
|
2910
|
+
);
|
|
2911
|
+
if ((result.affectedRows ?? 0) === 0) {
|
|
2912
|
+
throw new Error("Environment not found");
|
|
2913
|
+
}
|
|
2914
|
+
await this.recordAuditEntry(actingUserId, "update", "environment", id);
|
|
2915
|
+
const rows = await this.queryRows(
|
|
2916
|
+
`${ENVIRONMENT_SELECT} WHERE id = ?`,
|
|
2917
|
+
[id]
|
|
2918
|
+
);
|
|
2919
|
+
const row = rows[0];
|
|
2920
|
+
if (!row) {
|
|
2921
|
+
throw new Error("Environment not found");
|
|
2922
|
+
}
|
|
2923
|
+
return mapEnvironmentSqlRow(row);
|
|
2924
|
+
}
|
|
2741
2925
|
/**
|
|
2742
2926
|
* Lists all saved requests in a collection.
|
|
2743
2927
|
*
|
|
@@ -3590,6 +3774,14 @@ CREATE INDEX IF NOT EXISTS llm_usage_log_user_created_at_idx ON llm_usage_log (u
|
|
|
3590
3774
|
|
|
3591
3775
|
CREATE INDEX IF NOT EXISTS llm_usage_log_period_idx ON llm_usage_log (period);
|
|
3592
3776
|
`.trim();
|
|
3777
|
+
var COLLECTIONS_DELETION_LOCKED_MIGRATION_SQL2 = `
|
|
3778
|
+
ALTER TABLE collections
|
|
3779
|
+
ADD COLUMN IF NOT EXISTS deletion_locked BOOLEAN NOT NULL DEFAULT FALSE;
|
|
3780
|
+
`.trim();
|
|
3781
|
+
var ENVIRONMENTS_DELETION_LOCKED_MIGRATION_SQL2 = `
|
|
3782
|
+
ALTER TABLE environments
|
|
3783
|
+
ADD COLUMN IF NOT EXISTS deletion_locked BOOLEAN NOT NULL DEFAULT FALSE;
|
|
3784
|
+
`.trim();
|
|
3593
3785
|
var POSTGRES_MIGRATIONS = [
|
|
3594
3786
|
USERS_MIGRATION_SQL2,
|
|
3595
3787
|
API_TOKENS_MIGRATION_SQL2,
|
|
@@ -3610,7 +3802,9 @@ var POSTGRES_MIGRATIONS = [
|
|
|
3610
3802
|
FOLDERS_BACKFILL_UPDATED_AT_SQL2,
|
|
3611
3803
|
USERS_LLM_MIGRATION_SQL2,
|
|
3612
3804
|
LLM_USAGE_MIGRATION_SQL2,
|
|
3613
|
-
LLM_USAGE_LOG_MIGRATION_SQL2
|
|
3805
|
+
LLM_USAGE_LOG_MIGRATION_SQL2,
|
|
3806
|
+
COLLECTIONS_DELETION_LOCKED_MIGRATION_SQL2,
|
|
3807
|
+
ENVIRONMENTS_DELETION_LOCKED_MIGRATION_SQL2
|
|
3614
3808
|
];
|
|
3615
3809
|
|
|
3616
3810
|
// src/db/postgres/schemas.ts
|
|
@@ -4172,6 +4366,46 @@ var PostgresDatabase = class _PostgresDatabase {
|
|
|
4172
4366
|
await this.recordAuditEntry(actingUserId, "delete", "collection", id);
|
|
4173
4367
|
await this.query("DELETE FROM collections WHERE id = $1", [id]);
|
|
4174
4368
|
}
|
|
4369
|
+
/**
|
|
4370
|
+
* Finds a collection by stable identifier.
|
|
4371
|
+
*
|
|
4372
|
+
* @param id - Collection ID to look up.
|
|
4373
|
+
*/
|
|
4374
|
+
async findCollectionById(id) {
|
|
4375
|
+
const result = await this.query(`${COLLECTION_SELECT2} WHERE id = $1`, [id]);
|
|
4376
|
+
const row = result.rows[0];
|
|
4377
|
+
return row ? mapCollectionSqlRow(row) : null;
|
|
4378
|
+
}
|
|
4379
|
+
/**
|
|
4380
|
+
* Updates whether non-admin users may delete a collection.
|
|
4381
|
+
*
|
|
4382
|
+
* @param id - Collection ID to update.
|
|
4383
|
+
* @param deletionLocked - When true, user-role tokens cannot delete the collection.
|
|
4384
|
+
* @param actingUserId - Admin user performing the update.
|
|
4385
|
+
*/
|
|
4386
|
+
async setCollectionDeletionLocked(id, deletionLocked, actingUserId) {
|
|
4387
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
4388
|
+
const result = await this.query(
|
|
4389
|
+
`UPDATE collections
|
|
4390
|
+
SET deletion_locked = $1,
|
|
4391
|
+
updated_at = $2,
|
|
4392
|
+
updated_by_user_id = $3
|
|
4393
|
+
WHERE id = $4`,
|
|
4394
|
+
[deletionLocked, updatedAt, actingUserId, id]
|
|
4395
|
+
);
|
|
4396
|
+
if ((result.rowCount ?? 0) === 0) {
|
|
4397
|
+
throw new Error("Collection not found");
|
|
4398
|
+
}
|
|
4399
|
+
await this.recordAuditEntry(actingUserId, "update", "collection", id);
|
|
4400
|
+
const selectResult = await this.query(`${COLLECTION_SELECT2} WHERE id = $1`, [
|
|
4401
|
+
id
|
|
4402
|
+
]);
|
|
4403
|
+
const row = selectResult.rows[0];
|
|
4404
|
+
if (!row) {
|
|
4405
|
+
throw new Error("Collection not found");
|
|
4406
|
+
}
|
|
4407
|
+
return mapCollectionSqlRow(row);
|
|
4408
|
+
}
|
|
4175
4409
|
/**
|
|
4176
4410
|
* Lists all environments ordered by name.
|
|
4177
4411
|
*/
|
|
@@ -4250,6 +4484,47 @@ var PostgresDatabase = class _PostgresDatabase {
|
|
|
4250
4484
|
await this.recordAuditEntry(actingUserId, "delete", "environment", id);
|
|
4251
4485
|
await this.query("DELETE FROM environments WHERE id = $1", [id]);
|
|
4252
4486
|
}
|
|
4487
|
+
/**
|
|
4488
|
+
* Finds an environment by stable identifier.
|
|
4489
|
+
*
|
|
4490
|
+
* @param id - Environment ID to look up.
|
|
4491
|
+
*/
|
|
4492
|
+
async findEnvironmentById(id) {
|
|
4493
|
+
const result = await this.query(`${ENVIRONMENT_SELECT2} WHERE id = $1`, [id]);
|
|
4494
|
+
const row = result.rows[0];
|
|
4495
|
+
return row ? mapEnvironmentSqlRow(row) : null;
|
|
4496
|
+
}
|
|
4497
|
+
/**
|
|
4498
|
+
* Updates whether non-admin users may delete an environment.
|
|
4499
|
+
*
|
|
4500
|
+
* @param id - Environment ID to update.
|
|
4501
|
+
* @param deletionLocked - When true, user-role tokens cannot delete the environment.
|
|
4502
|
+
* @param actingUserId - Admin user performing the update.
|
|
4503
|
+
*/
|
|
4504
|
+
async setEnvironmentDeletionLocked(id, deletionLocked, actingUserId) {
|
|
4505
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
4506
|
+
const result = await this.query(
|
|
4507
|
+
`UPDATE environments
|
|
4508
|
+
SET deletion_locked = $1,
|
|
4509
|
+
updated_at = $2,
|
|
4510
|
+
updated_by_user_id = $3
|
|
4511
|
+
WHERE id = $4`,
|
|
4512
|
+
[deletionLocked, updatedAt, actingUserId, id]
|
|
4513
|
+
);
|
|
4514
|
+
if ((result.rowCount ?? 0) === 0) {
|
|
4515
|
+
throw new Error("Environment not found");
|
|
4516
|
+
}
|
|
4517
|
+
await this.recordAuditEntry(actingUserId, "update", "environment", id);
|
|
4518
|
+
const selectResult = await this.query(
|
|
4519
|
+
`${ENVIRONMENT_SELECT2} WHERE id = $1`,
|
|
4520
|
+
[id]
|
|
4521
|
+
);
|
|
4522
|
+
const row = selectResult.rows[0];
|
|
4523
|
+
if (!row) {
|
|
4524
|
+
throw new Error("Environment not found");
|
|
4525
|
+
}
|
|
4526
|
+
return mapEnvironmentSqlRow(row);
|
|
4527
|
+
}
|
|
4253
4528
|
/**
|
|
4254
4529
|
* Lists all saved requests in a collection.
|
|
4255
4530
|
*
|
|
@@ -5744,6 +6019,9 @@ function canUseDataApi(user) {
|
|
|
5744
6019
|
function canListCollections(user) {
|
|
5745
6020
|
return canUseDataApi(user) || canUseManagementApi(user);
|
|
5746
6021
|
}
|
|
6022
|
+
function canListEnvironments(user) {
|
|
6023
|
+
return canUseDataApi(user) || canUseManagementApi(user);
|
|
6024
|
+
}
|
|
5747
6025
|
function hasWildcardAccess(access) {
|
|
5748
6026
|
return access.includes("*");
|
|
5749
6027
|
}
|
|
@@ -5772,20 +6050,14 @@ function canCreateEnvironment(user) {
|
|
|
5772
6050
|
return user.role === "user" && hasWildcardAccess(user.environmentAccess);
|
|
5773
6051
|
}
|
|
5774
6052
|
function filterAccessibleCollections(user, collections) {
|
|
5775
|
-
if (user.role === "admin") {
|
|
5776
|
-
return [];
|
|
5777
|
-
}
|
|
5778
|
-
if (hasWildcardAccess(user.collectionAccess)) {
|
|
6053
|
+
if (user.role === "admin" || hasWildcardAccess(user.collectionAccess)) {
|
|
5779
6054
|
return collections;
|
|
5780
6055
|
}
|
|
5781
6056
|
const allowed = new Set(user.collectionAccess);
|
|
5782
6057
|
return collections.filter((collection) => allowed.has(collection.id));
|
|
5783
6058
|
}
|
|
5784
6059
|
function filterAccessibleEnvironments(user, environments) {
|
|
5785
|
-
if (user.role === "admin") {
|
|
5786
|
-
return [];
|
|
5787
|
-
}
|
|
5788
|
-
if (hasWildcardAccess(user.environmentAccess)) {
|
|
6060
|
+
if (user.role === "admin" || hasWildcardAccess(user.environmentAccess)) {
|
|
5789
6061
|
return environments;
|
|
5790
6062
|
}
|
|
5791
6063
|
const allowed = new Set(user.environmentAccess);
|
|
@@ -5810,6 +6082,17 @@ function isOverMonthlyLimit(totalTokens, limit) {
|
|
|
5810
6082
|
return totalTokens >= limit;
|
|
5811
6083
|
}
|
|
5812
6084
|
|
|
6085
|
+
// src/db/deletionLockedError.ts
|
|
6086
|
+
var DeletionLockedError = class extends Error {
|
|
6087
|
+
/**
|
|
6088
|
+
* @param entityType - Human-readable entity kind shown in the error message.
|
|
6089
|
+
*/
|
|
6090
|
+
constructor(entityType) {
|
|
6091
|
+
super(`Deletion is locked for this ${entityType}.`);
|
|
6092
|
+
this.name = "DeletionLockedError";
|
|
6093
|
+
}
|
|
6094
|
+
};
|
|
6095
|
+
|
|
5813
6096
|
// src/server/routes/schemas/common.ts
|
|
5814
6097
|
import { z as z5 } from "zod/v4";
|
|
5815
6098
|
var errorResponseSchema = z5.object({
|
|
@@ -5886,6 +6169,10 @@ function handleDbError(reply, error) {
|
|
|
5886
6169
|
void reply.code(400).send(errorResponseSchema.parse({ error: error.message }));
|
|
5887
6170
|
return true;
|
|
5888
6171
|
}
|
|
6172
|
+
if (error instanceof DeletionLockedError) {
|
|
6173
|
+
void reply.code(403).send(errorResponseSchema.parse({ error: error.message }));
|
|
6174
|
+
return true;
|
|
6175
|
+
}
|
|
5889
6176
|
if (isDuplicateUserNameDbError(error)) {
|
|
5890
6177
|
void reply.code(400).send(errorResponseSchema.parse({ error: DUPLICATE_USER_NAME_MESSAGE }));
|
|
5891
6178
|
return true;
|
|
@@ -5990,7 +6277,19 @@ var llmUsageSummaryResponseSchema = z7.object({
|
|
|
5990
6277
|
// src/server/routes/schemas/admin.ts
|
|
5991
6278
|
var adminResourceOptionSchema = z8.object({
|
|
5992
6279
|
id: z8.string(),
|
|
5993
|
-
name: z8.string()
|
|
6280
|
+
name: z8.string(),
|
|
6281
|
+
deletionLocked: z8.boolean()
|
|
6282
|
+
});
|
|
6283
|
+
var adminEntityConfigSchema = z8.object({
|
|
6284
|
+
id: z8.string(),
|
|
6285
|
+
name: z8.string(),
|
|
6286
|
+
deletionLocked: z8.boolean()
|
|
6287
|
+
});
|
|
6288
|
+
var updateAdminCollectionBodySchema = z8.object({
|
|
6289
|
+
deletionLocked: z8.boolean()
|
|
6290
|
+
});
|
|
6291
|
+
var updateAdminEnvironmentBodySchema = z8.object({
|
|
6292
|
+
deletionLocked: z8.boolean()
|
|
5994
6293
|
});
|
|
5995
6294
|
var listAdminCollectionsResponseSchema = z8.object({
|
|
5996
6295
|
collections: z8.array(adminResourceOptionSchema)
|
|
@@ -6107,7 +6406,8 @@ var collectionRecordSchema = z9.object({
|
|
|
6107
6406
|
createdAt: timestampSchema,
|
|
6108
6407
|
updatedAt: timestampSchema,
|
|
6109
6408
|
createdByUserId: z9.string().nullable(),
|
|
6110
|
-
updatedByUserId: z9.string().nullable()
|
|
6409
|
+
updatedByUserId: z9.string().nullable(),
|
|
6410
|
+
deletionLocked: z9.boolean()
|
|
6111
6411
|
});
|
|
6112
6412
|
var environmentRecordSchema = z9.object({
|
|
6113
6413
|
id: z9.string(),
|
|
@@ -6116,7 +6416,8 @@ var environmentRecordSchema = z9.object({
|
|
|
6116
6416
|
createdAt: timestampSchema,
|
|
6117
6417
|
updatedAt: timestampSchema,
|
|
6118
6418
|
createdByUserId: z9.string().nullable(),
|
|
6119
|
-
updatedByUserId: z9.string().nullable()
|
|
6419
|
+
updatedByUserId: z9.string().nullable(),
|
|
6420
|
+
deletionLocked: z9.boolean()
|
|
6120
6421
|
});
|
|
6121
6422
|
var folderRecordSchema = z9.object({
|
|
6122
6423
|
id: z9.string(),
|
|
@@ -6405,7 +6706,8 @@ async function registerAdminRoutes(app, options) {
|
|
|
6405
6706
|
return reply.send({
|
|
6406
6707
|
collections: collections.map((collection) => ({
|
|
6407
6708
|
id: collection.id,
|
|
6408
|
-
name: collection.name
|
|
6709
|
+
name: collection.name,
|
|
6710
|
+
deletionLocked: collection.deletionLocked
|
|
6409
6711
|
}))
|
|
6410
6712
|
});
|
|
6411
6713
|
} catch (error) {
|
|
@@ -6438,7 +6740,8 @@ async function registerAdminRoutes(app, options) {
|
|
|
6438
6740
|
return reply.send({
|
|
6439
6741
|
environments: environments.map((environment) => ({
|
|
6440
6742
|
id: environment.id,
|
|
6441
|
-
name: environment.name
|
|
6743
|
+
name: environment.name,
|
|
6744
|
+
deletionLocked: environment.deletionLocked
|
|
6442
6745
|
}))
|
|
6443
6746
|
});
|
|
6444
6747
|
} catch (error) {
|
|
@@ -6449,6 +6752,154 @@ async function registerAdminRoutes(app, options) {
|
|
|
6449
6752
|
}
|
|
6450
6753
|
}
|
|
6451
6754
|
});
|
|
6755
|
+
routes.route({
|
|
6756
|
+
method: "DELETE",
|
|
6757
|
+
url: "/admin/collections/:id",
|
|
6758
|
+
schema: {
|
|
6759
|
+
params: idParamSchema,
|
|
6760
|
+
response: {
|
|
6761
|
+
204: emptyResponseSchema,
|
|
6762
|
+
403: errorResponseSchema,
|
|
6763
|
+
404: errorResponseSchema
|
|
6764
|
+
}
|
|
6765
|
+
},
|
|
6766
|
+
/**
|
|
6767
|
+
* Deletes a collection regardless of deletion lock state.
|
|
6768
|
+
*/
|
|
6769
|
+
handler: async (request, reply) => {
|
|
6770
|
+
try {
|
|
6771
|
+
const user = requireAuthenticatedUser(request);
|
|
6772
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6773
|
+
return;
|
|
6774
|
+
}
|
|
6775
|
+
const collection = await db.findCollectionById(request.params.id);
|
|
6776
|
+
if (!collection) {
|
|
6777
|
+
void reply.code(404).send({ error: "Collection not found" });
|
|
6778
|
+
return;
|
|
6779
|
+
}
|
|
6780
|
+
await db.deleteCollection(request.params.id, user.id);
|
|
6781
|
+
return reply.code(204).send(null);
|
|
6782
|
+
} catch (error) {
|
|
6783
|
+
if (handleDbError(reply, error)) {
|
|
6784
|
+
return;
|
|
6785
|
+
}
|
|
6786
|
+
throw error;
|
|
6787
|
+
}
|
|
6788
|
+
}
|
|
6789
|
+
});
|
|
6790
|
+
routes.route({
|
|
6791
|
+
method: "PUT",
|
|
6792
|
+
url: "/admin/collections/:id",
|
|
6793
|
+
schema: {
|
|
6794
|
+
params: idParamSchema,
|
|
6795
|
+
body: updateAdminCollectionBodySchema,
|
|
6796
|
+
response: {
|
|
6797
|
+
200: adminEntityConfigSchema,
|
|
6798
|
+
403: errorResponseSchema,
|
|
6799
|
+
404: errorResponseSchema
|
|
6800
|
+
}
|
|
6801
|
+
},
|
|
6802
|
+
/**
|
|
6803
|
+
* Updates admin configuration for a collection.
|
|
6804
|
+
*/
|
|
6805
|
+
handler: async (request, reply) => {
|
|
6806
|
+
try {
|
|
6807
|
+
const user = requireAuthenticatedUser(request);
|
|
6808
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6809
|
+
return;
|
|
6810
|
+
}
|
|
6811
|
+
const collection = await db.setCollectionDeletionLocked(
|
|
6812
|
+
request.params.id,
|
|
6813
|
+
request.body.deletionLocked,
|
|
6814
|
+
user.id
|
|
6815
|
+
);
|
|
6816
|
+
return reply.send({
|
|
6817
|
+
id: collection.id,
|
|
6818
|
+
name: collection.name,
|
|
6819
|
+
deletionLocked: collection.deletionLocked
|
|
6820
|
+
});
|
|
6821
|
+
} catch (error) {
|
|
6822
|
+
if (handleDbError(reply, error)) {
|
|
6823
|
+
return;
|
|
6824
|
+
}
|
|
6825
|
+
throw error;
|
|
6826
|
+
}
|
|
6827
|
+
}
|
|
6828
|
+
});
|
|
6829
|
+
routes.route({
|
|
6830
|
+
method: "DELETE",
|
|
6831
|
+
url: "/admin/environments/:id",
|
|
6832
|
+
schema: {
|
|
6833
|
+
params: idParamSchema,
|
|
6834
|
+
response: {
|
|
6835
|
+
204: emptyResponseSchema,
|
|
6836
|
+
403: errorResponseSchema,
|
|
6837
|
+
404: errorResponseSchema
|
|
6838
|
+
}
|
|
6839
|
+
},
|
|
6840
|
+
/**
|
|
6841
|
+
* Deletes an environment regardless of deletion lock state.
|
|
6842
|
+
*/
|
|
6843
|
+
handler: async (request, reply) => {
|
|
6844
|
+
try {
|
|
6845
|
+
const user = requireAuthenticatedUser(request);
|
|
6846
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6847
|
+
return;
|
|
6848
|
+
}
|
|
6849
|
+
const environment = await db.findEnvironmentById(request.params.id);
|
|
6850
|
+
if (!environment) {
|
|
6851
|
+
void reply.code(404).send({ error: "Environment not found" });
|
|
6852
|
+
return;
|
|
6853
|
+
}
|
|
6854
|
+
await db.deleteEnvironment(request.params.id, user.id);
|
|
6855
|
+
return reply.code(204).send(null);
|
|
6856
|
+
} catch (error) {
|
|
6857
|
+
if (handleDbError(reply, error)) {
|
|
6858
|
+
return;
|
|
6859
|
+
}
|
|
6860
|
+
throw error;
|
|
6861
|
+
}
|
|
6862
|
+
}
|
|
6863
|
+
});
|
|
6864
|
+
routes.route({
|
|
6865
|
+
method: "PUT",
|
|
6866
|
+
url: "/admin/environments/:id",
|
|
6867
|
+
schema: {
|
|
6868
|
+
params: idParamSchema,
|
|
6869
|
+
body: updateAdminEnvironmentBodySchema,
|
|
6870
|
+
response: {
|
|
6871
|
+
200: adminEntityConfigSchema,
|
|
6872
|
+
403: errorResponseSchema,
|
|
6873
|
+
404: errorResponseSchema
|
|
6874
|
+
}
|
|
6875
|
+
},
|
|
6876
|
+
/**
|
|
6877
|
+
* Updates admin configuration for an environment.
|
|
6878
|
+
*/
|
|
6879
|
+
handler: async (request, reply) => {
|
|
6880
|
+
try {
|
|
6881
|
+
const user = requireAuthenticatedUser(request);
|
|
6882
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6883
|
+
return;
|
|
6884
|
+
}
|
|
6885
|
+
const environment = await db.setEnvironmentDeletionLocked(
|
|
6886
|
+
request.params.id,
|
|
6887
|
+
request.body.deletionLocked,
|
|
6888
|
+
user.id
|
|
6889
|
+
);
|
|
6890
|
+
return reply.send({
|
|
6891
|
+
id: environment.id,
|
|
6892
|
+
name: environment.name,
|
|
6893
|
+
deletionLocked: environment.deletionLocked
|
|
6894
|
+
});
|
|
6895
|
+
} catch (error) {
|
|
6896
|
+
if (handleDbError(reply, error)) {
|
|
6897
|
+
return;
|
|
6898
|
+
}
|
|
6899
|
+
throw error;
|
|
6900
|
+
}
|
|
6901
|
+
}
|
|
6902
|
+
});
|
|
6452
6903
|
routes.route({
|
|
6453
6904
|
method: "GET",
|
|
6454
6905
|
url: "/admin/llm/models",
|
|
@@ -6905,6 +7356,14 @@ async function registerCollectionRoutes(app, db) {
|
|
|
6905
7356
|
)) {
|
|
6906
7357
|
return;
|
|
6907
7358
|
}
|
|
7359
|
+
const collection = await db.findCollectionById(request.params.id);
|
|
7360
|
+
if (!collection) {
|
|
7361
|
+
void reply.code(404).send({ error: "Collection not found" });
|
|
7362
|
+
return;
|
|
7363
|
+
}
|
|
7364
|
+
if (collection.deletionLocked) {
|
|
7365
|
+
throw new DeletionLockedError("collection");
|
|
7366
|
+
}
|
|
6908
7367
|
await db.deleteCollection(request.params.id, user.id);
|
|
6909
7368
|
return reply.code(204).send(null);
|
|
6910
7369
|
} catch (error) {
|
|
@@ -6934,7 +7393,7 @@ async function registerEnvironmentRoutes(app, db) {
|
|
|
6934
7393
|
handler: async (request, reply) => {
|
|
6935
7394
|
try {
|
|
6936
7395
|
const user = requireAuthenticatedUser(request);
|
|
6937
|
-
if (denyUnlessAllowed(reply,
|
|
7396
|
+
if (denyUnlessAllowed(reply, canListEnvironments(user))) {
|
|
6938
7397
|
return;
|
|
6939
7398
|
}
|
|
6940
7399
|
const environments = await db.listEnvironments();
|
|
@@ -7041,6 +7500,14 @@ async function registerEnvironmentRoutes(app, db) {
|
|
|
7041
7500
|
)) {
|
|
7042
7501
|
return;
|
|
7043
7502
|
}
|
|
7503
|
+
const environment = await db.findEnvironmentById(request.params.id);
|
|
7504
|
+
if (!environment) {
|
|
7505
|
+
void reply.code(404).send({ error: "Environment not found" });
|
|
7506
|
+
return;
|
|
7507
|
+
}
|
|
7508
|
+
if (environment.deletionLocked) {
|
|
7509
|
+
throw new DeletionLockedError("environment");
|
|
7510
|
+
}
|
|
7044
7511
|
await db.deleteEnvironment(request.params.id, user.id);
|
|
7045
7512
|
return reply.code(204).send(null);
|
|
7046
7513
|
} catch (error) {
|