mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 15:51:26 +00:00
feat(service): variables editor with masked-secret preservation
PUT /services/{id}/variables replaced every row, and GET masks secrets as
******** — echoing the fetched list back would have overwritten real
secrets with asterisks. The backend now keeps the stored value when a
submitted secret still carries the mask.
Service detail gains a Variables section: add/edit/remove rows, secret
toggle, bulk .env paste (comments, export prefix, quoted values), and
inline validation for empty/invalid/duplicate keys. Parser and validator
live in variable-utils.ts with unit coverage.
Generated OpenAPI types drive the new updateServiceVariables client call.
ROADMAP: Variables UX marked done; per-environment values are already
structural — each environment owns its service instances.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
e6e76c1084
commit
1d5b99e1ba
6 files changed
+354
-13
No files matched your search
@@ -936,16 +936,12 @@ export async function deleteService(serviceId: string): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
export async function listServiceVariables(serviceId: string): Promise<ServiceVariable[]> {
|
||||
const payload = await requestJson<{ variables?: RawServiceVariable[] }>(`/services/${serviceId}/variables`);
|
||||
const rows = payload.variables ?? [];
|
||||
function normalizeServiceVariables(rows: RawServiceVariable[] | undefined): ServiceVariable[] {
|
||||
const result: ServiceVariable[] = [];
|
||||
|
||||
for (const row of rows) {
|
||||
for (const row of rows ?? []) {
|
||||
if (!row.id || !row.service_id || !row.key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push({
|
||||
id: row.id,
|
||||
serviceId: row.service_id,
|
||||
@@ -956,10 +952,27 @@ export async function listServiceVariables(serviceId: string): Promise<ServiceVa
|
||||
updatedAt: row.updated_at,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function listServiceVariables(serviceId: string): Promise<ServiceVariable[]> {
|
||||
const payload = await requestJson<{ variables?: RawServiceVariable[] }>(`/services/${serviceId}/variables`);
|
||||
return normalizeServiceVariables(payload.variables);
|
||||
}
|
||||
|
||||
export type UpdateServiceVariableInput = components['schemas']['VariableInput'];
|
||||
|
||||
export async function updateServiceVariables(
|
||||
serviceId: string,
|
||||
variables: UpdateServiceVariableInput[],
|
||||
): Promise<ServiceVariable[]> {
|
||||
const payload = await requestJson<{ variables?: RawServiceVariable[] }>(
|
||||
`/services/${serviceId}/variables`,
|
||||
{ method: 'PUT', body: JSON.stringify({ variables }) },
|
||||
);
|
||||
return normalizeServiceVariables(payload.variables);
|
||||
}
|
||||
|
||||
export async function listAuditLogs(input: ListAuditLogsInput = {}): Promise<AuditLogEntity[]> {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (input.resource) {
|
||||
|
||||
Reference in new issue
Block a user