mirror of
https://github.com/tale/headplane.git
synced 2026-08-24 11:36:31 +00:00
179 lines
4.1 KiB
TypeScript
179 lines
4.1 KiB
TypeScript
import { data, redirect } from 'react-router';
|
|
import { Capabilities } from '~/server/web/roles';
|
|
import type { Route } from './+types/machine';
|
|
|
|
export async function machineAction({ request, context }: Route.ActionArgs) {
|
|
const session = await context.sessions.auth(request);
|
|
const check = await context.sessions.check(
|
|
request,
|
|
Capabilities.write_machines,
|
|
);
|
|
|
|
const formData = await request.formData();
|
|
const api = context.hsApi.getRuntimeClient(session.api_key);
|
|
|
|
const action = formData.get('action_id')?.toString();
|
|
if (!action) {
|
|
throw data('Missing `action_id` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
// Fast track register since it doesn't require an existing machine
|
|
if (action === 'register') {
|
|
if (!check) {
|
|
throw data('You do not have permission to manage machines', {
|
|
status: 403,
|
|
});
|
|
}
|
|
|
|
const registrationKey = formData.get('register_key')?.toString();
|
|
if (!registrationKey) {
|
|
throw data('Missing `register_key` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
const user = formData.get('user')?.toString();
|
|
if (!user) {
|
|
throw data('Missing `user` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
const node = await api.registerNode(user, registrationKey);
|
|
return redirect(`/machines/${node.id}`);
|
|
}
|
|
|
|
// Check if the user has permission to manage this machine
|
|
const nodeId = formData.get('node_id')?.toString();
|
|
if (!nodeId) {
|
|
throw data('Missing `node_id` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
const node = await api.getNode(nodeId);
|
|
if (!node) {
|
|
throw data(`Machine with ID ${nodeId} not found`, {
|
|
status: 404,
|
|
});
|
|
}
|
|
|
|
if (
|
|
node.user.providerId?.split('/').pop() !== session.user.subject &&
|
|
!check
|
|
) {
|
|
throw data('You do not have permission to act on this machine', {
|
|
status: 403,
|
|
});
|
|
}
|
|
|
|
switch (action) {
|
|
case 'rename': {
|
|
const newName = formData.get('name')?.toString();
|
|
if (!newName) {
|
|
throw data('Missing `name` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
const name = String(formData.get('name'));
|
|
await api.renameNode(nodeId, name);
|
|
return { message: 'Machine renamed' };
|
|
}
|
|
|
|
case 'delete': {
|
|
await api.deleteNode(nodeId);
|
|
return redirect('/machines');
|
|
}
|
|
|
|
case 'expire': {
|
|
await api.expireNode(nodeId);
|
|
return { message: 'Machine expired' };
|
|
}
|
|
|
|
case 'update_tags': {
|
|
const tags = formData.get('tags')?.toString().split(',') ?? [];
|
|
if (tags.length === 0) {
|
|
throw data('Missing `tags` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
await api.setNodeTags(
|
|
nodeId,
|
|
tags.map((tag) => tag.trim()).filter((tag) => tag !== ''),
|
|
);
|
|
return { message: 'Tags updated' };
|
|
}
|
|
|
|
case 'update_routes': {
|
|
const newApproved = node.approvedRoutes;
|
|
const routes = formData.get('routes')?.toString();
|
|
if (!routes) {
|
|
throw data('Missing `routes` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
const allRoutes = routes.split(',').map((route) => route.trim());
|
|
if (allRoutes.length === 0) {
|
|
throw data('No routes provided to update', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
const enabled = formData.get('enabled')?.toString();
|
|
if (enabled === undefined) {
|
|
throw data('Missing `enabled` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
if (enabled === 'true') {
|
|
for (const route of allRoutes) {
|
|
// If already approved, skip, otherwise add to approved
|
|
if (newApproved.includes(route)) {
|
|
continue;
|
|
}
|
|
|
|
newApproved.push(route);
|
|
}
|
|
} else {
|
|
for (const route of allRoutes) {
|
|
// If not approved, skip, otherwise remove from approved
|
|
if (!newApproved.includes(route)) {
|
|
continue;
|
|
}
|
|
|
|
const index = newApproved.indexOf(route);
|
|
if (index > -1) {
|
|
newApproved.splice(index, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
await api.approveNodeRoutes(nodeId, newApproved);
|
|
return { message: 'Routes updated' };
|
|
}
|
|
|
|
case 'reassign': {
|
|
const user = formData.get('user_id')?.toString();
|
|
if (!user) {
|
|
throw data('Missing `user_id` in the form data.', {
|
|
status: 400,
|
|
});
|
|
}
|
|
|
|
await api.setNodeUser(nodeId, user);
|
|
return { message: 'Machine reassigned' };
|
|
}
|
|
|
|
default:
|
|
throw data('Invalid action', {
|
|
status: 400,
|
|
});
|
|
}
|
|
}
|