Fix remaining warnings: add API response types, fix unsafe access

- github-api.ts: Add ShaResponse, TreeResponse, CompareResponse, UserResponse, GitCommitResponse interfaces; cast all resp.json
- github-auth.ts: Add DeviceCodeResponse, TokenResponse, UserResponse interfaces; cast all resp.json
- sync-engine.ts: Remove unnecessary TFile cast, fix repoOwner -> githubUsername
- main.ts: Wrap async setTimeout callback to avoid void return warning
This commit is contained in:
Yusen 2026-06-13 21:25:47 +08:00
parent c4b7150146
commit 3ee1efb4de
4 changed files with 53 additions and 34 deletions

View file

@ -4,6 +4,16 @@ import type { GitHubFile, CompareResult, LocalFileInfo, TreeItem } from "./types
const API_BASE = "https://api.github.com";
const RAW_BASE = "https://raw.githubusercontent.com";
/** GitHub API response types */
interface ShaResponse { sha: string }
interface TreeResponse { sha: string; tree: GitHubFile[]; truncated: boolean }
interface CompareResponse {
files: { filename: string; status: string; previous_filename?: string }[];
commits: { sha: string }[];
}
interface UserResponse { login: string }
interface GitCommitResponse { sha: string; tree: { sha: string } }
export class GitHubApiClient {
private token: string;
private owner: string;
@ -34,7 +44,7 @@ export class GitHubApiClient {
url: `${API_BASE}/repos/${this.owner}/${this.repo}/commits/${this.branch}`,
headers: this.headers,
});
return resp.json.sha;
return (resp.json as ShaResponse).sha;
}
/**
@ -45,7 +55,7 @@ export class GitHubApiClient {
url: `${API_BASE}/repos/${this.owner}/${this.repo}/git/trees/${this.branch}?recursive=1`,
headers: this.headers,
});
const data = resp.json;
const data = resp.json as TreeResponse;
if (data.truncated) {
console.warn("[moving-note] File tree truncated, some files may be missing");
}
@ -60,19 +70,13 @@ export class GitHubApiClient {
url: `${API_BASE}/repos/${this.owner}/${this.repo}/compare/${oldSha}...${this.branch}`,
headers: this.headers,
});
const data = resp.json;
const data = resp.json as CompareResponse;
return {
files: data.files.map(
(f: {
filename: string;
status: string;
previous_filename?: string;
}) => ({
filename: f.filename,
status: f.status,
previous_filename: f.previous_filename,
})
),
files: data.files.map((f) => ({
filename: f.filename,
status: f.status,
previous_filename: f.previous_filename,
})),
newSha: data.commits[data.commits.length - 1]?.sha ?? oldSha,
};
}
@ -229,7 +233,7 @@ export class GitHubApiClient {
url: `${API_BASE}/user`,
headers: this.headers,
});
return { valid: true, username: resp.json.login };
return { valid: true, username: (resp.json as UserResponse).login };
} catch {
return { valid: false };
}
@ -246,7 +250,7 @@ export class GitHubApiClient {
url: `${API_BASE}/repos/${this.owner}/${this.repo}/git/commits/${commitSha}`,
headers: this.headers,
});
return resp.json.tree.sha;
return (resp.json as GitCommitResponse).tree.sha;
}
/**
@ -259,7 +263,7 @@ export class GitHubApiClient {
headers: this.headers,
body: JSON.stringify({ content, encoding }),
});
return resp.json.sha;
return (resp.json as ShaResponse).sha;
}
/**
@ -272,7 +276,7 @@ export class GitHubApiClient {
headers: this.headers,
body: JSON.stringify({ base_tree: baseTreeSha, tree: treeItems }),
});
return resp.json.sha;
return (resp.json as ShaResponse).sha;
}
/**
@ -285,7 +289,7 @@ export class GitHubApiClient {
headers: this.headers,
body: JSON.stringify({ message, tree: treeSha, parents: [parentSha] }),
});
return resp.json.sha;
return (resp.json as ShaResponse).sha;
}
/**
@ -353,7 +357,7 @@ export class GitHubApiClient {
url: `${API_BASE}/repos/${this.owner}/${this.repo}/contents/${encodeURI(path)}?ref=${this.branch}`,
headers: this.headers,
});
const fileSha = resp.json.sha;
const fileSha = (resp.json as ShaResponse).sha;
await requestUrl({
url: `${API_BASE}/repos/${this.owner}/${this.repo}/contents/${encodeURI(path)}`,

View file

@ -1,5 +1,19 @@
import { requestUrl } from "obsidian";
/** GitHub OAuth response types */
interface DeviceCodeResponse {
device_code: string;
user_code: string;
verification_uri: string;
expires_in: number;
interval: number;
}
interface TokenResponse {
access_token?: string;
error?: string;
}
interface UserResponse { login: string }
/**
* GitHub OAuth Device Flow
* Token
@ -54,7 +68,7 @@ export async function startDeviceFlow(
});
const { device_code, user_code, verification_uri, expires_in, interval } =
codeResp.json;
codeResp.json as DeviceCodeResponse;
// Step 2: 通知 UI 显示验证码
onCodeReady(user_code, verification_uri);
@ -83,7 +97,7 @@ export async function startDeviceFlow(
}),
});
const data = tokenResp.json;
const data = tokenResp.json as TokenResponse;
if (data.access_token) {
// 登录成功!获取用户名
@ -98,7 +112,7 @@ export async function startDeviceFlow(
return {
success: true,
token: data.access_token,
username: userResp.json.login,
username: (userResp.json as UserResponse).login,
};
}
@ -146,7 +160,7 @@ export async function validateToken(
Accept: "application/vnd.github.v3+json",
},
});
return { valid: true, username: resp.json.login };
return { valid: true, username: (resp.json as UserResponse).login };
} catch {
return { valid: false };
}

15
main.ts
View file

@ -39,14 +39,15 @@ export default class MovingNotePlugin extends Plugin {
// Sync on startup
if (this.settings.syncOnStartup) {
this.app.workspace.onLayoutReady(() => {
// Delay 2 seconds before syncing, wait for Obsidian to fully load
window.setTimeout(async () => {
if (this.isConfigured()) {
const result = await this.syncNow();
if (result.filesChanged > 0) {
new Notice(result.message);
window.setTimeout(() => {
void (async () => {
if (this.isConfigured()) {
const result = await this.syncNow();
if (result.filesChanged > 0) {
new Notice(result.message);
}
}
}
})();
}, 2000);
});
}

View file

@ -276,7 +276,7 @@ export class SyncEngine {
if (file.path.startsWith(this.app.vault.configDir + "/")) continue;
try {
const buffer = await this.app.vault.readBinary(file as import("obsidian").TFile);
const buffer = await this.app.vault.readBinary(file);
const sha = await this.computeGitSha1(buffer);
files[file.path] = sha;
} catch {
@ -351,7 +351,7 @@ export class SyncEngine {
const content = await this.app.vault.read(file as import("obsidian").TFile);
files.push({ path, content, encoding: "utf-8", mode: "100644" });
} else {
const buffer = await this.app.vault.readBinary(file as import("obsidian").TFile);
const buffer = await this.app.vault.readBinary(file);
const base64 = this.arrayBufferToBase64(buffer);
files.push({ path, content: base64, encoding: "base64", mode: "100644" });
}
@ -524,7 +524,7 @@ export class SyncEngine {
private formatCommitMessage(): string {
const now = new Date();
const date = now.toISOString().replace("T", " ").substring(0, 19);
const hostname = this.settings.repoOwner;
const hostname = this.settings.githubUsername || "unknown";
return this.settings.commitMessage
.replace("{{date}}", date)
.replace("{{hostname}}", hostname);