feat(plugin): developer whitelist CLI + idempotent backend
- CLI: plugin dev list/allow/revoke (userId or email) - Backend: PluginDeveloperService.addDeveloper is now idempotent (returns existing record instead of unique-constraint 500) - Backend: removeDeveloper silently no-ops on P2025 (not found) - Controller: route api/plugin-developers (avoids :pluginId collision), @DisabledPermission so admin check is the sole gate
This commit is contained in:
@@ -280,6 +280,31 @@ export class Client {
|
||||
upsertPluginConfig(baseId: string, pluginId: string, body: Record<string, unknown>) {
|
||||
return this.call<unknown>('PUT', `/api/base/${baseId}/plugin-config/${pluginId}`, body);
|
||||
}
|
||||
|
||||
// ---- plugin installs ----
|
||||
listInstalls(baseId: string) {
|
||||
return this.call<unknown[]>('GET', `/api/base/${baseId}/plugin-installs`);
|
||||
}
|
||||
createInstall(baseId: string, body: Record<string, unknown>) {
|
||||
return this.call<unknown>('POST', `/api/base/${baseId}/plugin-installs`, body);
|
||||
}
|
||||
deleteInstall(baseId: string, installId: string) {
|
||||
return this.call<unknown>('DELETE', `/api/base/${baseId}/plugin-installs/${installId}`);
|
||||
}
|
||||
|
||||
// ---- plugin developers ----
|
||||
listDevelopers() {
|
||||
return this.call<unknown[]>('GET', '/api/plugin-developers');
|
||||
}
|
||||
addDeveloper(userIdOrEmail: string) {
|
||||
// Backend reads @Body('userId') or @Body('email'). Send whichever looks right.
|
||||
const isEmail = userIdOrEmail.includes('@');
|
||||
const body = isEmail ? { email: userIdOrEmail } : { userId: userIdOrEmail };
|
||||
return this.call<unknown>('POST', '/api/plugin-developers', body);
|
||||
}
|
||||
removeDeveloper(userId: string) {
|
||||
return this.call<unknown>('DELETE', `/api/plugin-developers/${encodeURIComponent(userId)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- shared types (mirror backend workflow_node / workflow_edge shapes) ----
|
||||
|
||||
Reference in New Issue
Block a user