feat: static sites feature with Gitea/GitHub/GitLab support and Deno backend
Deploy static content from Git repository folders with optional server-side
API endpoints. Supports Gitea/Forgejo/Gogs, GitHub, and GitLab with provider
autodetection.
- New Sites entity with CRUD, encrypted secrets, and manual/push/tag sync triggers
- Pluggable GitProvider interface with three implementations
- Deno container mode: auto-generates router from API_{method}_{name} exports
- Static container mode: nginx serving files with optional markdown rendering
- Wizard UI with provider selector, repo picker, branch/folder tree pickers
- Deploy pipeline builds fresh image, starts container, configures NPM proxy
- Stop/Start buttons, force redeploy on manual trigger
- Periodic health checker detects crashed containers
- Proxy route existence check during auto-sync
This commit is contained in:
@@ -604,4 +604,110 @@ export function fetchContainerStats(
|
||||
);
|
||||
}
|
||||
|
||||
// ── Static Sites ──────────────────────────────────────────────────────
|
||||
|
||||
import type { StaticSite, StaticSiteSecret, FolderEntry, GitProvider, RepoInfo } from './types';
|
||||
|
||||
export function listStaticSites(): Promise<StaticSite[]> {
|
||||
return get<StaticSite[]>('/api/sites');
|
||||
}
|
||||
|
||||
export function getStaticSite(id: string): Promise<StaticSite> {
|
||||
return get<StaticSite>(`/api/sites/${id}`);
|
||||
}
|
||||
|
||||
export function createStaticSite(data: Partial<StaticSite>): Promise<StaticSite> {
|
||||
return post<StaticSite>('/api/sites', data);
|
||||
}
|
||||
|
||||
export function updateStaticSite(id: string, data: Partial<StaticSite>): Promise<StaticSite> {
|
||||
return put<StaticSite>(`/api/sites/${id}`, data);
|
||||
}
|
||||
|
||||
export function deleteStaticSite(id: string): Promise<{ deleted: string }> {
|
||||
return del<{ deleted: string }>(`/api/sites/${id}`);
|
||||
}
|
||||
|
||||
export function deployStaticSite(id: string): Promise<{ status: string }> {
|
||||
return post<{ status: string }>(`/api/sites/${id}/deploy`);
|
||||
}
|
||||
|
||||
export function stopStaticSite(id: string): Promise<{ status: string }> {
|
||||
return post<{ status: string }>(`/api/sites/${id}/stop`);
|
||||
}
|
||||
|
||||
export function startStaticSite(id: string): Promise<{ status: string }> {
|
||||
return post<{ status: string }>(`/api/sites/${id}/start`);
|
||||
}
|
||||
|
||||
export function listStaticSiteRepos(data: {
|
||||
provider?: string;
|
||||
gitea_url: string;
|
||||
access_token?: string;
|
||||
query?: string;
|
||||
}): Promise<RepoInfo[]> {
|
||||
return post<RepoInfo[]>('/api/sites/repos', data);
|
||||
}
|
||||
|
||||
export function detectStaticSiteProvider(url: string): Promise<{ provider: GitProvider }> {
|
||||
return post<{ provider: GitProvider }>('/api/sites/detect-provider', { url });
|
||||
}
|
||||
|
||||
export function testStaticSiteConnection(data: {
|
||||
provider?: string;
|
||||
gitea_url: string;
|
||||
access_token?: string;
|
||||
repo_owner: string;
|
||||
repo_name: string;
|
||||
}): Promise<{ status: string }> {
|
||||
return post<{ status: string }>('/api/sites/test-connection', data);
|
||||
}
|
||||
|
||||
export function listStaticSiteBranches(data: {
|
||||
provider?: string;
|
||||
gitea_url: string;
|
||||
access_token?: string;
|
||||
repo_owner: string;
|
||||
repo_name: string;
|
||||
}): Promise<string[]> {
|
||||
return post<string[]>('/api/sites/branches', data);
|
||||
}
|
||||
|
||||
export function listStaticSiteTree(data: {
|
||||
provider?: string;
|
||||
gitea_url: string;
|
||||
access_token?: string;
|
||||
repo_owner: string;
|
||||
repo_name: string;
|
||||
branch: string;
|
||||
}): Promise<FolderEntry[]> {
|
||||
return post<FolderEntry[]>('/api/sites/tree', data);
|
||||
}
|
||||
|
||||
export function listStaticSiteSecrets(siteId: string): Promise<StaticSiteSecret[]> {
|
||||
return get<StaticSiteSecret[]>(`/api/sites/${siteId}/secrets`);
|
||||
}
|
||||
|
||||
export function createStaticSiteSecret(
|
||||
siteId: string,
|
||||
data: { key: string; value: string; encrypted?: boolean }
|
||||
): Promise<StaticSiteSecret> {
|
||||
return post<StaticSiteSecret>(`/api/sites/${siteId}/secrets`, data);
|
||||
}
|
||||
|
||||
export function updateStaticSiteSecret(
|
||||
siteId: string,
|
||||
secretId: string,
|
||||
data: { key?: string; value?: string; encrypted?: boolean }
|
||||
): Promise<StaticSiteSecret> {
|
||||
return put<StaticSiteSecret>(`/api/sites/${siteId}/secrets/${secretId}`, data);
|
||||
}
|
||||
|
||||
export function deleteStaticSiteSecret(
|
||||
siteId: string,
|
||||
secretId: string
|
||||
): Promise<{ deleted: string }> {
|
||||
return del<{ deleted: string }>(`/api/sites/${siteId}/secrets/${secretId}`);
|
||||
}
|
||||
|
||||
export { ApiError };
|
||||
|
||||
Reference in New Issue
Block a user