This commit is contained in:
2026-03-13 15:25:44 +08:00
parent 4ec09661cd
commit 8ff555f9e4
11 changed files with 393 additions and 371 deletions

View File

@@ -1,14 +1,13 @@
import { KintoneRestAPIClient } from "@kintone/rest-api-client";
import type { KintoneRestAPIError } from "@kintone/rest-api-client";
import type { DomainWithPassword } from "@shared/types/domain";
import type {
AppResponse,
AppCustomizeResponse,
AppDetail,
FileContent,
KintoneApiError,
FileConfig,
} from "@shared/types/kintone";
import { KintoneRestAPIClient } from '@kintone/rest-api-client';
import type { KintoneRestAPIError } from '@kintone/rest-api-client';
import type { DomainWithPassword } from '@shared/types/domain';
import {
type AppResponse,
type AppDetail,
type FileContent,
type KintoneApiError,
AppCustomizeParameter,
} from '@shared/types/kintone';
/**
* Custom error class for Kintone API errors
@@ -18,13 +17,9 @@ export class KintoneError extends Error {
public readonly id?: string;
public readonly statusCode?: number;
constructor(
message: string,
apiError?: KintoneApiError,
statusCode?: number,
) {
constructor(message: string, apiError?: KintoneApiError, statusCode?: number) {
super(message);
this.name = "KintoneError";
this.name = 'KintoneError';
this.code = apiError?.code;
this.id = apiError?.id;
this.statusCode = statusCode;
@@ -34,7 +29,7 @@ export class KintoneError extends Error {
/**
* Kintone REST API Client
*/
export class SelfKintoneClient {
export class KintoneClient {
private client: KintoneRestAPIClient;
private domain: string;
@@ -42,8 +37,8 @@ export class SelfKintoneClient {
this.domain = domainConfig.domain;
const auth =
domainConfig.authType === "api_token"
? { apiToken: domainConfig.apiToken || "" }
domainConfig.authType === 'api_token'
? { apiToken: domainConfig.apiToken || '' }
: {
username: domainConfig.username,
password: domainConfig.password,
@@ -56,7 +51,7 @@ export class SelfKintoneClient {
}
private convertError(error: unknown): KintoneError {
if (error && typeof error === "object" && "code" in error) {
if (error && typeof error === 'object' && 'code' in error) {
const apiError = error as KintoneRestAPIError;
return new KintoneError(
apiError.message,
@@ -69,7 +64,7 @@ export class SelfKintoneClient {
return new KintoneError(error.message);
}
return new KintoneError("Unknown error occurred");
return new KintoneError('Unknown error occurred');
}
private async withErrorHandling<T>(operation: () => Promise<T>): Promise<T> {
@@ -80,31 +75,13 @@ export class SelfKintoneClient {
}
}
private buildCustomizeSection(
files?: FileConfig[],
): NonNullable<
Parameters<KintoneRestAPIClient["app"]["updateAppCustomize"]>[0]["desktop"]
>["js"] {
if (!files || files.length === 0) return undefined;
return files.map((item) => {
if (item.type === "FILE" && item.file) {
return { type: "FILE" as const, file: { fileKey: item.file.fileKey } };
}
return { type: "URL" as const, url: item.url || "" };
});
}
// ==================== App APIs ====================
/**
* Get all apps with pagination support
* Fetches all apps by making multiple requests if needed
*/
async getApps(options?: {
limit?: number;
offset?: number;
}): Promise<AppResponse[]> {
async getApps(options?: { limit?: number; offset?: number }): Promise<AppResponse[]> {
return this.withErrorHandling(async () => {
// If pagination options provided, use them directly
if (options?.limit !== undefined || options?.offset !== undefined) {
@@ -157,23 +134,19 @@ export class SelfKintoneClient {
return this.withErrorHandling(async () => {
const data = await this.client.file.downloadFile({ fileKey });
const buffer = Buffer.from(data);
const content = buffer.toString("base64");
const content = buffer.toString('base64');
return {
fileKey,
name: fileKey,
size: buffer.byteLength,
mimeType: "application/octet-stream",
mimeType: 'application/octet-stream',
content,
};
});
}
async uploadFile(
content: string | Buffer,
fileName: string,
_mimeType?: string,
): Promise<{ fileKey: string }> {
async uploadFile(content: string | Buffer, fileName: string, _mimeType?: string): Promise<{ fileKey: string }> {
return this.withErrorHandling(async () => {
const response = await this.client.file.uploadFile({
file: { name: fileName, data: content },
@@ -184,23 +157,9 @@ export class SelfKintoneClient {
// ==================== Deploy APIs ====================
async updateAppCustomize(
appId: string,
config: AppCustomizeResponse,
): Promise<void> {
async updateAppCustomize(appId: string, config: Omit<AppCustomizeParameter, 'app'>): Promise<void> {
return this.withErrorHandling(async () => {
await this.client.app.updateAppCustomize({
app: appId,
desktop: {
js: this.buildCustomizeSection(config.desktop?.js),
css: this.buildCustomizeSection(config.desktop?.css),
},
mobile: {
js: this.buildCustomizeSection(config.mobile?.js),
css: this.buildCustomizeSection(config.mobile?.css),
},
scope: config.scope || "ALL",
});
await this.client.app.updateAppCustomize({ ...config, app: appId });
});
}
@@ -210,12 +169,10 @@ export class SelfKintoneClient {
});
}
async getDeployStatus(
appId: string,
): Promise<"PROCESSING" | "SUCCESS" | "FAIL" | "CANCEL"> {
async getDeployStatus(appId: string): Promise<'PROCESSING' | 'SUCCESS' | 'FAIL' | 'CANCEL'> {
return this.withErrorHandling(async () => {
const response = await this.client.app.getDeployStatus({ apps: [appId] });
return response.apps[0]?.status || "FAIL";
return response.apps[0]?.status || 'FAIL';
});
}
@@ -229,8 +186,7 @@ export class SelfKintoneClient {
} catch (error) {
return {
success: false,
error:
error instanceof KintoneError ? error.message : "Connection failed",
error: error instanceof KintoneError ? error.message : 'Connection failed',
};
}
}
@@ -240,8 +196,6 @@ export class SelfKintoneClient {
}
}
export function createKintoneClient(
domain: DomainWithPassword,
): SelfKintoneClient {
return new SelfKintoneClient(domain);
export function createKintoneClient(domain: DomainWithPassword): KintoneClient {
return new KintoneClient(domain);
}