From 935f9d9d3d9b1b02d480872dead0e09eeebf92f4 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Tue, 14 Apr 2026 13:57:03 +0200 Subject: [PATCH] Use notes and install_methods fields Update PocketBase bot and import workflow to use direct `notes` and `install_methods` fields instead of the older `notes_json`/`install_methods_json` relations. Adjust parsing, PATCH/POST payloads and error messages accordingly, remove project_url handling, and drop the separate resolution/creation of related note/install_method records in the import script. Also update docs to reflect the new field names and clean up some formatting and allowed-field lists. --- .github/workflows/pocketbase-bot.yml | 28 +++-- .github/workflows/push_json_to_pocketbase.yml | 107 +----------------- docs/pocketbase-bot.md | 20 +++- 3 files changed, 32 insertions(+), 123 deletions(-) diff --git a/.github/workflows/pocketbase-bot.yml b/.github/workflows/pocketbase-bot.yml index ce9155d4..1154caf0 100644 --- a/.github/workflows/pocketbase-bot.yml +++ b/.github/workflows/pocketbase-bot.yml @@ -168,7 +168,7 @@ jobs: '/pocketbase method hdd=10\n' + '/pocketbase method cpu=4 ram=2048 hdd=20\n' + '```\n\n' + - '**Editable fields:** `name` `description` `logo` `documentation` `website` `project_url` `github` ' + + '**Editable fields:** `name` `description` `logo` `documentation` `website` `github` ' + '`config_path` `port` `default_user` `default_passwd` ' + '`updateable` `privileged` `has_arm` `is_dev` ' + '`is_disabled` `disable_message` `is_deleted` `deleted_message`'; @@ -234,15 +234,15 @@ jobs: const setMatch = rest.match(/^set\s+(\S+)/i); if (noteMatch) { - // ── NOTE SUBCOMMAND (reads/writes notes_json on script record) ──── + // ── NOTE SUBCOMMAND (reads/writes notes on script record) ──── const noteAction = noteMatch[1].toLowerCase(); const noteArgsStr = rest.substring(noteMatch[0].length).trim(); - // Parse notes_json from the already-fetched script record + // Parse notes from the already-fetched script record // PocketBase may return JSON fields as already-parsed objects let notesArr = []; try { - const rawNotes = record.notes_json; + const rawNotes = record.notes; notesArr = Array.isArray(rawNotes) ? rawNotes : JSON.parse(rawNotes || '[]'); } catch (e) { notesArr = []; } @@ -282,11 +282,11 @@ jobs: const res = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, - body: JSON.stringify({ notes_json: JSON.stringify(arr) }) + body: JSON.stringify({ notes: arr }) }); if (!res.ok) { await addReaction('-1'); - await postComment('❌ **PocketBase Bot**: Failed to update `notes_json`:\n```\n' + res.body + '\n```'); + await postComment('❌ **PocketBase Bot**: Failed to update `notes`:\n```\n' + res.body + '\n```'); process.exit(1); } } @@ -392,15 +392,15 @@ jobs: } } else if (methodMatch) { - // ── METHOD SUBCOMMAND (reads/writes install_methods_json on script record) ── + // ── METHOD SUBCOMMAND (reads/writes install_methods on script record) ── const methodArgs = rest.replace(/^method\s*/i, '').trim(); const methodListMode = !methodArgs || methodArgs.toLowerCase() === 'list'; - // Parse install_methods_json from the already-fetched script record + // Parse install_methods from the already-fetched script record // PocketBase may return JSON fields as already-parsed objects let methodsArr = []; try { - const rawMethods = record.install_methods_json; + const rawMethods = record.install_methods; methodsArr = Array.isArray(rawMethods) ? rawMethods : JSON.parse(rawMethods || '[]'); } catch (e) { methodsArr = []; } @@ -417,11 +417,11 @@ jobs: const res = await request(recordsUrl + '/' + record.id, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, - body: JSON.stringify({ install_methods_json: JSON.stringify(arr) }) + body: JSON.stringify({ install_methods: arr }) }); if (!res.ok) { await addReaction('-1'); - await postComment('❌ **PocketBase Bot**: Failed to update `install_methods_json`:\n```\n' + res.body + '\n```'); + await postComment('❌ **PocketBase Bot**: Failed to update `install_methods`:\n```\n' + res.body + '\n```'); process.exit(1); } } @@ -499,7 +499,7 @@ jobs: const fieldName = setMatch[1].toLowerCase(); const SET_ALLOWED = { name: 'string', description: 'string', logo: 'string', - documentation: 'string', website: 'string', project_url: 'string', github: 'string', + documentation: 'string', website: 'string', github: 'string', config_path: 'string', disable_message: 'string', deleted_message: 'string' }; if (!SET_ALLOWED[fieldName]) { @@ -544,15 +544,13 @@ jobs: const fieldsStr = rest; // Skipped: slug, script_created/updated, created (auto), categories/ - // install_methods/notes/type (relations), github_data/install_methods_json/ - // notes_json (auto-generated), execute_in (select relation), last_update_commit (auto) + // type (relation), github_data (auto-generated), execute_in (select), last_update_commit (auto) const ALLOWED_FIELDS = { name: 'string', description: 'string', logo: 'string', documentation: 'string', website: 'string', - project_url: 'string', github: 'string', config_path: 'string', port: 'number', diff --git a/.github/workflows/push_json_to_pocketbase.yml b/.github/workflows/push_json_to_pocketbase.yml index ad3d0ddd..b99587ab 100644 --- a/.github/workflows/push_json_to_pocketbase.yml +++ b/.github/workflows/push_json_to_pocketbase.yml @@ -159,43 +159,7 @@ jobs: (catData.items || []).forEach(function(item) { if (item.name) categoryNameToPbId[item.name] = item.id; }); } } catch (e) { console.warn('Could not fetch script_categories:', e.message); } - var noteTypeToId = {}; - var installMethodTypeToId = {}; - var osToId = {}; - var osVersionToId = {}; - try { - const res = await request(apiBase + '/collections/z_ref_note_types/records?perPage=500', { headers: { 'Authorization': token } }); - if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { - if (item.type != null) { noteTypeToId[item.type] = item.id; noteTypeToId[item.type.toLowerCase()] = item.id; } - }); - } catch (e) { console.warn('z_ref_note_types:', e.message); } - try { - const res = await request(apiBase + '/collections/z_ref_install_method_types/records?perPage=500', { headers: { 'Authorization': token } }); - if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { - if (item.type != null) { installMethodTypeToId[item.type] = item.id; installMethodTypeToId[item.type.toLowerCase()] = item.id; } - }); - } catch (e) { console.warn('z_ref_install_method_types:', e.message); } - try { - const res = await request(apiBase + '/collections/z_ref_os/records?perPage=500', { headers: { 'Authorization': token } }); - if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { - if (item.os != null) { osToId[item.os] = item.id; osToId[item.os.toLowerCase()] = item.id; } - }); - } catch (e) { console.warn('z_ref_os:', e.message); } - try { - const res = await request(apiBase + '/collections/z_ref_os_version/records?perPage=500&expand=os', { headers: { 'Authorization': token } }); - if (res.ok) { - (JSON.parse(res.body).items || []).forEach(function(item) { - var osName = item.expand && item.expand.os && item.expand.os.os != null ? item.expand.os.os : null; - if (osName != null && item.version != null) { - var key = osName + '|' + item.version; - osVersionToId[key] = item.id; - osVersionToId[osName.toLowerCase() + '|' + item.version] = item.id; - } - }); - } - } catch (e) { console.warn('z_ref_os_version:', e.message); } - var notesCollUrl = apiBase + '/collections/script_notes/records'; - var installMethodsCollUrl = apiBase + '/collections/script_install_methods/records'; + for (const file of files) { if (!fs.existsSync(file)) continue; const data = JSON.parse(fs.readFileSync(file, 'utf8')); @@ -205,7 +169,6 @@ jobs: var executeIn = data.type ? (executeInMap[data.type.toLowerCase()] || null) : null; // github: extract owner/repo from full GitHub URL var githubField = null; - var projectUrl = data.github || null; if (data.github) { var ghMatch = data.github.match(/github\.com\/([^/]+\/[^/?#]+)/); if (ghMatch) githubField = ghMatch[1].replace(/\.git$/, ''); @@ -244,13 +207,12 @@ jobs: config_path: data.config_path, default_user: (data.default_credentials && data.default_credentials.username) || data.default_user || null, default_passwd: (data.default_credentials && data.default_credentials.password) || data.default_passwd || null, - notes_json: JSON.stringify(data.notes || []), - install_methods_json: JSON.stringify(data.install_methods || []), + notes: data.notes || [], + install_methods: data.install_methods || [], is_dev: true }; if (executeIn) payload.execute_in = executeIn; if (githubField) payload.github = githubField; - if (projectUrl) payload.project_url = projectUrl; if (lastCommit) payload.last_update_commit = lastCommit; var resolvedType = typeValueToId[data.type]; if (resolvedType == null && data.type === 'ct') resolvedType = typeValueToId['lxc']; @@ -266,56 +228,8 @@ jobs: }); const list = JSON.parse(listRes.body); const existingId = list.items && list.items[0] && list.items[0].id; - async function resolveNotesAndInstallMethods(scriptId) { - var noteIds = []; - for (var i = 0; i < (data.notes || []).length; i++) { - var note = data.notes[i]; - var typeId = noteTypeToId[note.type] || (note.type && noteTypeToId[note.type.toLowerCase()]); - if (typeId == null) { - console.warn('Note type not in z_ref_note_types:', note.type); - continue; - } - var postRes = await request(notesCollUrl, { - method: 'POST', - headers: { 'Authorization': token, 'Content-Type': 'application/json' }, - body: JSON.stringify({ text: note.text || '', type: typeId, script: scriptId }) - }); - if (postRes.ok) noteIds.push(JSON.parse(postRes.body).id); - else console.error('script_notes POST failed:', postRes.statusCode, postRes.body); - } - var installMethodIds = []; - for (var j = 0; j < (data.install_methods || []).length; j++) { - var im = data.install_methods[j]; - var typeId = installMethodTypeToId[im.type] || (im.type && installMethodTypeToId[im.type.toLowerCase()]); - var res = im.resources || {}; - var osId = osToId[res.os] || (res.os && osToId[res.os.toLowerCase()]); - var osVersionKey = (res.os != null && res.version != null) ? res.os + '|' + res.version : null; - var osVersionId = osVersionKey ? (osVersionToId[osVersionKey] || osVersionToId[res.os.toLowerCase() + '|' + res.version]) : null; - var imBody = { - script: scriptId, - resources_cpu: res.cpu != null ? res.cpu : 0, - resources_ram: res.ram != null ? res.ram : 0, - resources_hdd: res.hdd != null ? res.hdd : 0 - }; - if (typeId) imBody.type = typeId; - if (osId) imBody.os = osId; - if (osVersionId) imBody.os_version = osVersionId; - var imPostRes = await request(installMethodsCollUrl, { - method: 'POST', - headers: { 'Authorization': token, 'Content-Type': 'application/json' }, - body: JSON.stringify(imBody) - }); - if (imPostRes.ok) installMethodIds.push(JSON.parse(imPostRes.body).id); - else console.error('script_install_methods POST failed:', imPostRes.statusCode, imPostRes.body); - } - return { noteIds: noteIds, installMethodIds: installMethodIds }; - } if (existingId) { - var resolved = await resolveNotesAndInstallMethods(existingId); - payload.notes = resolved.noteIds; - payload.install_methods = resolved.installMethodIds; console.log('Updating', file, '(slug=' + data.slug + ')'); - console.log('Created', resolved.noteIds.length, 'notes,', resolved.installMethodIds.length, 'install_methods for slug=' + data.slug); const r = await request(recordsUrl + '/' + existingId, { method: 'PATCH', headers: { 'Authorization': token, 'Content-Type': 'application/json' }, @@ -330,21 +244,6 @@ jobs: body: JSON.stringify(payload) }); if (!r.ok) throw new Error('POST failed: ' + r.body); - var scriptId = JSON.parse(r.body).id; - var resolved = await resolveNotesAndInstallMethods(scriptId); - console.log('Created', resolved.noteIds.length, 'notes,', resolved.installMethodIds.length, 'install_methods for slug=' + data.slug); - var patchPayload = {}; - for (var k in payload) patchPayload[k] = payload[k]; - patchPayload.notes = resolved.noteIds; - patchPayload.install_methods = resolved.installMethodIds; - var patchRes = await request(recordsUrl + '/' + scriptId, { - method: 'PATCH', - headers: { 'Authorization': token, 'Content-Type': 'application/json' }, - body: JSON.stringify(patchPayload) - }); - if (!patchRes.ok) throw new Error('PATCH relations failed: ' + patchRes.body); - var patched = JSON.parse(patchRes.body); - console.log('Script linked: notes=' + (patched.notes && patched.notes.length) + ', install_methods=' + (patched.install_methods && patched.install_methods.length)); } } console.log('Done.'); diff --git a/docs/pocketbase-bot.md b/docs/pocketbase-bot.md index 12539ba8..87a97576 100644 --- a/docs/pocketbase-bot.md +++ b/docs/pocketbase-bot.md @@ -6,16 +6,20 @@ --- ### 🔧 Field Updates + Simple key=value pairs. Multiple in one line. + ``` /pocketbase field=value [field=value ...] ``` + **Boolean fields** (`true`/`false`): `updateable` `privileged` `has_arm` `is_dev` `is_disabled` `is_deleted` -**Text fields**: `name` `description` `logo` `documentation` `website` `project_url` `github` `config_path` `disable_message` `deleted_message` +**Text fields**: `name` `description` `logo` `documentation` `website` `github` `config_path` `disable_message` `deleted_message` **Number**: `port` -**Nullable**: `default_user` `default_passwd` *(empty value = null: `default_passwd=`)* +**Nullable**: `default_user` `default_passwd` _(empty value = null: `default_passwd=`)_ **Examples:** + ``` /pocketbase homeassistant is_disabled=true disable_message="Broken upstream" /pocketbase homeassistant documentation=https://www.home-assistant.io/docs @@ -26,40 +30,48 @@ Simple key=value pairs. Multiple in one line. --- ### 📝 set — HTML / Multiline / Special Characters + Use a code block for values that contain HTML, links, quotes or newlines. + ```` /pocketbase set ``` Your content here — HTML tags, links, quotes, all fine ``` ```` -**Allowed fields:** `name` `description` `logo` `documentation` `website` `project_url` `github` `config_path` `disable_message` `deleted_message` + +**Allowed fields:** `name` `description` `logo` `documentation` `website` `github` `config_path` `disable_message` `deleted_message` --- ### 🗒️ Notes + ``` /pocketbase note list /pocketbase note add "" /pocketbase note edit "" "" /pocketbase note remove "" ``` -Note types come from `z_ref_note_types` in PocketBase (e.g. `info`, `warning`). + +Note types: `info`, `warning`. If text doesn't match exactly, the bot lists all current notes automatically. --- ### ⚙️ Install Method Resources + ``` /pocketbase method list /pocketbase method hdd=10 /pocketbase method cpu=4 ram=2048 hdd=20 ``` + `` matches the install method type name (e.g. `default`, `alpine`). Use `method list` to see available types and current values. `ram` = MB, `hdd` = GB. --- ### 💡 Tips + - The bot reacts with 👀 when it picks up the command, ✅ on success, 👎 on error - On any error, a comment explains what went wrong - `note edit` / `note remove` show the current note list if the text doesn't match