diff --git a/packages/cli/src/user/cmd.ts b/packages/cli/src/user/cmd.ts index f74ad86..6ee7241 100644 --- a/packages/cli/src/user/cmd.ts +++ b/packages/cli/src/user/cmd.ts @@ -1,16 +1,19 @@ import { Context, - createFetch, createInstance, + deleteSecret, getInstance, getLogsForInstance, getPortsForInstance, listInstances, listReservedNodes, + listSecrets, removeInstance, getInstanceScaling, restartInstance, - scaleInstanceReplicas + saveSecret, + scaleInstanceReplicas, + updateSecret } from '@osaas/client-core'; import { Command } from 'commander'; import { confirm, instanceOptsToPayload, makeSafeName } from './util'; @@ -434,21 +437,8 @@ export function cmdSecrets() { const globalOpts = command.optsWithGlobals(); const environment = globalOpts?.env || 'prod'; const ctx = new Context({ environment }); - const personalAccessToken = await ctx.getPersonalAccessToken(); - const secrets = await createFetch< - { serviceId: string; secretName: string }[] - >( - new URL( - '/mysecrets/' + serviceId, - `https://deploy.svc.${ctx.getEnvironment()}.osaas.io` - ), - { - headers: { - 'x-pat-jwt': `Bearer ${personalAccessToken}` - } - } - ); - secrets.forEach((secret) => { + const secretsList = await listSecrets(serviceId, ctx); + secretsList.forEach((secret) => { console.log(`${secret.secretName}: ***`); }); } catch (err) { @@ -467,19 +457,7 @@ export function cmdSecrets() { const globalOpts = command.optsWithGlobals(); const environment = globalOpts?.env || 'prod'; const ctx = new Context({ environment }); - const personalAccessToken = await ctx.getPersonalAccessToken(); - const secretUrl = new URL( - '/mysecrets/' + serviceId, - `https://deploy.svc.${ctx.getEnvironment()}.osaas.io` - ); - await createFetch(secretUrl, { - method: 'POST', - headers: { - 'x-pat-jwt': `Bearer ${personalAccessToken}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ secretName, secretData: secretValue }) - }); + await saveSecret(serviceId, secretName, secretValue, ctx); console.log(`Secret ${secretName} created successfully.`); } catch (err) { console.log((err as Error).message); @@ -497,24 +475,13 @@ export function cmdSecrets() { const globalOpts = command.optsWithGlobals(); const environment = globalOpts?.env || 'prod'; const ctx = new Context({ environment }); - const personalAccessToken = await ctx.getPersonalAccessToken(); - const secretUrl = new URL( - '/mysecrets/' + serviceId + `/${secretName}`, - `https://deploy.svc.${ctx.getEnvironment()}.osaas.io` - ); - await createFetch(secretUrl, { - method: 'PUT', - headers: { - 'x-pat-jwt': `Bearer ${personalAccessToken}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ secretData: secretValue }) - }); + await updateSecret(serviceId, secretName, secretValue, ctx); console.log(`Secret ${secretName} updated successfully.`); } catch (err) { console.log((err as Error).message); } }); + secrets .command('remove') .description('Remove a secret for a service') @@ -525,22 +492,12 @@ export function cmdSecrets() { const globalOpts = command.optsWithGlobals(); const environment = globalOpts?.env || 'prod'; const ctx = new Context({ environment }); - const personalAccessToken = await ctx.getPersonalAccessToken(); - const secretUrl = new URL( - '/mysecrets/' + serviceId + `/${secretName}`, - `https://deploy.svc.${ctx.getEnvironment()}.osaas.io` - ); - await createFetch(secretUrl, { - method: 'DELETE', - headers: { - 'x-pat-jwt': `Bearer ${personalAccessToken}`, - 'Content-Type': 'application/json' - } - }); + await deleteSecret(serviceId, secretName, ctx); console.log(`Secret ${secretName} removed successfully.`); } catch (err) { console.log((err as Error).message); } }); + return secrets; } diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index dbd66f6..874589e 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -528,3 +528,109 @@ export async function saveSecret( } }); } + +/** + * List all secrets for a service in Open Source Cloud. + * Returns metadata only — secret values are write-only by design and are never + * returned by the API. + * @memberof module:@osaas/client-core + * @param {string} serviceId - The service identifier + * @param {Context} ctx - Open Source Cloud configuration context + * @returns {Promise>} - List of secret metadata + */ +export async function listSecrets( + serviceId: string, + ctx: Context +): Promise<{ serviceId: string; secretName: string }[]> { + const secretsUrl = new URL( + `/mysecrets/${serviceId}`, + `https://deploy.svc.${ctx.getEnvironment()}.osaas.io` + ); + return await createFetch<{ serviceId: string; secretName: string }[]>( + secretsUrl, + { + method: 'GET', + headers: { + 'x-pat-jwt': `Bearer ${ctx.getPersonalAccessToken()}`, + 'Content-Type': 'application/json' + } + } + ); +} + +/** + * Look up a single secret by name for a service in Open Source Cloud. + * Returns metadata only — secret values are write-only by design and are never + * returned by the API. Returns undefined if the secret does not exist. + * + * Note: implemented client-side via listSecrets + .find() because no + * single-secret GET endpoint exists on the backend. + * @memberof module:@osaas/client-core + * @param {string} serviceId - The service identifier + * @param {string} secretName - The secret name to look up + * @param {Context} ctx - Open Source Cloud configuration context + * @returns {Promise<{serviceId: string, secretName: string} | undefined>} - Secret metadata or undefined + */ +export async function getSecret( + serviceId: string, + secretName: string, + ctx: Context +): Promise<{ serviceId: string; secretName: string } | undefined> { + const secrets = await listSecrets(serviceId, ctx); + return secrets.find((s) => s.secretName === secretName); +} + +/** + * Update (upsert) a secret for a service in Open Source Cloud via PUT. + * Use saveSecret for initial creation (POST). updateSecret uses PUT and + * will overwrite an existing secret of the same name. + * @memberof module:@osaas/client-core + * @param {string} serviceId - The service identifier + * @param {string} secretName - The secret name + * @param {string} value - The secret value + * @param {Context} ctx - Open Source Cloud configuration context + */ +export async function updateSecret( + serviceId: string, + secretName: string, + value: string, + ctx: Context +): Promise { + const secretUrl = new URL( + `/mysecrets/${serviceId}/${secretName}`, + `https://deploy.svc.${ctx.getEnvironment()}.osaas.io` + ); + await createFetch(secretUrl, { + method: 'PUT', + body: JSON.stringify({ secretData: value }), + headers: { + 'x-pat-jwt': `Bearer ${ctx.getPersonalAccessToken()}`, + 'Content-Type': 'application/json' + } + }); +} + +/** + * Delete a secret for a service in Open Source Cloud. + * @memberof module:@osaas/client-core + * @param {string} serviceId - The service identifier + * @param {string} secretName - The secret name to delete + * @param {Context} ctx - Open Source Cloud configuration context + */ +export async function deleteSecret( + serviceId: string, + secretName: string, + ctx: Context +): Promise { + const secretUrl = new URL( + `/mysecrets/${serviceId}/${secretName}`, + `https://deploy.svc.${ctx.getEnvironment()}.osaas.io` + ); + await createFetch(secretUrl, { + method: 'DELETE', + headers: { + 'x-pat-jwt': `Bearer ${ctx.getPersonalAccessToken()}`, + 'Content-Type': 'application/json' + } + }); +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 840922a..8ec99f6 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -16,6 +16,10 @@ export { isValidInstanceName, waitForInstanceReady, saveSecret, + listSecrets, + getSecret, + updateSecret, + deleteSecret, restartInstance, updateInstance, getInstanceScaling,