@harborclient/team-hub 0.2.2 → 0.2.4
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 +608 -32
- 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
|
}
|
|
@@ -5765,6 +6043,12 @@ function canAccessEnvironment(user, environmentId) {
|
|
|
5765
6043
|
}
|
|
5766
6044
|
return user.environmentAccess.includes(environmentId);
|
|
5767
6045
|
}
|
|
6046
|
+
function canDeleteCollection(user, collection) {
|
|
6047
|
+
return canUseDataApi(user) && canAccessCollection(user, collection.id) && collection.createdByUserId === user.id && !collection.deletionLocked;
|
|
6048
|
+
}
|
|
6049
|
+
function canDeleteRequest(user, request) {
|
|
6050
|
+
return canUseDataApi(user) && canAccessCollection(user, request.collectionId) && request.createdByUserId === user.id;
|
|
6051
|
+
}
|
|
5768
6052
|
function canCreateCollection(user) {
|
|
5769
6053
|
return user.role === "user" && hasWildcardAccess(user.collectionAccess);
|
|
5770
6054
|
}
|
|
@@ -5772,20 +6056,14 @@ function canCreateEnvironment(user) {
|
|
|
5772
6056
|
return user.role === "user" && hasWildcardAccess(user.environmentAccess);
|
|
5773
6057
|
}
|
|
5774
6058
|
function filterAccessibleCollections(user, collections) {
|
|
5775
|
-
if (user.role === "admin") {
|
|
5776
|
-
return [];
|
|
5777
|
-
}
|
|
5778
|
-
if (hasWildcardAccess(user.collectionAccess)) {
|
|
6059
|
+
if (user.role === "admin" || hasWildcardAccess(user.collectionAccess)) {
|
|
5779
6060
|
return collections;
|
|
5780
6061
|
}
|
|
5781
6062
|
const allowed = new Set(user.collectionAccess);
|
|
5782
6063
|
return collections.filter((collection) => allowed.has(collection.id));
|
|
5783
6064
|
}
|
|
5784
6065
|
function filterAccessibleEnvironments(user, environments) {
|
|
5785
|
-
if (user.role === "admin") {
|
|
5786
|
-
return [];
|
|
5787
|
-
}
|
|
5788
|
-
if (hasWildcardAccess(user.environmentAccess)) {
|
|
6066
|
+
if (user.role === "admin" || hasWildcardAccess(user.environmentAccess)) {
|
|
5789
6067
|
return environments;
|
|
5790
6068
|
}
|
|
5791
6069
|
const allowed = new Set(user.environmentAccess);
|
|
@@ -5810,6 +6088,17 @@ function isOverMonthlyLimit(totalTokens, limit) {
|
|
|
5810
6088
|
return totalTokens >= limit;
|
|
5811
6089
|
}
|
|
5812
6090
|
|
|
6091
|
+
// src/db/deletionLockedError.ts
|
|
6092
|
+
var DeletionLockedError = class extends Error {
|
|
6093
|
+
/**
|
|
6094
|
+
* @param entityType - Human-readable entity kind shown in the error message.
|
|
6095
|
+
*/
|
|
6096
|
+
constructor(entityType) {
|
|
6097
|
+
super(`Deletion is locked for this ${entityType}.`);
|
|
6098
|
+
this.name = "DeletionLockedError";
|
|
6099
|
+
}
|
|
6100
|
+
};
|
|
6101
|
+
|
|
5813
6102
|
// src/server/routes/schemas/common.ts
|
|
5814
6103
|
import { z as z5 } from "zod/v4";
|
|
5815
6104
|
var errorResponseSchema = z5.object({
|
|
@@ -5886,6 +6175,10 @@ function handleDbError(reply, error) {
|
|
|
5886
6175
|
void reply.code(400).send(errorResponseSchema.parse({ error: error.message }));
|
|
5887
6176
|
return true;
|
|
5888
6177
|
}
|
|
6178
|
+
if (error instanceof DeletionLockedError) {
|
|
6179
|
+
void reply.code(403).send(errorResponseSchema.parse({ error: error.message }));
|
|
6180
|
+
return true;
|
|
6181
|
+
}
|
|
5889
6182
|
if (isDuplicateUserNameDbError(error)) {
|
|
5890
6183
|
void reply.code(400).send(errorResponseSchema.parse({ error: DUPLICATE_USER_NAME_MESSAGE }));
|
|
5891
6184
|
return true;
|
|
@@ -5990,7 +6283,19 @@ var llmUsageSummaryResponseSchema = z7.object({
|
|
|
5990
6283
|
// src/server/routes/schemas/admin.ts
|
|
5991
6284
|
var adminResourceOptionSchema = z8.object({
|
|
5992
6285
|
id: z8.string(),
|
|
5993
|
-
name: z8.string()
|
|
6286
|
+
name: z8.string(),
|
|
6287
|
+
deletionLocked: z8.boolean()
|
|
6288
|
+
});
|
|
6289
|
+
var adminEntityConfigSchema = z8.object({
|
|
6290
|
+
id: z8.string(),
|
|
6291
|
+
name: z8.string(),
|
|
6292
|
+
deletionLocked: z8.boolean()
|
|
6293
|
+
});
|
|
6294
|
+
var updateAdminCollectionBodySchema = z8.object({
|
|
6295
|
+
deletionLocked: z8.boolean()
|
|
6296
|
+
});
|
|
6297
|
+
var updateAdminEnvironmentBodySchema = z8.object({
|
|
6298
|
+
deletionLocked: z8.boolean()
|
|
5994
6299
|
});
|
|
5995
6300
|
var listAdminCollectionsResponseSchema = z8.object({
|
|
5996
6301
|
collections: z8.array(adminResourceOptionSchema)
|
|
@@ -6107,7 +6412,8 @@ var collectionRecordSchema = z9.object({
|
|
|
6107
6412
|
createdAt: timestampSchema,
|
|
6108
6413
|
updatedAt: timestampSchema,
|
|
6109
6414
|
createdByUserId: z9.string().nullable(),
|
|
6110
|
-
updatedByUserId: z9.string().nullable()
|
|
6415
|
+
updatedByUserId: z9.string().nullable(),
|
|
6416
|
+
deletionLocked: z9.boolean()
|
|
6111
6417
|
});
|
|
6112
6418
|
var environmentRecordSchema = z9.object({
|
|
6113
6419
|
id: z9.string(),
|
|
@@ -6116,7 +6422,8 @@ var environmentRecordSchema = z9.object({
|
|
|
6116
6422
|
createdAt: timestampSchema,
|
|
6117
6423
|
updatedAt: timestampSchema,
|
|
6118
6424
|
createdByUserId: z9.string().nullable(),
|
|
6119
|
-
updatedByUserId: z9.string().nullable()
|
|
6425
|
+
updatedByUserId: z9.string().nullable(),
|
|
6426
|
+
deletionLocked: z9.boolean()
|
|
6120
6427
|
});
|
|
6121
6428
|
var folderRecordSchema = z9.object({
|
|
6122
6429
|
id: z9.string(),
|
|
@@ -6405,7 +6712,8 @@ async function registerAdminRoutes(app, options) {
|
|
|
6405
6712
|
return reply.send({
|
|
6406
6713
|
collections: collections.map((collection) => ({
|
|
6407
6714
|
id: collection.id,
|
|
6408
|
-
name: collection.name
|
|
6715
|
+
name: collection.name,
|
|
6716
|
+
deletionLocked: collection.deletionLocked
|
|
6409
6717
|
}))
|
|
6410
6718
|
});
|
|
6411
6719
|
} catch (error) {
|
|
@@ -6416,6 +6724,80 @@ async function registerAdminRoutes(app, options) {
|
|
|
6416
6724
|
}
|
|
6417
6725
|
}
|
|
6418
6726
|
});
|
|
6727
|
+
routes.route({
|
|
6728
|
+
method: "GET",
|
|
6729
|
+
url: "/admin/collections/:collectionId/folders",
|
|
6730
|
+
schema: {
|
|
6731
|
+
params: collectionIdParamSchema,
|
|
6732
|
+
response: {
|
|
6733
|
+
200: listFoldersResponseSchema,
|
|
6734
|
+
403: errorResponseSchema,
|
|
6735
|
+
404: errorResponseSchema
|
|
6736
|
+
}
|
|
6737
|
+
},
|
|
6738
|
+
/**
|
|
6739
|
+
* Lists folders in a collection for operator inspection.
|
|
6740
|
+
*/
|
|
6741
|
+
handler: async (request, reply) => {
|
|
6742
|
+
try {
|
|
6743
|
+
const user = requireAuthenticatedUser(request);
|
|
6744
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6745
|
+
return;
|
|
6746
|
+
}
|
|
6747
|
+
const collection = await db.findCollectionById(request.params.collectionId);
|
|
6748
|
+
if (!collection) {
|
|
6749
|
+
void reply.code(404).send({ error: "Collection not found" });
|
|
6750
|
+
return;
|
|
6751
|
+
}
|
|
6752
|
+
const folders = await db.listFolders(request.params.collectionId);
|
|
6753
|
+
return reply.send({
|
|
6754
|
+
folders: folders.map((folder) => serializeFolder(folder))
|
|
6755
|
+
});
|
|
6756
|
+
} catch (error) {
|
|
6757
|
+
if (handleDbError(reply, error)) {
|
|
6758
|
+
return;
|
|
6759
|
+
}
|
|
6760
|
+
throw error;
|
|
6761
|
+
}
|
|
6762
|
+
}
|
|
6763
|
+
});
|
|
6764
|
+
routes.route({
|
|
6765
|
+
method: "GET",
|
|
6766
|
+
url: "/admin/collections/:collectionId/requests",
|
|
6767
|
+
schema: {
|
|
6768
|
+
params: collectionIdParamSchema,
|
|
6769
|
+
response: {
|
|
6770
|
+
200: listRequestsResponseSchema,
|
|
6771
|
+
403: errorResponseSchema,
|
|
6772
|
+
404: errorResponseSchema
|
|
6773
|
+
}
|
|
6774
|
+
},
|
|
6775
|
+
/**
|
|
6776
|
+
* Lists saved requests in a collection for operator inspection.
|
|
6777
|
+
*/
|
|
6778
|
+
handler: async (request, reply) => {
|
|
6779
|
+
try {
|
|
6780
|
+
const user = requireAuthenticatedUser(request);
|
|
6781
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6782
|
+
return;
|
|
6783
|
+
}
|
|
6784
|
+
const collection = await db.findCollectionById(request.params.collectionId);
|
|
6785
|
+
if (!collection) {
|
|
6786
|
+
void reply.code(404).send({ error: "Collection not found" });
|
|
6787
|
+
return;
|
|
6788
|
+
}
|
|
6789
|
+
const requests = await db.listRequests(request.params.collectionId);
|
|
6790
|
+
return reply.send({
|
|
6791
|
+
requests: requests.map((savedRequest) => serializeSavedRequest(savedRequest))
|
|
6792
|
+
});
|
|
6793
|
+
} catch (error) {
|
|
6794
|
+
if (handleDbError(reply, error)) {
|
|
6795
|
+
return;
|
|
6796
|
+
}
|
|
6797
|
+
throw error;
|
|
6798
|
+
}
|
|
6799
|
+
}
|
|
6800
|
+
});
|
|
6419
6801
|
routes.route({
|
|
6420
6802
|
method: "GET",
|
|
6421
6803
|
url: "/admin/environments",
|
|
@@ -6438,7 +6820,8 @@ async function registerAdminRoutes(app, options) {
|
|
|
6438
6820
|
return reply.send({
|
|
6439
6821
|
environments: environments.map((environment) => ({
|
|
6440
6822
|
id: environment.id,
|
|
6441
|
-
name: environment.name
|
|
6823
|
+
name: environment.name,
|
|
6824
|
+
deletionLocked: environment.deletionLocked
|
|
6442
6825
|
}))
|
|
6443
6826
|
});
|
|
6444
6827
|
} catch (error) {
|
|
@@ -6449,6 +6832,189 @@ async function registerAdminRoutes(app, options) {
|
|
|
6449
6832
|
}
|
|
6450
6833
|
}
|
|
6451
6834
|
});
|
|
6835
|
+
routes.route({
|
|
6836
|
+
method: "DELETE",
|
|
6837
|
+
url: "/admin/collections/:id",
|
|
6838
|
+
schema: {
|
|
6839
|
+
params: idParamSchema,
|
|
6840
|
+
response: {
|
|
6841
|
+
204: emptyResponseSchema,
|
|
6842
|
+
403: errorResponseSchema,
|
|
6843
|
+
404: errorResponseSchema
|
|
6844
|
+
}
|
|
6845
|
+
},
|
|
6846
|
+
/**
|
|
6847
|
+
* Deletes a collection regardless of deletion lock state.
|
|
6848
|
+
*/
|
|
6849
|
+
handler: async (request, reply) => {
|
|
6850
|
+
try {
|
|
6851
|
+
const user = requireAuthenticatedUser(request);
|
|
6852
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6853
|
+
return;
|
|
6854
|
+
}
|
|
6855
|
+
const collection = await db.findCollectionById(request.params.id);
|
|
6856
|
+
if (!collection) {
|
|
6857
|
+
void reply.code(404).send({ error: "Collection not found" });
|
|
6858
|
+
return;
|
|
6859
|
+
}
|
|
6860
|
+
await db.deleteCollection(request.params.id, user.id);
|
|
6861
|
+
return reply.code(204).send(null);
|
|
6862
|
+
} catch (error) {
|
|
6863
|
+
if (handleDbError(reply, error)) {
|
|
6864
|
+
return;
|
|
6865
|
+
}
|
|
6866
|
+
throw error;
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
});
|
|
6870
|
+
routes.route({
|
|
6871
|
+
method: "DELETE",
|
|
6872
|
+
url: "/admin/requests/:id",
|
|
6873
|
+
schema: {
|
|
6874
|
+
params: idParamSchema,
|
|
6875
|
+
response: {
|
|
6876
|
+
204: emptyResponseSchema,
|
|
6877
|
+
403: errorResponseSchema,
|
|
6878
|
+
404: errorResponseSchema
|
|
6879
|
+
}
|
|
6880
|
+
},
|
|
6881
|
+
/**
|
|
6882
|
+
* Deletes a saved request regardless of collection access lists.
|
|
6883
|
+
*/
|
|
6884
|
+
handler: async (request, reply) => {
|
|
6885
|
+
try {
|
|
6886
|
+
const user = requireAuthenticatedUser(request);
|
|
6887
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6888
|
+
return;
|
|
6889
|
+
}
|
|
6890
|
+
const existingRequest = await db.findRequestById(request.params.id);
|
|
6891
|
+
if (!existingRequest) {
|
|
6892
|
+
void reply.code(404).send({ error: "Request not found" });
|
|
6893
|
+
return;
|
|
6894
|
+
}
|
|
6895
|
+
await db.deleteRequest(request.params.id, user.id);
|
|
6896
|
+
return reply.code(204).send(null);
|
|
6897
|
+
} catch (error) {
|
|
6898
|
+
if (handleDbError(reply, error)) {
|
|
6899
|
+
return;
|
|
6900
|
+
}
|
|
6901
|
+
throw error;
|
|
6902
|
+
}
|
|
6903
|
+
}
|
|
6904
|
+
});
|
|
6905
|
+
routes.route({
|
|
6906
|
+
method: "PUT",
|
|
6907
|
+
url: "/admin/collections/:id",
|
|
6908
|
+
schema: {
|
|
6909
|
+
params: idParamSchema,
|
|
6910
|
+
body: updateAdminCollectionBodySchema,
|
|
6911
|
+
response: {
|
|
6912
|
+
200: adminEntityConfigSchema,
|
|
6913
|
+
403: errorResponseSchema,
|
|
6914
|
+
404: errorResponseSchema
|
|
6915
|
+
}
|
|
6916
|
+
},
|
|
6917
|
+
/**
|
|
6918
|
+
* Updates admin configuration for a collection.
|
|
6919
|
+
*/
|
|
6920
|
+
handler: async (request, reply) => {
|
|
6921
|
+
try {
|
|
6922
|
+
const user = requireAuthenticatedUser(request);
|
|
6923
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6924
|
+
return;
|
|
6925
|
+
}
|
|
6926
|
+
const collection = await db.setCollectionDeletionLocked(
|
|
6927
|
+
request.params.id,
|
|
6928
|
+
request.body.deletionLocked,
|
|
6929
|
+
user.id
|
|
6930
|
+
);
|
|
6931
|
+
return reply.send({
|
|
6932
|
+
id: collection.id,
|
|
6933
|
+
name: collection.name,
|
|
6934
|
+
deletionLocked: collection.deletionLocked
|
|
6935
|
+
});
|
|
6936
|
+
} catch (error) {
|
|
6937
|
+
if (handleDbError(reply, error)) {
|
|
6938
|
+
return;
|
|
6939
|
+
}
|
|
6940
|
+
throw error;
|
|
6941
|
+
}
|
|
6942
|
+
}
|
|
6943
|
+
});
|
|
6944
|
+
routes.route({
|
|
6945
|
+
method: "DELETE",
|
|
6946
|
+
url: "/admin/environments/:id",
|
|
6947
|
+
schema: {
|
|
6948
|
+
params: idParamSchema,
|
|
6949
|
+
response: {
|
|
6950
|
+
204: emptyResponseSchema,
|
|
6951
|
+
403: errorResponseSchema,
|
|
6952
|
+
404: errorResponseSchema
|
|
6953
|
+
}
|
|
6954
|
+
},
|
|
6955
|
+
/**
|
|
6956
|
+
* Deletes an environment regardless of deletion lock state.
|
|
6957
|
+
*/
|
|
6958
|
+
handler: async (request, reply) => {
|
|
6959
|
+
try {
|
|
6960
|
+
const user = requireAuthenticatedUser(request);
|
|
6961
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6962
|
+
return;
|
|
6963
|
+
}
|
|
6964
|
+
const environment = await db.findEnvironmentById(request.params.id);
|
|
6965
|
+
if (!environment) {
|
|
6966
|
+
void reply.code(404).send({ error: "Environment not found" });
|
|
6967
|
+
return;
|
|
6968
|
+
}
|
|
6969
|
+
await db.deleteEnvironment(request.params.id, user.id);
|
|
6970
|
+
return reply.code(204).send(null);
|
|
6971
|
+
} catch (error) {
|
|
6972
|
+
if (handleDbError(reply, error)) {
|
|
6973
|
+
return;
|
|
6974
|
+
}
|
|
6975
|
+
throw error;
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
});
|
|
6979
|
+
routes.route({
|
|
6980
|
+
method: "PUT",
|
|
6981
|
+
url: "/admin/environments/:id",
|
|
6982
|
+
schema: {
|
|
6983
|
+
params: idParamSchema,
|
|
6984
|
+
body: updateAdminEnvironmentBodySchema,
|
|
6985
|
+
response: {
|
|
6986
|
+
200: adminEntityConfigSchema,
|
|
6987
|
+
403: errorResponseSchema,
|
|
6988
|
+
404: errorResponseSchema
|
|
6989
|
+
}
|
|
6990
|
+
},
|
|
6991
|
+
/**
|
|
6992
|
+
* Updates admin configuration for an environment.
|
|
6993
|
+
*/
|
|
6994
|
+
handler: async (request, reply) => {
|
|
6995
|
+
try {
|
|
6996
|
+
const user = requireAuthenticatedUser(request);
|
|
6997
|
+
if (denyUnlessAllowed(reply, canUseManagementApi(user))) {
|
|
6998
|
+
return;
|
|
6999
|
+
}
|
|
7000
|
+
const environment = await db.setEnvironmentDeletionLocked(
|
|
7001
|
+
request.params.id,
|
|
7002
|
+
request.body.deletionLocked,
|
|
7003
|
+
user.id
|
|
7004
|
+
);
|
|
7005
|
+
return reply.send({
|
|
7006
|
+
id: environment.id,
|
|
7007
|
+
name: environment.name,
|
|
7008
|
+
deletionLocked: environment.deletionLocked
|
|
7009
|
+
});
|
|
7010
|
+
} catch (error) {
|
|
7011
|
+
if (handleDbError(reply, error)) {
|
|
7012
|
+
return;
|
|
7013
|
+
}
|
|
7014
|
+
throw error;
|
|
7015
|
+
}
|
|
7016
|
+
}
|
|
7017
|
+
});
|
|
6452
7018
|
routes.route({
|
|
6453
7019
|
method: "GET",
|
|
6454
7020
|
url: "/admin/llm/models",
|
|
@@ -6899,10 +7465,15 @@ async function registerCollectionRoutes(app, db) {
|
|
|
6899
7465
|
handler: async (request, reply) => {
|
|
6900
7466
|
try {
|
|
6901
7467
|
const user = requireAuthenticatedUser(request);
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
7468
|
+
const collection = await db.findCollectionById(request.params.id);
|
|
7469
|
+
if (!collection) {
|
|
7470
|
+
void reply.code(404).send({ error: "Collection not found" });
|
|
7471
|
+
return;
|
|
7472
|
+
}
|
|
7473
|
+
if (collection.deletionLocked) {
|
|
7474
|
+
throw new DeletionLockedError("collection");
|
|
7475
|
+
}
|
|
7476
|
+
if (denyUnlessAllowed(reply, canDeleteCollection(user, collection))) {
|
|
6906
7477
|
return;
|
|
6907
7478
|
}
|
|
6908
7479
|
await db.deleteCollection(request.params.id, user.id);
|
|
@@ -6934,7 +7505,7 @@ async function registerEnvironmentRoutes(app, db) {
|
|
|
6934
7505
|
handler: async (request, reply) => {
|
|
6935
7506
|
try {
|
|
6936
7507
|
const user = requireAuthenticatedUser(request);
|
|
6937
|
-
if (denyUnlessAllowed(reply,
|
|
7508
|
+
if (denyUnlessAllowed(reply, canListEnvironments(user))) {
|
|
6938
7509
|
return;
|
|
6939
7510
|
}
|
|
6940
7511
|
const environments = await db.listEnvironments();
|
|
@@ -7041,6 +7612,14 @@ async function registerEnvironmentRoutes(app, db) {
|
|
|
7041
7612
|
)) {
|
|
7042
7613
|
return;
|
|
7043
7614
|
}
|
|
7615
|
+
const environment = await db.findEnvironmentById(request.params.id);
|
|
7616
|
+
if (!environment) {
|
|
7617
|
+
void reply.code(404).send({ error: "Environment not found" });
|
|
7618
|
+
return;
|
|
7619
|
+
}
|
|
7620
|
+
if (environment.deletionLocked) {
|
|
7621
|
+
throw new DeletionLockedError("environment");
|
|
7622
|
+
}
|
|
7044
7623
|
await db.deleteEnvironment(request.params.id, user.id);
|
|
7045
7624
|
return reply.code(204).send(null);
|
|
7046
7625
|
} catch (error) {
|
|
@@ -7425,10 +8004,7 @@ async function registerRequestRoutes(app, db) {
|
|
|
7425
8004
|
if (!existingRequest) {
|
|
7426
8005
|
return reply.code(404).send({ error: "Request not found" });
|
|
7427
8006
|
}
|
|
7428
|
-
if (denyUnlessAllowed(
|
|
7429
|
-
reply,
|
|
7430
|
-
canUseDataApi(user) && canAccessCollection(user, existingRequest.collectionId)
|
|
7431
|
-
)) {
|
|
8007
|
+
if (denyUnlessAllowed(reply, canDeleteRequest(user, existingRequest))) {
|
|
7432
8008
|
return;
|
|
7433
8009
|
}
|
|
7434
8010
|
await db.deleteRequest(request.params.id, user.id);
|