120 lines
2.9 KiB
JavaScript
120 lines
2.9 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
CONTROL_API_TOKEN,
|
|
GITHUB_TOKEN,
|
|
GITHUB_REPO,
|
|
GITHUB_WORKFLOW,
|
|
GITHUB_REF,
|
|
} = process.env;
|
|
|
|
function json(statusCode, payload) {
|
|
return {
|
|
statusCode,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload),
|
|
};
|
|
}
|
|
|
|
function unauthorized() {
|
|
return json(401, { ok: false, error: 'unauthorized' });
|
|
}
|
|
|
|
function badRequest(message) {
|
|
return json(400, { ok: false, error: message });
|
|
}
|
|
|
|
function getAuthToken(headers) {
|
|
return headers['x-clawdinator-token'] || headers['X-Clawdinator-Token'] || null;
|
|
}
|
|
|
|
async function dispatchWorkflow(inputs) {
|
|
const repo = GITHUB_REPO || 'openclaw/clawdinators';
|
|
const workflow = GITHUB_WORKFLOW || 'fleet-deploy.yml';
|
|
const ref = GITHUB_REF || 'main';
|
|
|
|
const res = await fetch(`https://api.github.com/repos/${repo}/actions/workflows/${workflow}/dispatches`, {
|
|
method: 'POST',
|
|
headers: {
|
|
Accept: 'application/vnd.github+json',
|
|
Authorization: `Bearer ${GITHUB_TOKEN}`,
|
|
'User-Agent': 'clawdinator-control',
|
|
},
|
|
body: JSON.stringify({ ref, inputs }),
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const body = await res.text();
|
|
throw new Error(`workflow dispatch failed: ${res.status} ${body}`);
|
|
}
|
|
}
|
|
|
|
|
|
exports.handler = async (event) => {
|
|
if (!CONTROL_API_TOKEN) {
|
|
return json(500, { ok: false, error: 'missing CONTROL_API_TOKEN' });
|
|
}
|
|
|
|
const headers = event.headers || {};
|
|
const token = getAuthToken(headers);
|
|
if (!token || token !== CONTROL_API_TOKEN) {
|
|
return unauthorized();
|
|
}
|
|
|
|
let payload;
|
|
if (event && typeof event.body === 'string') {
|
|
const body = event.isBase64Encoded
|
|
? Buffer.from(event.body, 'base64').toString('utf-8')
|
|
: event.body;
|
|
try {
|
|
payload = JSON.parse(body);
|
|
} catch (err) {
|
|
return badRequest('invalid json');
|
|
}
|
|
} else if (event && typeof event === 'object') {
|
|
payload = event;
|
|
} else {
|
|
return badRequest('missing payload');
|
|
}
|
|
|
|
const action = (payload.action || '').toLowerCase();
|
|
const target = payload.target;
|
|
const caller = payload.caller;
|
|
const amiOverride = payload.ami_override || '';
|
|
const controlToken = payload.control_token || null;
|
|
|
|
if (CONTROL_API_TOKEN && controlToken !== CONTROL_API_TOKEN) {
|
|
return unauthorized();
|
|
}
|
|
|
|
if (action === 'status') {
|
|
return json(400, { ok: false, error: 'status not supported via api' });
|
|
}
|
|
|
|
if (action !== 'deploy') {
|
|
return badRequest('unsupported action');
|
|
}
|
|
|
|
if (!target) {
|
|
return badRequest('target required');
|
|
}
|
|
|
|
if (caller && target === caller) {
|
|
return badRequest('refusing self-deploy');
|
|
}
|
|
|
|
if (!GITHUB_TOKEN) {
|
|
return json(500, { ok: false, error: 'missing GITHUB_TOKEN' });
|
|
}
|
|
|
|
try {
|
|
await dispatchWorkflow({
|
|
target,
|
|
ami_override: amiOverride,
|
|
});
|
|
return json(200, { ok: true, message: `deploy queued for ${target}` });
|
|
} catch (err) {
|
|
return json(500, { ok: false, error: err.message });
|
|
}
|
|
};
|