feat(api, web): implement short install link functionality for agents
Build and Push EvoFirewall Docker Image / build-and-push (push) Successful in 1m37s
Build and Push EvoFirewall Docker Image / create-release (push) Skipped

- Added new API endpoints for creating, retrieving, and revoking install links for agents.
- Enhanced the agent installation process with short links accessible via `/agent-install/:id` and `/:slug`.
- Updated the README and documentation to reflect the new installation method and usage instructions.
- Refactored relevant components in the web application to support the new install link feature.
- Improved error handling and validation for install link operations.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-21 01:25:05 +07:00
co-authored by Cursor
parent 14f516d5cc
commit ef56da4d91
13 changed files with 677 additions and 117 deletions
+40
View File
@@ -5,6 +5,7 @@ import {
createIpListBodySchema,
createPolicyRuleBodySchema,
createPolicySetBodySchema,
createInstallLinkBodySchema,
patchPolicySetBodySchema,
putAgentPolicySetsBodySchema,
patchAgentBodySchema,
@@ -25,6 +26,10 @@ import {
resolveAndStoreHostnameRule,
resolveHostnameToCidrs,
} from '../services/policy/resolve-hostname.js'
import {
mapInstallLink,
randomToken,
} from '../services/install-links.js'
import type { AppConfig } from '../config.js'
function mapAgent(a: NonNullable<ReturnType<typeof repos.getAgent>>) {
@@ -134,6 +139,41 @@ export const controlRoutes: FastifyPluginAsync<{ config: AppConfig }> = async (
}
})
// Install short-links
app.get('/install-links', async () => ({
items: repos
.listInstallLinks(app.db)
.map((row) => mapInstallLink(row, config.publicBaseUrl)),
}))
app.post('/install-links', async (req, reply) => {
const body = createInstallLinkBodySchema.parse(req.body)
const id = randomToken(10)
const slug = randomToken(16)
if (repos.getInstallLink(app.db, id) || repos.getInstallLinkBySlug(app.db, slug)) {
throw new AppError('CONFLICT', 'Retry create (id collision)', 409)
}
const row = repos.insertInstallLink(app.db, {
id,
slug,
clientName: body.name.trim(),
platform: body.platform ?? 'linux',
createdAt: new Date().toISOString(),
useCount: 0,
})
return reply.code(201).send(mapInstallLink(row!, config.publicBaseUrl))
})
app.delete<{ Params: { id: string } }>(
'/install-links/:id',
async (req) => {
const row = repos.getInstallLink(app.db, req.params.id)
if (!row) throw new AppError('NOT_FOUND', 'Install link not found', 404)
const updated = repos.revokeInstallLink(app.db, row.id)
return mapInstallLink(updated!, config.publicBaseUrl)
},
)
// Agents
app.get('/agents', async () => ({
items: repos.listAgents(app.db).map(mapAgent),