Move from openapi-generator-cli to custom Axios approach, and add listFolders endpoint

This commit is contained in:
carlosmonastyrski
2025-05-02 19:12:08 -03:00
parent 1b8aa6d20e
commit 68613f6b13
25 changed files with 1757 additions and 2605 deletions

92
src/api/base.ts Normal file
View File

@@ -0,0 +1,92 @@
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
export interface ApiClientConfig {
baseURL: string;
headers?: Record<string, string>;
timeout?: number;
}
export class ApiClient {
private client: AxiosInstance;
constructor(config: ApiClientConfig) {
this.client = axios.create({
baseURL: config.baseURL,
headers: config.headers || {},
timeout: config.timeout || 10000,
});
this.setupRetryInterceptor();
}
private setupRetryInterceptor() {
const maxRetries = 4;
const initialRetryDelay = 1000;
const backoffFactor = 2;
this.client.interceptors.response.use(null, (error) => {
const config = error?.config;
if (!config) return Promise.reject(error);
if (!config._retryCount) config._retryCount = 0;
if (
(error.response?.status === 429 ||
error.response?.status === undefined) &&
config._retryCount < maxRetries
) {
config._retryCount++;
const baseDelay =
initialRetryDelay * Math.pow(backoffFactor, config._retryCount - 1);
const jitter = baseDelay * 0.2;
const exponentialDelay = baseDelay + (Math.random() * 2 - 1) * jitter;
return new Promise((resolve) => {
setTimeout(() => resolve(this.client(config)), exponentialDelay);
});
}
return Promise.reject(error);
});
}
public setAccessToken(token: string) {
this.client.defaults.headers.common["Authorization"] = `Bearer ${token}`;
}
public async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
const response: AxiosResponse<T> = await this.client.get(url, config);
return response.data;
}
public async post<T>(
url: string,
data?: any,
config?: AxiosRequestConfig
): Promise<T> {
const response: AxiosResponse<T> = await this.client.post(
url,
data,
config
);
return response.data;
}
public async patch<T>(
url: string,
data?: any,
config?: AxiosRequestConfig
): Promise<T> {
const response: AxiosResponse<T> = await this.client.patch(
url,
data,
config
);
return response.data;
}
public async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
const response: AxiosResponse<T> = await this.client.delete(url, config);
return response.data;
}
}

38
src/api/endpoints/auth.ts Normal file
View File

@@ -0,0 +1,38 @@
import { ApiClient } from "../base";
import {
UniversalAuthLoginRequest,
UniversalAuthLoginResponse,
AwsIamAuthLoginRequest,
AwsIamAuthLoginResponse,
TokenRenewRequest,
TokenRenewResponse,
} from "../types";
export class AuthApi {
constructor(private apiClient: ApiClient) {}
async universalAuthLogin(
data: UniversalAuthLoginRequest
): Promise<UniversalAuthLoginResponse> {
return this.apiClient.post<UniversalAuthLoginResponse>(
"/api/v1/auth/universal-auth/login",
data
);
}
async awsIamAuthLogin(
data: AwsIamAuthLoginRequest
): Promise<AwsIamAuthLoginResponse> {
return this.apiClient.post<AwsIamAuthLoginResponse>(
"/api/v1/auth/aws-auth/login",
data
);
}
async renewToken(data: TokenRenewRequest): Promise<TokenRenewResponse> {
return this.apiClient.post<TokenRenewResponse>(
"/api/v1/auth/token/renew",
data
);
}
}

View File

@@ -0,0 +1,65 @@
import { ApiClient } from "../base";
import {
CreateDynamicSecretRequest,
CreateDynamicSecretResponse,
DeleteDynamicSecretRequest,
DeleteDynamicSecretResponse,
CreateLeaseRequest,
CreateLeaseResponse,
DeleteLeaseRequest,
DeleteLeaseResponse,
RenewLeaseRequest,
RenewLeaseResponse,
} from "../types";
export class DynamicSecretsApi {
constructor(private apiClient: ApiClient) {}
async create(
data: CreateDynamicSecretRequest
): Promise<CreateDynamicSecretResponse> {
return this.apiClient.post<CreateDynamicSecretResponse>(
"/api/v1/dynamic-secrets",
data
);
}
async delete(
secretName: string,
data: DeleteDynamicSecretRequest
): Promise<DeleteDynamicSecretResponse> {
return this.apiClient.delete<DeleteDynamicSecretResponse>(
`/api/v1/dynamic-secrets/${encodeURIComponent(secretName)}`,
{ data }
);
}
leases = {
create: async (data: CreateLeaseRequest): Promise<CreateLeaseResponse> => {
return this.apiClient.post<CreateLeaseResponse>(
"/api/v1/dynamic-secrets/leases",
data
);
},
delete: async (
leaseId: string,
data: DeleteLeaseRequest
): Promise<DeleteLeaseResponse> => {
return this.apiClient.delete<DeleteLeaseResponse>(
`/api/v1/dynamic-secrets/leases/${leaseId}`,
{ data }
);
},
renew: async (
leaseId: string,
data: RenewLeaseRequest
): Promise<RenewLeaseResponse> => {
return this.apiClient.post<RenewLeaseResponse>(
`/api/v1/dynamic-secrets/leases/${leaseId}/renew`,
data
);
},
};
}

View File

@@ -0,0 +1,19 @@
import { ApiClient } from "../base";
import { CreateEnvironmentRequest, CreateEnvironmentResponse } from "../types";
export class EnvironmentsApi {
constructor(private apiClient: ApiClient) {}
async create(
data: CreateEnvironmentRequest
): Promise<CreateEnvironmentResponse> {
return this.apiClient.post<CreateEnvironmentResponse>(
`/api/v1/workspace/${data.projectId}/environments`,
{
name: data.name,
slug: data.slug,
position: data.position,
}
);
}
}

View File

@@ -0,0 +1,16 @@
import { ApiClient } from "../base";
import { CreateFolderRequest, CreateFolderResponse, ListFoldersRequest, ListFoldersResponse } from "../types";
export class FoldersApi {
constructor(private apiClient: ApiClient) {}
async create(data: CreateFolderRequest): Promise<CreateFolderResponse> {
return this.apiClient.post<CreateFolderResponse>("/api/v1/folders", data);
}
async listFolders(queryParams: ListFoldersRequest): Promise<ListFoldersResponse> {
return this.apiClient.get<ListFoldersResponse>("/api/v1/folders", {
params: queryParams
});
}
}

View File

@@ -0,0 +1,31 @@
import { ApiClient } from "../base";
import {
CreateProjectRequest,
CreateProjectResponse,
InviteMembersRequest,
InviteMembersResponse,
} from "../types";
export class ProjectsApi {
constructor(private apiClient: ApiClient) {}
async create(data: CreateProjectRequest): Promise<CreateProjectResponse> {
return this.apiClient.post<CreateProjectResponse>(
"/api/v2/workspace",
data
);
}
async inviteMembers(
data: InviteMembersRequest
): Promise<InviteMembersResponse> {
return this.apiClient.post<InviteMembersResponse>(
`/api/v2/workspace/${data.projectId}/memberships`,
{
emails: data.emails,
usernames: data.usernames,
roleSlugs: data.roleSlugs,
}
);
}
}

View File

@@ -0,0 +1,61 @@
import { ApiClient } from "../base";
import {
ListSecretsRequest,
ListSecretsResponse,
GetSecretRequest,
GetSecretResponse,
CreateSecretRequest,
CreateSecretResponse,
UpdateSecretRequest,
UpdateSecretResponse,
DeleteSecretRequest,
DeleteSecretResponse,
} from "../types";
export class SecretsApi {
constructor(private apiClient: ApiClient) {}
async listSecrets(params: ListSecretsRequest): Promise<ListSecretsResponse> {
return this.apiClient.get<ListSecretsResponse>("/api/v3/secrets/raw", {
params,
});
}
async getSecret(params: GetSecretRequest): Promise<GetSecretResponse> {
const { secretName, ...queryParams } = params;
return this.apiClient.get<GetSecretResponse>(
`/api/v3/secrets/raw/${encodeURIComponent(secretName)}`,
{ params: queryParams }
);
}
async createSecret(
secretName: string,
data: CreateSecretRequest
): Promise<CreateSecretResponse> {
return this.apiClient.post<CreateSecretResponse>(
`/api/v3/secrets/raw/${encodeURIComponent(secretName)}`,
data
);
}
async updateSecret(
secretName: string,
data: UpdateSecretRequest
): Promise<UpdateSecretResponse> {
return this.apiClient.patch<UpdateSecretResponse>(
`/api/v3/secrets/raw/${encodeURIComponent(secretName)}`,
data
);
}
async deleteSecret(
secretName: string,
data: DeleteSecretRequest
): Promise<DeleteSecretResponse> {
return this.apiClient.delete<DeleteSecretResponse>(
`/api/v3/secrets/raw/${encodeURIComponent(secretName)}`,
{ data }
);
}
}

30
src/api/types/auth.ts Normal file
View File

@@ -0,0 +1,30 @@
export interface UniversalAuthLoginRequest {
clientId: string;
clientSecret: string;
}
export interface UniversalAuthLoginResponse {
accessToken: string;
expiresIn: number;
}
export interface AwsIamAuthLoginRequest {
identityId: string;
iamHttpRequestMethod: string;
iamRequestBody: string;
iamRequestHeaders: string;
}
export interface AwsIamAuthLoginResponse {
accessToken: string;
expiresIn: number;
}
export interface TokenRenewRequest {
accessToken: string;
}
export interface TokenRenewResponse {
accessToken: string;
expiresIn: number;
}

View File

@@ -0,0 +1,89 @@
import { DynamicSecretProviders } from "../../custom/schemas";
import { TDynamicSecretProvider } from "../../custom/schemas";
export interface CreateDynamicSecretRequest {
provider: TDynamicSecretProvider;
defaultTTL: string;
maxTTL: string;
name: string;
projectSlug: string;
environmentSlug: string;
}
export interface DynamicSecret {
id: string;
name: string;
defaultTTL: string;
maxTTL: string;
provider: {
type: DynamicSecretProviders;
inputs: Record<string, any>;
};
createdAt: string;
updatedAt: string;
version: number;
type: string;
folderId: string;
status: string;
statusDetails: string;
projectGatewayId: string;
metadata: Record<string, any>;
}
export interface CreateDynamicSecretResponse {
dynamicSecret: DynamicSecret;
}
export interface DeleteDynamicSecretRequest {
environmentSlug: string;
projectSlug: string;
path?: string;
isForced?: boolean;
}
export interface DeleteDynamicSecretResponse {
dynamicSecret: DynamicSecret;
}
export interface CreateLeaseRequest {
dynamicSecretName: string;
environmentSlug: string;
projectSlug: string;
path?: string;
ttl?: string;
}
export interface Lease {
id: string;
dynamicSecretId: string;
data: Record<string, any>;
expiresAt: string;
createdAt: string;
updatedAt: string;
}
export interface CreateLeaseResponse {
lease: Lease;
}
export interface DeleteLeaseRequest {
environmentSlug: string;
projectSlug: string;
path?: string;
isForced?: boolean;
}
export interface DeleteLeaseResponse {
lease: Lease;
}
export interface RenewLeaseRequest {
environmentSlug: string;
projectSlug: string;
path?: string;
ttl?: string;
}
export interface RenewLeaseResponse {
lease: Lease;
}

View File

@@ -0,0 +1,20 @@
export interface Environment {
id: string;
name: string;
slug: string;
position: number;
projectId: string;
createdAt: string;
updatedAt: string;
}
export interface CreateEnvironmentRequest {
name: string;
projectId: string;
slug: string;
position?: number;
}
export type CreateEnvironmentResponse = {
environment: Environment;
};

35
src/api/types/folders.ts Normal file
View File

@@ -0,0 +1,35 @@
export interface Folder {
id: string;
name: string;
path: string;
workspaceId: string;
environment: string;
description?: string;
createdAt: string;
updatedAt: string;
}
export interface CreateFolderRequest {
name: string;
path: string;
workspaceId: string;
environment: string;
description?: string;
}
export interface ListFoldersRequest {
environment: string;
workspaceId: string;
path?: string;
recursive?: boolean;
lastSecretModified?: string;
}
export interface CreateFolderResponse {
folder: Folder;
}
export interface ListFoldersResponse {
folders: Folder[];
}

26
src/api/types/index.ts Normal file
View File

@@ -0,0 +1,26 @@
import { Secret } from "./secrets";
export * from "./auth";
export * from "./secrets";
export * from "./dynamic-secrets";
export * from "./environments";
export * from "./projects";
export * from "./folders";
export interface ApiResponse<T> {
statusCode: number;
message: string;
data: T;
}
export interface CreateSecretResponse {
secret: Secret;
}
export interface UpdateSecretResponse {
secret: Secret;
}
export interface DeleteSecretResponse {
secret: Secret;
}

43
src/api/types/projects.ts Normal file
View File

@@ -0,0 +1,43 @@
export interface Project {
id: string;
name: string;
slug: string;
description?: string;
type: string;
createdAt: string;
updatedAt: string;
}
export interface CreateProjectRequest {
projectName: string;
type: string;
projectDescription?: string;
slug?: string;
template?: string;
kmsKeyId?: string;
}
export interface CreateProjectResponse {
project: Project;
}
export interface InviteMembersRequest {
projectId: string;
emails?: string[];
usernames?: string[];
roleSlugs?: string[];
}
export interface Membership {
id: string;
userId: string;
projectId: string;
role: string;
status: string;
createdAt: string;
updatedAt: string;
}
export interface InviteMembersResponse {
memberships: Membership[];
}

84
src/api/types/secrets.ts Normal file
View File

@@ -0,0 +1,84 @@
export interface Secret {
id: string;
workspaceId: string;
environment: string;
secretKey: string;
secretValue: string;
secretComment?: string;
secretPath?: string;
type: "shared" | "personal";
createdAt: string;
updatedAt: string;
version: number;
tags: string[];
}
export interface ListSecretsRequest {
workspaceId: string;
environment: string;
expandSecretReferences?: string;
includeImports?: string;
recursive?: string;
secretPath?: string;
tagSlugs?: string;
viewSecretValue?: string;
}
export interface ListSecretsResponse {
secrets: Secret[];
imports?: Array<{
secretPath: string;
secrets: Secret[];
}>;
}
export interface GetSecretRequest {
secretName: string;
workspaceId: string;
environment: string;
expandSecretReferences?: string;
includeImports?: string;
secretPath?: string;
type?: "shared" | "personal";
version?: number;
viewSecretValue?: string;
}
export interface GetSecretResponse {
secret: Secret;
}
export interface CreateSecretRequest {
workspaceId: string;
environment: string;
secretValue: string;
secretComment?: string;
secretPath?: string;
secretReminderNote?: string;
secretReminderRepeatDays?: number;
skipMultilineEncoding?: boolean;
tagIds?: string[];
type?: "shared" | "personal";
}
export interface UpdateSecretRequest {
workspaceId: string;
environment: string;
secretValue?: string;
newSecretName?: string;
secretComment?: string;
secretPath?: string;
secretReminderNote?: string;
secretReminderRepeatDays?: number;
skipMultilineEncoding?: boolean;
tagIds?: string[];
type?: "shared" | "personal";
metadata?: Record<string, any>;
}
export interface DeleteSecretRequest {
workspaceId: string;
environment: string;
secretPath?: string;
type?: "shared" | "personal";
}