remove api token auth
This commit is contained in:
@@ -19,7 +19,6 @@ const TEST_CONFIG: DomainWithPassword = {
|
||||
domain: "alicorn.cybozu.com",
|
||||
username: "maxz",
|
||||
password: "7ld7i8vd",
|
||||
authType: "password",
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
@@ -124,8 +124,6 @@ function registerCreateDomain(): void {
|
||||
name: params.name,
|
||||
domain: params.domain,
|
||||
username: params.username,
|
||||
authType: params.authType,
|
||||
apiToken: params.apiToken,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
@@ -152,8 +150,6 @@ function registerUpdateDomain(): void {
|
||||
name: params.name ?? existing.name,
|
||||
domain: params.domain ?? existing.domain,
|
||||
username: params.username ?? existing.username,
|
||||
authType: params.authType ?? existing.authType,
|
||||
apiToken: params.apiToken ?? existing.apiToken,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
@@ -219,8 +215,6 @@ function registerTestDomainConnection(): void {
|
||||
domain: params.domain,
|
||||
username: params.username,
|
||||
password: params.password || "",
|
||||
authType: params.authType,
|
||||
apiToken: params.apiToken,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
@@ -1,13 +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 { 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';
|
||||
} from "@shared/types/kintone";
|
||||
|
||||
/**
|
||||
* Custom error class for Kintone API errors
|
||||
@@ -17,9 +17,13 @@ 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;
|
||||
@@ -36,22 +40,17 @@ export class KintoneClient {
|
||||
constructor(domainConfig: DomainWithPassword) {
|
||||
this.domain = domainConfig.domain;
|
||||
|
||||
const auth =
|
||||
domainConfig.authType === 'api_token'
|
||||
? { apiToken: domainConfig.apiToken || '' }
|
||||
: {
|
||||
username: domainConfig.username,
|
||||
password: domainConfig.password,
|
||||
};
|
||||
|
||||
this.client = new KintoneRestAPIClient({
|
||||
baseUrl: `https://${domainConfig.domain}`,
|
||||
auth,
|
||||
auth: {
|
||||
username: domainConfig.username,
|
||||
password: domainConfig.password,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -64,7 +63,7 @@ export class KintoneClient {
|
||||
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> {
|
||||
@@ -81,7 +80,10 @@ export class KintoneClient {
|
||||
* 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) {
|
||||
@@ -134,19 +136,23 @@ export class KintoneClient {
|
||||
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 },
|
||||
@@ -157,7 +163,10 @@ export class KintoneClient {
|
||||
|
||||
// ==================== Deploy APIs ====================
|
||||
|
||||
async updateAppCustomize(appId: string, config: Omit<AppCustomizeParameter, 'app'>): Promise<void> {
|
||||
async updateAppCustomize(
|
||||
appId: string,
|
||||
config: Omit<AppCustomizeParameter, "app">,
|
||||
): Promise<void> {
|
||||
return this.withErrorHandling(async () => {
|
||||
await this.client.app.updateAppCustomize({ ...config, app: appId });
|
||||
});
|
||||
@@ -169,10 +178,12 @@ export class KintoneClient {
|
||||
});
|
||||
}
|
||||
|
||||
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";
|
||||
});
|
||||
}
|
||||
|
||||
@@ -186,7 +197,8 @@ export class KintoneClient {
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof KintoneError ? error.message : 'Connection failed',
|
||||
error:
|
||||
error instanceof KintoneError ? error.message : "Connection failed",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import { createStyles } from "antd-style";
|
||||
import { useAppStore } from "@renderer/stores";
|
||||
import { useDomainStore } from "@renderer/stores";
|
||||
import { CodeViewer } from "../CodeViewer";
|
||||
import { FileConfigResponse } from "@shared/types/kintone";
|
||||
import { getDisplayName, getFileKey, isFileUpload } from "@shared/utils/fileDisplay";
|
||||
import { FileConfigResponse, isFileResource } from "@shared/types/kintone";
|
||||
import { getDisplayName, getFileKey } from "@shared/utils/fileDisplay";
|
||||
const useStyles = createStyles(({ token, css }) => ({
|
||||
container: css`
|
||||
height: 100%;
|
||||
@@ -180,7 +180,7 @@ const AppDetail: React.FC = () => {
|
||||
{files.map((file, index) => {
|
||||
const fileName = getDisplayName(file, type, index);
|
||||
const fileKey = getFileKey(file);
|
||||
const canView = isFileUpload(file);
|
||||
const canView = isFileResource(file);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -4,13 +4,10 @@
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Modal, Form, Input, Select, Button, Space, message } from "antd";
|
||||
import { Modal, Form, Input, Button, Space, message } from "antd";
|
||||
import { createStyles } from "antd-style";
|
||||
import { useDomainStore } from "@renderer/stores";
|
||||
import type {
|
||||
CreateDomainParams,
|
||||
UpdateDomainParams,
|
||||
} from "@shared/types/ipc";
|
||||
import type { CreateDomainParams, UpdateDomainParams } from "@shared/types/ipc";
|
||||
|
||||
const useStyles = createStyles(({ token, css }) => ({
|
||||
form: css`
|
||||
@@ -49,12 +46,9 @@ const DomainForm: React.FC<DomainFormProps> = ({ open, onClose, domainId }) => {
|
||||
name: editingDomain.name,
|
||||
domain: editingDomain.domain,
|
||||
username: editingDomain.username,
|
||||
authType: editingDomain.authType,
|
||||
apiToken: editingDomain.apiToken || "",
|
||||
});
|
||||
} else {
|
||||
form.resetFields();
|
||||
form.setFieldsValue({ authType: "password" });
|
||||
}
|
||||
}
|
||||
}, [open, editingDomain, form]);
|
||||
@@ -81,9 +75,6 @@ const DomainForm: React.FC<DomainFormProps> = ({ open, onClose, domainId }) => {
|
||||
name,
|
||||
domain: processedDomain,
|
||||
username: values.username,
|
||||
authType: values.authType,
|
||||
apiToken:
|
||||
values.authType === "api_token" ? values.apiToken : undefined,
|
||||
};
|
||||
|
||||
// Only include password if provided
|
||||
@@ -104,9 +95,6 @@ const DomainForm: React.FC<DomainFormProps> = ({ open, onClose, domainId }) => {
|
||||
domain: processedDomain,
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
authType: values.authType,
|
||||
apiToken:
|
||||
values.authType === "api_token" ? values.apiToken : undefined,
|
||||
};
|
||||
|
||||
const success = await createDomain(params);
|
||||
@@ -122,7 +110,6 @@ const DomainForm: React.FC<DomainFormProps> = ({ open, onClose, domainId }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const authType = Form.useWatch("authType", form);
|
||||
const [testing, setTesting] = React.useState(false);
|
||||
|
||||
// Test connection with current form values
|
||||
@@ -131,9 +118,7 @@ const DomainForm: React.FC<DomainFormProps> = ({ open, onClose, domainId }) => {
|
||||
const values = await form.validateFields([
|
||||
"domain",
|
||||
"username",
|
||||
"authType",
|
||||
"password",
|
||||
"apiToken",
|
||||
]);
|
||||
|
||||
// Process domain
|
||||
@@ -149,9 +134,7 @@ const DomainForm: React.FC<DomainFormProps> = ({ open, onClose, domainId }) => {
|
||||
const result = await window.api.testDomainConnection({
|
||||
domain: processedDomain,
|
||||
username: values.username,
|
||||
authType: values.authType,
|
||||
password: values.authType === "password" ? values.password : undefined,
|
||||
apiToken: values.authType === "api_token" ? values.apiToken : undefined,
|
||||
password: values.password,
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
@@ -175,17 +158,11 @@ const DomainForm: React.FC<DomainFormProps> = ({ open, onClose, domainId }) => {
|
||||
width={520}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
className={styles.form}
|
||||
initialValues={{ authType: "password" }}
|
||||
>
|
||||
<Form form={form} layout="vertical" className={styles.form}>
|
||||
<Form.Item name="name" label="名称" style={{ marginTop: 8 }}>
|
||||
<Input placeholder="可选,留空则使用域名" />
|
||||
</Form.Item>
|
||||
|
||||
|
||||
<Form.Item
|
||||
name="domain"
|
||||
label="Kintone 域名"
|
||||
@@ -223,18 +200,6 @@ const DomainForm: React.FC<DomainFormProps> = ({ open, onClose, domainId }) => {
|
||||
<Input placeholder="登录 Kintone 的用户名" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="authType"
|
||||
label="认证方式"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option value="password">密码认证</Select.Option>
|
||||
<Select.Option value="api_token">API Token 认证</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{authType === "password" && (
|
||||
<Form.Item
|
||||
name="password"
|
||||
label="密码"
|
||||
@@ -244,17 +209,6 @@ const DomainForm: React.FC<DomainFormProps> = ({ open, onClose, domainId }) => {
|
||||
placeholder={isEdit ? "留空则保持原密码" : "请输入密码"}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{authType === "api_token" && (
|
||||
<Form.Item
|
||||
name="apiToken"
|
||||
label="API Token"
|
||||
rules={[{ required: true, message: "请输入 API Token" }]}
|
||||
>
|
||||
<Input placeholder="从 Kintone 设置中获取 API Token" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item style={{ marginTop: 24, marginBottom: 0 }}>
|
||||
<Space style={{ width: "100%", justifyContent: "flex-end" }}>
|
||||
|
||||
@@ -4,11 +4,7 @@
|
||||
*/
|
||||
|
||||
import type { Domain, DomainWithStatus } from "./domain";
|
||||
import type {
|
||||
AppResponse,
|
||||
AppDetail,
|
||||
FileContent,
|
||||
} from "./kintone";
|
||||
import type { AppResponse, AppDetail, FileContent } from "./kintone";
|
||||
import type { Version, DownloadMetadata, BackupMetadata } from "./version";
|
||||
|
||||
// Unified result type
|
||||
@@ -23,8 +19,6 @@ export interface CreateDomainParams {
|
||||
domain: string;
|
||||
username: string;
|
||||
password: string;
|
||||
authType: "password" | "api_token";
|
||||
apiToken?: string;
|
||||
}
|
||||
|
||||
export interface UpdateDomainParams {
|
||||
@@ -33,16 +27,12 @@ export interface UpdateDomainParams {
|
||||
domain?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
authType?: "password" | "api_token";
|
||||
apiToken?: string;
|
||||
}
|
||||
|
||||
export interface TestDomainConnectionParams {
|
||||
domain: string;
|
||||
username: string;
|
||||
authType: "password" | "api_token";
|
||||
password?: string;
|
||||
apiToken?: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
// ==================== Browse IPC Types ====================
|
||||
|
||||
@@ -86,7 +86,7 @@ export type FileResourceResponse = ExtractFileType<FileConfigResponse>;
|
||||
export function isUrlResource(
|
||||
resource: FileConfigResponse | FileConfigParameter,
|
||||
): resource is UrlResourceParameter | UrlResourceResponse {
|
||||
return resource.type === "URL";
|
||||
return resource.type === "URL" && !!resource.url;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,5 +96,5 @@ export function isUrlResource(
|
||||
export function isFileResource(
|
||||
resource: FileConfigResponse | FileConfigParameter,
|
||||
): resource is FileResourceParameter | FileResourceResponse {
|
||||
return resource.type === "FILE";
|
||||
return resource.type === "FILE" && !!resource.file?.fileKey;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FileConfigResponse } from "@shared/types/kintone";
|
||||
import { isFileResource, isUrlResource, type FileConfigResponse } from "@shared/types/kintone";
|
||||
|
||||
/**
|
||||
* Get user-friendly display name for a file config
|
||||
@@ -8,11 +8,11 @@ export function getDisplayName(
|
||||
fileType: "js" | "css",
|
||||
index: number,
|
||||
): string {
|
||||
if (file.type === "URL" && file.url) {
|
||||
if (isUrlResource(file)) {
|
||||
return extractFilenameFromUrl(file.url) || `URL ${index + 1}`;
|
||||
}
|
||||
|
||||
if (file.type === "FILE" && file.file?.fileKey) {
|
||||
if (isFileResource(file)) {
|
||||
return `${file.file.fileKey}.${fileType}`;
|
||||
}
|
||||
|
||||
@@ -33,16 +33,9 @@ export function extractFilenameFromUrl(url: string): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file config is a FILE type upload
|
||||
*/
|
||||
export function isFileUpload(file: FileConfigResponse): boolean {
|
||||
return file.type === "FILE" && !!file.file?.fileKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fileKey from file config (only for FILE type)
|
||||
*/
|
||||
export function getFileKey(file: FileConfigResponse): string | undefined {
|
||||
return file.type === "FILE" ? file.file?.fileKey : undefined;
|
||||
return isFileResource(file) ? file.file?.fileKey : undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user