Files
penx/apps/web/lib/SyncService.ts
2025-07-23 20:33:59 +08:00

183 lines
3.7 KiB
TypeScript

import { Octokit } from 'octokit'
export type TreeItem = {
path: string
// mode: '100644' | '100755' | '040000' | '160000' | '120000'
mode: '100644'
// type: 'blob' | 'tree' | 'commit'
type: 'blob'
content?: string
sha?: string | null
}
interface SharedParams {
owner: string
repo: string
headers: {
'X-GitHub-Api-Version': string
}
}
type Content = {
content?: string
name: string
path: string
sha: string
size: number
url: string
html_url: string
git_url: string
download_url: string
type: 'file' | 'dir'
}
export class SyncService {
private params: SharedParams
private app: Octokit
private baseBranchSha: string
private space: any
private creation: any
filesTree: Content[]
commitSha: string
get baseBranchName() {
return 'main'
}
setSharedParams(owner: string, repo: string) {
const sharedParams = {
owner,
repo,
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
}
this.params = sharedParams
}
static async init(token: string, space: any) {
const s = new SyncService()
const [owner, repo] = (space.repo || '').split('/')
s.setSharedParams(owner, repo)
s.app = new Octokit({ auth: token })
return s
}
private async updateRef(commitSha: string = '') {
const branchName = this.baseBranchName
await this.app.request('PATCH /repos/{owner}/{repo}/git/refs/{ref}', {
...this.params,
ref: `heads/${branchName}`,
sha: commitSha,
force: true,
})
}
private async commit(treeSha: string) {
const parentSha = this.baseBranchSha
const msg = this.creation
? `Publish post: ${this.creation.title}`
: 'Push site'
const commit = await this.app.request(
'POST /repos/{owner}/{repo}/git/commits',
{
...this.params,
message: `${msg}`,
parents: [parentSha],
tree: treeSha,
},
)
return commit
}
async getBaseBranchInfo() {
const ref = await this.app.request(
'GET /repos/{owner}/{repo}/git/ref/{ref}',
{
...this.params,
ref: `heads/${this.baseBranchName}`,
},
)
const refSha = ref.data.object.sha
this.baseBranchSha = refSha
}
async getSiteTree() {
let tree: TreeItem[] = []
const item = {
path: `sites/${this.space.id}.json`,
mode: '100644',
type: 'blob',
content: JSON.stringify(this.space, null, 2),
} as TreeItem
tree.push(item)
return tree
}
async getPostTree(markdown = '') {
let tree: TreeItem[] = []
tree.push({
path: `json/${this.creation.id}.json`,
mode: '100644',
type: 'blob',
content: JSON.stringify(this.creation, null, 2),
})
tree.push({
path: `markdown/${this.creation.id}.md`,
mode: '100644',
type: 'blob',
content: markdown,
})
return tree
}
async pushSpace(space: any) {
this.space = space
let tree: TreeItem[] = []
tree = await this.getSiteTree()
await this.pushTree(tree)
}
async pushPost(post: any, markdown = '') {
this.creation = post
let tree: TreeItem[] = []
tree = await this.getPostTree(markdown)
await this.pushTree(tree)
}
async pushTree(tree: TreeItem[]) {
// console.log('===========push tree....')
await this.getBaseBranchInfo()
// update tree to GitHub before commit
const { data } = await this.app.request(
'POST /repos/{owner}/{repo}/git/trees',
{
...this.params,
tree,
base_tree: this.baseBranchSha,
},
)
// create a commit for the tree
const { data: commitData } = await this.commit(data.sha)
// update ref to GitHub server after commit
await this.updateRef(commitData.sha)
}
}