ci: fix workflow branch and deploy config

This commit is contained in:
2026-04-23 22:23:36 +05:00
parent 65cf8ca01f
commit 54f4a331ed
2 changed files with 72 additions and 21 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ name: Deploy XFlow (AVTEX/Projects)
on: on:
push: push:
branches: branches:
- main - master
workflow_dispatch: workflow_dispatch:
jobs: jobs:
+71 -20
View File
@@ -1,5 +1,8 @@
// Gitea API integration for XFlow canvas sync // Gitea API integration for XFlow canvas sync
// Uses Gitea REST API to store mind maps in MMAVTEX repo // Structure:
// Personal canvases → AVTEX/user-{username} (created on first save)
// Group canvases → AVTEX/group-{groupId} (created on group creation)
// Public canvases → AVTEX/MMAVTEX (shared org repo)
interface GiteaFile { interface GiteaFile {
content: string // base64 content: string // base64
@@ -17,19 +20,58 @@ const getConfig = () => {
} }
} }
const headers = (token: string) => ({ const apiHeaders = (token: string) => ({
'Authorization': `token ${token}`, 'Authorization': `token ${token}`,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Accept': 'application/json' 'Accept': 'application/json'
}) })
// Resolve which repo to use based on canvas ownership
export function resolveRepo(opts: { username?: string; groupId?: string; isPublic?: boolean }): string {
const { org, mindmapRepo } = getConfig()
if (opts.groupId) return `group-${opts.groupId}`
if (opts.username && !opts.isPublic) return `user-${opts.username}`
return mindmapRepo
}
// Ensure repository exists in org, create if not
export async function ensureRepo(repoName: string, description = ''): Promise<boolean> {
const { url, token, org } = getConfig()
const h = apiHeaders(token)
// Check if exists
try {
await $fetch(`${url}/api/v1/repos/${org}/${repoName}`, { headers: h })
return true
} catch { }
// Create
try {
await $fetch(`${url}/api/v1/orgs/${org}/repos`, {
method: 'POST',
headers: h,
body: {
name: repoName,
description: description || `XFlow canvas repo: ${repoName}`,
private: !repoName.startsWith('group-') && repoName !== getConfig().mindmapRepo,
auto_init: true,
default_branch: 'main'
}
})
return true
} catch (e) {
console.error(`Gitea ensureRepo(${repoName}) error:`, e)
return false
}
}
// Get file from Gitea (returns null if not found) // Get file from Gitea (returns null if not found)
export async function giteaGetFile(path: string, repo?: string): Promise<GiteaFile | null> { export async function giteaGetFile(path: string, repo?: string): Promise<GiteaFile | null> {
const { url, token, org, mindmapRepo } = getConfig() const { url, token, org, mindmapRepo } = getConfig()
const repoName = repo || mindmapRepo const repoName = repo || mindmapRepo
try { try {
const res = await $fetch<any>(`${url}/api/v1/repos/${org}/${repoName}/contents/${path}`, { const res = await $fetch<any>(`${url}/api/v1/repos/${org}/${repoName}/contents/${path}`, {
headers: headers(token) headers: apiHeaders(token)
}) })
return { content: res.content, sha: res.sha } return { content: res.content, sha: res.sha }
} catch { } catch {
@@ -46,7 +88,7 @@ export async function giteaPutFile(path: string, content: string, message: strin
try { try {
await $fetch(`${url}/api/v1/repos/${org}/${repoName}/contents/${path}`, { await $fetch(`${url}/api/v1/repos/${org}/${repoName}/contents/${path}`, {
method: sha ? 'PUT' : 'POST', method: sha ? 'PUT' : 'POST',
headers: headers(token), headers: apiHeaders(token),
body: { body: {
message, message,
content: encoded, content: encoded,
@@ -60,37 +102,46 @@ export async function giteaPutFile(path: string, content: string, message: strin
} }
} }
// Save canvas to Gitea MMAVTEX repo // Save canvas to correct repo based on ownership
export async function saveCanvasToGitea(canvasId: string, canvasData: any, commitMessage?: string): Promise<boolean> { export async function saveCanvasToGitea(
const path = `canvases/${canvasId}.canvas` canvasId: string,
canvasData: any,
opts: { username?: string; groupId?: string; isPublic?: boolean } = {},
commitMessage?: string
): Promise<boolean> {
const repo = resolveRepo(opts)
const path = `${canvasId}.canvas`
const content = JSON.stringify(canvasData, null, 2) const content = JSON.stringify(canvasData, null, 2)
const message = commitMessage || `Update canvas ${canvasId} at ${new Date().toLocaleString('ru-RU')}` const message = commitMessage || `Update ${canvasId} at ${new Date().toLocaleString('ru-RU')}`
// Get existing file SHA if exists (required for update) await ensureRepo(repo)
const existing = await giteaGetFile(path) const existing = await giteaGetFile(path, repo)
return giteaPutFile(path, content, message, existing?.sha) return giteaPutFile(path, content, message, existing?.sha, repo)
} }
// Load canvas from Gitea // Load canvas from Gitea
export async function loadCanvasFromGitea(canvasId: string): Promise<any | null> { export async function loadCanvasFromGitea(
const path = `canvases/${canvasId}.canvas` canvasId: string,
const file = await giteaGetFile(path) opts: { username?: string; groupId?: string; isPublic?: boolean } = {}
): Promise<any | null> {
const repo = resolveRepo(opts)
const file = await giteaGetFile(`${canvasId}.canvas`, repo)
if (!file) return null if (!file) return null
try { try {
const decoded = Buffer.from(file.content, 'base64').toString('utf-8') return JSON.parse(Buffer.from(file.content, 'base64').toString('utf-8'))
return JSON.parse(decoded)
} catch { } catch {
return null return null
} }
} }
// List all canvases in Gitea // List canvases in a repo
export async function listCanvasesFromGitea(): Promise<string[]> { export async function listCanvasesFromGitea(repo?: string): Promise<string[]> {
const { url, token, org, mindmapRepo } = getConfig() const { url, token, org, mindmapRepo } = getConfig()
const repoName = repo || mindmapRepo
try { try {
const res = await $fetch<any[]>(`${url}/api/v1/repos/${org}/${mindmapRepo}/contents/canvases`, { const res = await $fetch<any[]>(`${url}/api/v1/repos/${org}/${repoName}/contents/`, {
headers: headers(token) headers: apiHeaders(token)
}) })
return res return res
.filter((f: any) => f.name.endsWith('.canvas')) .filter((f: any) => f.name.endsWith('.canvas'))