From 5af0c623b2ff4e500bd2f8e9da8ff9aa61e7bf07 Mon Sep 17 00:00:00 2001 From: KernelDeimos <7225168+KernelDeimos@users.noreply.github.com> Date: Mon, 2 Mar 2026 15:29:58 -0500 Subject: [PATCH] dev(extension): [+] test-actions Adds an extension that presents an unauthenticated page for testing various actions of Puter when it's under development. This action is only meant to be used in development and is not loaded by default. --- mods/mods_available/test-actions/main.js | 138 ++++++++++++++++++ mods/mods_available/test-actions/package.json | 8 + 2 files changed, 146 insertions(+) create mode 100644 mods/mods_available/test-actions/main.js create mode 100644 mods/mods_available/test-actions/package.json diff --git a/mods/mods_available/test-actions/main.js b/mods/mods_available/test-actions/main.js new file mode 100644 index 0000000000..3d234f151a --- /dev/null +++ b/mods/mods_available/test-actions/main.js @@ -0,0 +1,138 @@ +/* + * Test-actions extension: declarative actions page for testing user suspension + * and other admin actions. All changes in this single file. + */ + +const { db } = extension.import('data'); +const { invalidate_cached_user } = use('core.util.helpers'); + +// Declarative actions: id, label, and inputs drive the generated GUI. +const ACTIONS = [ + { + id: 'suspend-user', + label: 'Suspend user', + inputs: [ + { name: 'username', label: 'Username', type: 'text' }, + ], + }, + // Add more actions here; each needs a handler in INVOKE_HANDLERS. +]; + +// Handlers for each action id. Receives (req, res, body). +const INVOKE_HANDLERS = { + 'suspend-user': async (req, res, body) => { + const username = body?.username?.trim(); + if ( ! username ) { + return res.status(400).json({ ok: false, error: 'username is required' }); + } + const svc_get_user = req.services.get('get-user'); + const user = await svc_get_user.get_user({ username }); + if ( ! user ) { + return res.status(404).json({ ok: false, error: 'User not found' }); + } + await db.write('UPDATE `user` SET suspended = 1 WHERE id = ? LIMIT 1', [user.id]); + invalidate_cached_user(user); + // Cache invalidation would require backend helpers (ESM); skipped here. + return res.json({ ok: true, message: `User "${username}" suspended.` }); + }, +}; + +const PAGE_HTML = (actionsJson) => ` + + + + Test actions + + + +

Test actions

+
+ + + +`; + +extension.get('/test-actions', (req, res) => { + res.setHeader('Content-Type', 'text/html; charset=utf-8'); + res.send(PAGE_HTML(JSON.stringify(ACTIONS))); +}); + +extension.post('/test-actions/invoke/:actionId', async (req, res) => { + const actionId = req.params.actionId; + const handler = INVOKE_HANDLERS[actionId]; + if ( ! handler ) { + return res.status(404).json({ ok: false, error: 'Unknown action' }); + } + return handler(req, res, req.body || {}); +}); + +extension.on('ai.prompt.validate', async event => { + console.log('ai.prompt.validate'); + const messages = event.parameters?.messages ?? []; + console.log(`ai prompt validate: ${messages.length} messages`); + + console.log('is user suspended?', event.actor.type.user.suspended); +}); diff --git a/mods/mods_available/test-actions/package.json b/mods/mods_available/test-actions/package.json new file mode 100644 index 0000000000..d75646375c --- /dev/null +++ b/mods/mods_available/test-actions/package.json @@ -0,0 +1,8 @@ +{ + "name": "@heyputer/test-actions", + "version": "1.0.0", + "description": "Actions for test purposes", + "main": "main.js", + "type": "module", + "private": true +} \ No newline at end of file