update main ts

This commit is contained in:
2026-03-12 23:03:01 +08:00
parent 4a8cd53617
commit f53f43a6b9
4 changed files with 48 additions and 111 deletions

View File

@@ -38,6 +38,7 @@ import type {
DownloadMetadata, DownloadMetadata,
BackupMetadata, BackupMetadata,
} from "@shared/types/version"; } from "@shared/types/version";
import type { AppCustomizeResponse } from "@shared/types/kintone";
// Cache for Kintone clients // Cache for Kintone clients
const clientCache = new Map<string, SelfKintoneClient>(); const clientCache = new Map<string, SelfKintoneClient>();
@@ -317,7 +318,7 @@ function registerDeploy(): void {
const backupFileList: BackupMetadata["files"] = []; const backupFileList: BackupMetadata["files"] = [];
// Add JS files to backup // Add JS files to backup
for (const js of appDetail.customization?.javascript?.pc || []) { for (const js of appDetail.customization?.desktop?.js || []) {
if (js.file?.fileKey) { if (js.file?.fileKey) {
const fileContent = await client.getFileContent(js.file.fileKey); const fileContent = await client.getFileContent(js.file.fileKey);
const content = Buffer.from(fileContent.content || "", "base64"); const content = Buffer.from(fileContent.content || "", "base64");
@@ -334,7 +335,7 @@ function registerDeploy(): void {
} }
// Add CSS files to backup // Add CSS files to backup
for (const css of appDetail.customization?.stylesheet?.pc || []) { for (const css of appDetail.customization?.desktop?.css || []) {
if (css.file?.fileKey) { if (css.file?.fileKey) {
const fileContent = await client.getFileContent(css.file.fileKey); const fileContent = await client.getFileContent(css.file.fileKey);
const content = Buffer.from(fileContent.content || "", "base64"); const content = Buffer.from(fileContent.content || "", "base64");
@@ -349,7 +350,6 @@ function registerDeploy(): void {
}); });
} }
} }
// Save backup // Save backup
const backupMetadata: BackupMetadata = { const backupMetadata: BackupMetadata = {
backedUpAt: new Date().toISOString(), backedUpAt: new Date().toISOString(),
@@ -382,39 +382,40 @@ function registerDeploy(): void {
// Build new customization config // Build new customization config
// Note: This is simplified - real implementation would merge with existing config // Note: This is simplified - real implementation would merge with existing config
const newConfig = { const newConfig: AppCustomizeResponse = {
javascript: { desktop: {
pc: uploadedFiles js: uploadedFiles
.filter((f) => f.type === "js" && f.position.startsWith("pc_")) .filter((f) => f.type === "js" && f.position.startsWith("pc_"))
.map((f) => ({ .map((f) => ({
type: "FILE" as const, type: "FILE" as const,
file: { fileKey: f.fileKey }, file: { fileKey: f.fileKey },
})), })),
mobile: uploadedFiles css: uploadedFiles
.filter((f) => f.type === "js" && f.position.startsWith("mobile_"))
.map((f) => ({
type: "FILE" as const,
file: { fileKey: f.fileKey },
})),
},
stylesheet: {
pc: uploadedFiles
.filter((f) => f.type === "css" && f.position === "pc_css") .filter((f) => f.type === "css" && f.position === "pc_css")
.map((f) => ({ .map((f) => ({
type: "FILE" as const, type: "FILE" as const,
file: { fileKey: f.fileKey }, file: { fileKey: f.fileKey },
})), })),
mobile: uploadedFiles },
mobile: {
js: uploadedFiles
.filter((f) => f.type === "js" && f.position.startsWith("mobile_"))
.map((f) => ({
type: "FILE" as const,
file: { fileKey: f.fileKey },
})),
css: uploadedFiles
.filter((f) => f.type === "css" && f.position === "mobile_css") .filter((f) => f.type === "css" && f.position === "mobile_css")
.map((f) => ({ .map((f) => ({
type: "FILE" as const, type: "FILE" as const,
file: { fileKey: f.fileKey }, file: { fileKey: f.fileKey },
})), })),
}, },
scope: "ALL",
revision: "-1",
}; };
// Update app customization // Update app customization
await client.updateAppCustomize(params.appId, newConfig);
// Deploy the changes // Deploy the changes
await client.deployApp(params.appId); await client.deployApp(params.appId);
@@ -458,12 +459,12 @@ function registerDownload(): void {
for (const fileType of fileTypes) { for (const fileType of fileTypes) {
const files = const files =
fileType === "pc_js" fileType === "pc_js"
? appDetail.customization?.javascript?.pc ? appDetail.customization?.desktop?.js
: fileType === "pc_css" : fileType === "pc_css"
? appDetail.customization?.stylesheet?.pc ? appDetail.customization?.desktop?.css
: fileType === "mobile_js" : fileType === "mobile_js"
? appDetail.customization?.javascript?.mobile ? appDetail.customization?.mobile?.js
: appDetail.customization?.stylesheet?.mobile; : appDetail.customization?.mobile?.css;
for (const file of files || []) { for (const file of files || []) {
if (file.file?.fileKey) { if (file.file?.fileKey) {

View File

@@ -3,11 +3,9 @@ import type { KintoneRestAPIError } from "@kintone/rest-api-client";
import type { DomainWithPassword } from "@shared/types/domain"; import type { DomainWithPassword } from "@shared/types/domain";
import type { import type {
AppResponse, AppResponse,
AppsResponse,
AppCustomizeResponse, AppCustomizeResponse,
AppDetail, AppDetail,
FileContent, FileContent,
AppCustomizationConfig,
KintoneApiError, KintoneApiError,
FileConfig, FileConfig,
} from "@shared/types/kintone"; } from "@shared/types/kintone";
@@ -82,34 +80,19 @@ export class SelfKintoneClient {
} }
} }
private mapResource(
resource:
| Awaited<AppCustomizeResponse>["desktop"]["js"][number]
| Awaited<AppCustomizeResponse>["desktop"]["css"][number],
): FileConfig {
// Return SDK type directly - no conversion needed
return resource;
}
private buildCustomizeSection( private buildCustomizeSection(
js?: FileConfig[], files?: FileConfig[],
css?: FileConfig[], ): NonNullable<
): Parameters< Parameters<KintoneRestAPIClient["app"]["updateAppCustomize"]>[0]["desktop"]
KintoneRestAPIClient["app"]["updateAppCustomize"] >["js"] {
>[0]["desktop"] { if (!files || files.length === 0) return undefined;
if (!js && !css) return undefined;
const mapItem = (item: FileConfig) => { return files.map((item) => {
if (item.type === "FILE" && item.file) { if (item.type === "FILE" && item.file) {
return { type: "FILE" as const, file: { fileKey: item.file.fileKey } }; return { type: "FILE" as const, file: { fileKey: item.file.fileKey } };
} }
return { type: "URL" as const, url: item.url || "" }; return { type: "URL" as const, url: item.url || "" };
}; });
return {
js: js?.map(mapItem),
css: css?.map(mapItem),
};
} }
// ==================== App APIs ==================== // ==================== App APIs ====================
@@ -161,32 +144,9 @@ export class SelfKintoneClient {
this.client.app.getAppCustomize({ app: appId }), this.client.app.getAppCustomize({ app: appId }),
]); ]);
const customization: AppCustomizationConfig = {
javascript: {
pc: customizeInfo.desktop.js?.map((js) => this.mapResource(js)) || [],
mobile:
customizeInfo.mobile.js?.map((js) => this.mapResource(js)) || [],
},
stylesheet: {
pc:
customizeInfo.desktop.css?.map((css) => this.mapResource(css)) ||
[],
mobile:
customizeInfo.mobile.css?.map((css) => this.mapResource(css)) || [],
},
};
return { return {
appId: appInfo.appId, ...appInfo,
name: appInfo.name, customization: customizeInfo,
code: appInfo.code,
description: appInfo.description,
spaceId: appInfo.spaceId || undefined,
createdAt: appInfo.createdAt,
creator: appInfo.creator,
modifiedAt: appInfo.modifiedAt,
modifier: appInfo.modifier,
customization,
}; };
}); });
} }
@@ -226,20 +186,20 @@ export class SelfKintoneClient {
async updateAppCustomize( async updateAppCustomize(
appId: string, appId: string,
config: AppCustomizationConfig, config: AppCustomizeResponse,
): Promise<void> { ): Promise<void> {
return this.withErrorHandling(async () => { return this.withErrorHandling(async () => {
await this.client.app.updateAppCustomize({ await this.client.app.updateAppCustomize({
app: appId, app: appId,
desktop: this.buildCustomizeSection( desktop: {
config.javascript?.pc, js: this.buildCustomizeSection(config.desktop?.js),
config.stylesheet?.pc, css: this.buildCustomizeSection(config.desktop?.css),
), },
mobile: this.buildCustomizeSection( mobile: {
config.javascript?.mobile, js: this.buildCustomizeSection(config.mobile?.js),
config.stylesheet?.mobile, css: this.buildCustomizeSection(config.mobile?.css),
), },
scope: "ALL", scope: config.scope || "ALL",
}); });
}); });
} }

View File

@@ -282,7 +282,7 @@ const AppDetail: React.FC = () => {
key: "pc-js", key: "pc-js",
label: "PC端 JS", label: "PC端 JS",
children: renderFileList( children: renderFileList(
currentApp.customization?.javascript?.pc, currentApp.customization?.desktop?.js,
"js", "js",
), ),
}, },
@@ -290,7 +290,7 @@ const AppDetail: React.FC = () => {
key: "pc-css", key: "pc-css",
label: "PC端 CSS", label: "PC端 CSS",
children: renderFileList( children: renderFileList(
currentApp.customization?.stylesheet?.pc, currentApp.customization?.desktop?.css,
"css", "css",
), ),
}, },
@@ -298,7 +298,7 @@ const AppDetail: React.FC = () => {
key: "mobile-js", key: "mobile-js",
label: "移动端 JS", label: "移动端 JS",
children: renderFileList( children: renderFileList(
currentApp.customization?.javascript?.mobile, currentApp.customization?.mobile?.js,
"js", "js",
), ),
}, },
@@ -306,7 +306,7 @@ const AppDetail: React.FC = () => {
key: "mobile-css", key: "mobile-css",
label: "移动端 CSS", label: "移动端 CSS",
children: renderFileList( children: renderFileList(
currentApp.customization?.stylesheet?.mobile, currentApp.customization?.mobile?.css,
"css", "css",
), ),
}, },

View File

@@ -12,19 +12,11 @@ type KintoneClient = KintoneRestAPIClient;
/** App response from getApp/getApps */ /** App response from getApp/getApps */
export type AppResponse = Awaited<ReturnType<KintoneClient["app"]["getApp"]>>; export type AppResponse = Awaited<ReturnType<KintoneClient["app"]["getApp"]>>;
/** Apps list response */
export type AppsResponse = Awaited<ReturnType<KintoneClient["app"]["getApps"]>>;
/** App customization response */ /** App customization response */
export type AppCustomizeResponse = Awaited< export type AppCustomizeResponse = Awaited<
ReturnType<KintoneClient["app"]["getAppCustomize"]> ReturnType<KintoneClient["app"]["getAppCustomize"]>
>; >;
/** File upload response */
export type FileUploadResponse = Awaited<
ReturnType<KintoneClient["file"]["uploadFile"]>
>;
// ==================== Custom Business Types ==================== // ==================== Custom Business Types ====================
// These types represent business-layer aggregation or additional metadata // These types represent business-layer aggregation or additional metadata
@@ -33,7 +25,7 @@ export type FileUploadResponse = Awaited<
* This is a business-layer type that aggregates data from multiple API calls * This is a business-layer type that aggregates data from multiple API calls
*/ */
export interface AppDetail extends AppResponse { export interface AppDetail extends AppResponse {
customization?: AppCustomizationConfig; customization?: AppCustomizeResponse;
} }
/** /**
@@ -48,27 +40,11 @@ export interface FileContent {
content?: string; // Base64 encoded or text content?: string; // Base64 encoded or text
} }
/**
* App customization config
* Uses SDK's customize types structure (desktop/mobile) but simplified for internal use
* Note: SDK uses { js: [], css: [] } but we use { javascript: { pc, mobile } } for easier UI binding
*/
export interface AppCustomizationConfig {
javascript?: {
pc?: FileConfig[];
mobile?: FileConfig[];
};
stylesheet?: {
pc?: FileConfig[];
mobile?: FileConfig[];
};
}
/** /**
* File config for customization * File config for customization
* Using SDK's type directly from AppCustomizeResponse * Using SDK's type directly from AppCustomizeResponse
*/ */
export type FileConfig = Awaited<AppCustomizeResponse>["desktop"]["js"][number]; export type FileConfig = NonNullable<AppCustomizeResponse["desktop"]["js"]>[number];
/** /**
* API Error - simplified from SDK's KintoneRestAPIError * API Error - simplified from SDK's KintoneRestAPIError