feat: add Docker and Gitea services, monitoring, queue, and Telegram notification functionalities
- Implemented Docker operations including image building, container management, and resource stats. - Added Gitea API client for repository management and webhook handling. - Introduced monitoring service to collect and store container metrics in InfluxDB. - Created a queue system using BullMQ for managing deployment jobs with real-time log streaming. - Developed Telegram notification service for deployment status updates. - Added Traefik label generation for dynamic reverse proxy configuration. - Implemented WebSocket endpoints for log streaming and terminal access to containers. - Created an updater sidecar for self-updating the AutoDeployer container.
This commit is contained in:
134
server/src/services/gitea.js
Normal file
134
server/src/services/gitea.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import config from '../config.js';
|
||||
|
||||
/**
|
||||
* Gitea API client — uses API token auth
|
||||
*/
|
||||
|
||||
const headers = () => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `token ${config.giteaToken}`,
|
||||
});
|
||||
|
||||
const apiUrl = (path) => `${config.giteaUrl}/api/v1${path}`;
|
||||
|
||||
async function request(path, opts = {}) {
|
||||
const url = apiUrl(path);
|
||||
const res = await fetch(url, {
|
||||
...opts,
|
||||
headers: { ...headers(), ...opts.headers },
|
||||
});
|
||||
if (!res.ok) {
|
||||
const body = await res.text().catch(() => '');
|
||||
throw new Error(`Gitea API error: ${res.status} ${res.statusText} — ${body}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* List all accessible repositories
|
||||
*/
|
||||
export async function listRepos(page = 1, limit = 50) {
|
||||
return request(`/repos/search?page=${page}&limit=${limit}&sort=updated&order=desc`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a repository by owner/name
|
||||
*/
|
||||
export async function getRepo(owner, name) {
|
||||
return request(`/repos/${owner}/${name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* List branches for a repository
|
||||
*/
|
||||
export async function listBranches(owner, name) {
|
||||
return request(`/repos/${owner}/${name}/branches`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest commit on a branch
|
||||
*/
|
||||
export async function getLatestCommit(owner, name, branch = 'main') {
|
||||
const commits = await request(`/repos/${owner}/${name}/commits?sha=${branch}&limit=1`);
|
||||
if (commits.length === 0) return null;
|
||||
return {
|
||||
sha: commits[0].sha,
|
||||
message: commits[0].commit?.message || '',
|
||||
author: commits[0].commit?.author?.name || '',
|
||||
date: commits[0].commit?.author?.date || '',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a webhook on a Gitea repository
|
||||
*/
|
||||
export async function createWebhook(owner, name, targetUrl, secret) {
|
||||
return request(`/repos/${owner}/${name}/hooks`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
type: 'gitea',
|
||||
config: {
|
||||
url: targetUrl,
|
||||
content_type: 'json',
|
||||
secret: secret,
|
||||
},
|
||||
events: ['push'],
|
||||
active: true,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a webhook from a Gitea repository
|
||||
*/
|
||||
export async function deleteWebhook(owner, name, hookId) {
|
||||
const url = apiUrl(`/repos/${owner}/${name}/hooks/${hookId}`);
|
||||
const res = await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: headers(),
|
||||
});
|
||||
if (!res.ok && res.status !== 404) {
|
||||
throw new Error(`Gitea API error: ${res.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List webhooks for a repository
|
||||
*/
|
||||
export async function listWebhooks(owner, name) {
|
||||
return request(`/repos/${owner}/${name}/hooks`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clone URL (with token embedded for private repos)
|
||||
*/
|
||||
export function getCloneUrl(repoUrl) {
|
||||
// repoUrl is like "http://gitea:3000/owner/repo"
|
||||
// We need to inject the token for authenticated clone
|
||||
const url = new URL(repoUrl);
|
||||
url.username = 'autodeployer';
|
||||
url.password = config.giteaToken;
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a Gitea repo URL into owner/name
|
||||
*/
|
||||
export function parseRepoUrl(repoUrl) {
|
||||
const url = new URL(repoUrl);
|
||||
const parts = url.pathname.replace(/^\//, '').replace(/\.git$/, '').split('/');
|
||||
if (parts.length < 2) throw new Error(`Invalid repo URL: ${repoUrl}`);
|
||||
return { owner: parts[0], name: parts[1] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Gitea connection
|
||||
*/
|
||||
export async function testConnection() {
|
||||
try {
|
||||
const user = await request('/user');
|
||||
return { ok: true, user: user.login };
|
||||
} catch (err) {
|
||||
return { ok: false, error: err.message };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user