Compare commits
13 Commits
feature/po
...
delete_fil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
099debeda2 | ||
|
|
b76e25910d | ||
|
|
34250d6adf | ||
|
|
7738065237 | ||
|
|
b833eb68eb | ||
|
|
cc8dd29f63 | ||
|
|
4b3d9366d2 | ||
|
|
0527db122b | ||
|
|
b689917eee | ||
|
|
8bb2f28b48 | ||
|
|
71c02e052b | ||
|
|
105dceb42e | ||
|
|
8ba7b55cd5 |
19
.github/workflows/move-to-main-repo.yaml
generated
vendored
19
.github/workflows/move-to-main-repo.yaml
generated
vendored
@@ -39,11 +39,22 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
echo "Filtering Issues with Label Migration To ProxmoxVE"
|
||||
raw_output=$(gh issue list --json title,labels,number,body)
|
||||
filtered_issue=$(echo "$raw_output" | jq -r '[.[] | select(.labels[]?.name == "Migration To ProxmoxVE")][0]')
|
||||
echo "Resolving issue with label Migration To ProxmoxVE"
|
||||
|
||||
if [ "$filtered_issue" == "null" ] || [ -z "$filtered_issue" ]; then
|
||||
if [[ "${{ github.event_name }}" == "issues" ]]; then
|
||||
# For labeled issue events, use the exact issue from event payload.
|
||||
filtered_issue='${{ toJson(github.event.issue) }}'
|
||||
else
|
||||
# Fallback for workflow_dispatch: query explicitly by label and raise limit.
|
||||
raw_output=$(gh issue list \
|
||||
--label "Migration To ProxmoxVE" \
|
||||
--state open \
|
||||
--limit 500 \
|
||||
--json title,labels,number,body)
|
||||
filtered_issue=$(echo "$raw_output" | jq -c '.[0]')
|
||||
fi
|
||||
|
||||
if [[ "$filtered_issue" == "null" ]] || [[ -z "$filtered_issue" ]]; then
|
||||
echo "No issues found with label 'Migration To ProxmoxVE'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
502
.github/workflows/pocketbase-bot.yml
generated
vendored
502
.github/workflows/pocketbase-bot.yml
generated
vendored
@@ -234,36 +234,204 @@ jobs:
|
||||
const setMatch = rest.match(/^set\s+(\S+)/i);
|
||||
|
||||
if (noteMatch) {
|
||||
// ── NOTE SUBCOMMAND ──────────────────────────────────────────────
|
||||
} else if (methodMatch) {
|
||||
// ── METHOD SUBCOMMAND ────────────────────────────────────────────
|
||||
const methodArgs = rest.replace(/^method\s*/i, '').trim();
|
||||
const methodListMode = !methodArgs || methodArgs.toLowerCase() === 'list';
|
||||
// ── NOTE SUBCOMMAND (reads/writes notes_json on script record) ────
|
||||
const noteAction = noteMatch[1].toLowerCase();
|
||||
const noteArgsStr = rest.substring(noteMatch[0].length).trim();
|
||||
|
||||
// Helper: format bytes/numbers nicely
|
||||
function fmtMethod(im) {
|
||||
const r = im.resources || {};
|
||||
const typeLabel = im.expand && im.expand.type ? (im.expand.type.type || im.expand.type.name || im.expand.type.value || im.type) : im.type;
|
||||
return '**`' + (typeLabel || '(unknown type)') + '`** — CPU: `' + (r.cpu != null ? r.cpu : im.resources_cpu || 0) + '` · RAM: `' + (r.ram != null ? r.ram : im.resources_ram || 0) + ' MB` · HDD: `' + (r.hdd != null ? r.hdd : im.resources_hdd || 0) + ' GB`';
|
||||
// Parse notes_json from the already-fetched script record
|
||||
// PocketBase may return JSON fields as already-parsed objects
|
||||
let notesArr = [];
|
||||
try {
|
||||
const rawNotes = record.notes_json;
|
||||
notesArr = Array.isArray(rawNotes) ? rawNotes : JSON.parse(rawNotes || '[]');
|
||||
} catch (e) { notesArr = []; }
|
||||
|
||||
// Token parser: unquoted-word OR "quoted string" (supports \" escapes)
|
||||
function parseNoteTokens(str) {
|
||||
const tokens = [];
|
||||
let pos = 0;
|
||||
while (pos < str.length) {
|
||||
while (pos < str.length && /\s/.test(str[pos])) pos++;
|
||||
if (pos >= str.length) break;
|
||||
if (str[pos] === '"') {
|
||||
pos++;
|
||||
let start = pos;
|
||||
while (pos < str.length && str[pos] !== '"') {
|
||||
if (str[pos] === '\\') pos++;
|
||||
pos++;
|
||||
}
|
||||
tokens.push(str.substring(start, pos).replace(/\\"/g, '"'));
|
||||
if (pos < str.length) pos++;
|
||||
} else {
|
||||
let start = pos;
|
||||
while (pos < str.length && !/\s/.test(str[pos])) pos++;
|
||||
tokens.push(str.substring(start, pos));
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// Fetch expanded install_methods
|
||||
const expRes = await request(recordsUrl + '/' + record.id + '?expand=install_methods,install_methods.type', { headers: { 'Authorization': token } });
|
||||
const expRec = JSON.parse(expRes.body);
|
||||
const installMethods = (expRec.expand && expRec.expand.install_methods) || [];
|
||||
function formatNotesList(arr) {
|
||||
if (arr.length === 0) return '*None*';
|
||||
return arr.map(function (n, i) {
|
||||
return (i + 1) + '. **`' + (n.type || '?') + '`**: ' + (n.text || '');
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
async function patchNotesJson(arr) {
|
||||
const res = await request(recordsUrl + '/' + record.id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ notes_json: JSON.stringify(arr) })
|
||||
});
|
||||
if (!res.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: Failed to update `notes_json`:\n```\n' + res.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (noteAction === 'list') {
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'ℹ️ **PocketBase Bot**: Notes for **`' + slug + '`** (' + notesArr.length + ' total)\n\n' +
|
||||
formatNotesList(notesArr)
|
||||
);
|
||||
|
||||
} else if (noteAction === 'add') {
|
||||
const tokens = parseNoteTokens(noteArgsStr);
|
||||
if (tokens.length < 2) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: `note add` requires `<type>` and `"<text>"`.\n\n' +
|
||||
'**Usage:** `/pocketbase ' + slug + ' note add <type> "<text>"`'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const noteType = tokens[0].toLowerCase();
|
||||
const noteText = tokens.slice(1).join(' ');
|
||||
notesArr.push({ type: noteType, text: noteText });
|
||||
await patchNotesJson(notesArr);
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'✅ **PocketBase Bot**: Added note to **`' + slug + '`**\n\n' +
|
||||
'- **Type:** `' + noteType + '`\n' +
|
||||
'- **Text:** ' + noteText + '\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
|
||||
} else if (noteAction === 'edit') {
|
||||
const tokens = parseNoteTokens(noteArgsStr);
|
||||
if (tokens.length < 3) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: `note edit` requires `<type>`, `"<old text>"`, and `"<new text>"`.\n\n' +
|
||||
'**Usage:** `/pocketbase ' + slug + ' note edit <type> "<old text>" "<new text>"`\n\n' +
|
||||
'Use `/pocketbase ' + slug + ' note list` to see current notes.'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const noteType = tokens[0].toLowerCase();
|
||||
const oldText = tokens[1];
|
||||
const newText = tokens[2];
|
||||
const idx = notesArr.findIndex(function (n) {
|
||||
return n.type.toLowerCase() === noteType && n.text === oldText;
|
||||
});
|
||||
if (idx === -1) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: No `' + noteType + '` note found with that exact text.\n\n' +
|
||||
'**Current notes for `' + slug + '`:**\n' + formatNotesList(notesArr)
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
notesArr[idx].text = newText;
|
||||
await patchNotesJson(notesArr);
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'✅ **PocketBase Bot**: Edited note in **`' + slug + '`**\n\n' +
|
||||
'- **Type:** `' + noteType + '`\n' +
|
||||
'- **Old:** ' + oldText + '\n' +
|
||||
'- **New:** ' + newText + '\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
|
||||
} else if (noteAction === 'remove') {
|
||||
const tokens = parseNoteTokens(noteArgsStr);
|
||||
if (tokens.length < 2) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: `note remove` requires `<type>` and `"<text>"`.\n\n' +
|
||||
'**Usage:** `/pocketbase ' + slug + ' note remove <type> "<text>"`\n\n' +
|
||||
'Use `/pocketbase ' + slug + ' note list` to see current notes.'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const noteType = tokens[0].toLowerCase();
|
||||
const noteText = tokens[1];
|
||||
const before = notesArr.length;
|
||||
notesArr = notesArr.filter(function (n) {
|
||||
return !(n.type.toLowerCase() === noteType && n.text === noteText);
|
||||
});
|
||||
if (notesArr.length === before) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: No `' + noteType + '` note found with that exact text.\n\n' +
|
||||
'**Current notes for `' + slug + '`:**\n' + formatNotesList(notesArr)
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
await patchNotesJson(notesArr);
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'✅ **PocketBase Bot**: Removed note from **`' + slug + '`**\n\n' +
|
||||
'- **Type:** `' + noteType + '`\n' +
|
||||
'- **Text:** ' + noteText + '\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
}
|
||||
|
||||
} else if (methodMatch) {
|
||||
// ── METHOD SUBCOMMAND (reads/writes install_methods_json 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
|
||||
// PocketBase may return JSON fields as already-parsed objects
|
||||
let methodsArr = [];
|
||||
try {
|
||||
const rawMethods = record.install_methods_json;
|
||||
methodsArr = Array.isArray(rawMethods) ? rawMethods : JSON.parse(rawMethods || '[]');
|
||||
} catch (e) { methodsArr = []; }
|
||||
|
||||
function formatMethodsList(arr) {
|
||||
if (arr.length === 0) return '*None*';
|
||||
return arr.map(function (im, i) {
|
||||
const r = im.resources || {};
|
||||
return (i + 1) + '. **`' + (im.type || '?') + '`** — CPU: `' + (r.cpu != null ? r.cpu : '?') +
|
||||
'` · RAM: `' + (r.ram != null ? r.ram : '?') + ' MB` · HDD: `' + (r.hdd != null ? r.hdd : '?') + ' GB`';
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
async function patchInstallMethodsJson(arr) {
|
||||
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) })
|
||||
});
|
||||
if (!res.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: Failed to update `install_methods_json`:\n```\n' + res.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (methodListMode) {
|
||||
await addReaction('+1');
|
||||
if (installMethods.length === 0) {
|
||||
await postComment('ℹ️ **PocketBase Bot**: No install methods found for **`' + slug + '`**.');
|
||||
} else {
|
||||
const lines = installMethods.map(function (im, i) {
|
||||
const r = im.resources || {};
|
||||
const typeLabel = im.expand && im.expand.type ? (im.expand.type.type || im.expand.type.name || im.expand.type.value || '') : '';
|
||||
return (i + 1) + '. **`' + (typeLabel || im.type || im.id) + '`** — CPU: `' + (im.resources_cpu || 0) + '` · RAM: `' + (im.resources_ram || 0) + ' MB` · HDD: `' + (im.resources_hdd || 0) + ' GB`';
|
||||
}).join('\n');
|
||||
await postComment('ℹ️ **PocketBase Bot**: Install methods for **`' + slug + '`** (' + installMethods.length + ' total)\n\n' + lines);
|
||||
}
|
||||
await postComment(
|
||||
'ℹ️ **PocketBase Bot**: Install methods for **`' + slug + '`** (' + methodsArr.length + ' total)\n\n' +
|
||||
formatMethodsList(methodsArr)
|
||||
);
|
||||
} else {
|
||||
// Parse: <type> cpu=N ram=N hdd=N
|
||||
const methodParts = methodArgs.match(/^(\S+)\s+(.+)$/);
|
||||
@@ -275,7 +443,7 @@ jobs:
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const targetType = methodParts[1].toLowerCase();
|
||||
const targetType = methodParts[1].toLowerCase();
|
||||
const resourcesStr = methodParts[2];
|
||||
|
||||
// Parse resource fields (only cpu/ram/hdd allowed)
|
||||
@@ -293,50 +461,34 @@ jobs:
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Find matching install method by type name/value
|
||||
const matchedMethod = installMethods.find(function (im) {
|
||||
const typeLabel = im.expand && im.expand.type ?
|
||||
(im.expand.type.type || im.expand.type.name || im.expand.type.value || '') : '';
|
||||
return typeLabel.toLowerCase() === targetType || (im.type && im.type.toLowerCase && im.type.toLowerCase() === targetType);
|
||||
// Find matching method by type name (case-insensitive)
|
||||
const idx = methodsArr.findIndex(function (im) {
|
||||
return (im.type || '').toLowerCase() === targetType;
|
||||
});
|
||||
|
||||
if (!matchedMethod) {
|
||||
if (idx === -1) {
|
||||
await addReaction('-1');
|
||||
const availableTypes = installMethods.map(function (im) {
|
||||
return im.expand && im.expand.type ? (im.expand.type.type || im.expand.type.name || im.expand.type.value || im.type || im.id) : (im.type || im.id);
|
||||
});
|
||||
const availableTypes = methodsArr.map(function (im) { return im.type || '?'; });
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: No install method with type `' + targetType + '` found for `' + slug + '`.\n\n' +
|
||||
'**Available types:** `' + availableTypes.join('`, `') + '`\n\n' +
|
||||
'**Available types:** `' + (availableTypes.length ? availableTypes.join('`, `') : '(none)') + '`\n\n' +
|
||||
'Use `/pocketbase ' + slug + ' method list` to see all methods.'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Build patch payload for script_install_methods record
|
||||
const imPatch = {};
|
||||
if (resourceChanges.cpu != null) imPatch.resources_cpu = resourceChanges.cpu;
|
||||
if (resourceChanges.ram != null) imPatch.resources_ram = resourceChanges.ram;
|
||||
if (resourceChanges.hdd != null) imPatch.resources_hdd = resourceChanges.hdd;
|
||||
if (!methodsArr[idx].resources) methodsArr[idx].resources = {};
|
||||
if (resourceChanges.cpu != null) methodsArr[idx].resources.cpu = resourceChanges.cpu;
|
||||
if (resourceChanges.ram != null) methodsArr[idx].resources.ram = resourceChanges.ram;
|
||||
if (resourceChanges.hdd != null) methodsArr[idx].resources.hdd = resourceChanges.hdd;
|
||||
|
||||
await patchInstallMethodsJson(methodsArr);
|
||||
|
||||
const imPatchRes = await request(apiBase + '/collections/script_install_methods/records/' + matchedMethod.id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(imPatch)
|
||||
});
|
||||
if (!imPatchRes.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: PATCH failed for install method:\n```\n' + imPatchRes.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
const typeLabel = matchedMethod.expand && matchedMethod.expand.type ?
|
||||
(matchedMethod.expand.type.type || matchedMethod.expand.type.name || matchedMethod.expand.type.value || targetType) : targetType;
|
||||
const changesLines = Object.entries(resourceChanges)
|
||||
.map(function ([k, v]) { return '- `' + k + '` → `' + v + (k === 'ram' ? ' MB' : k === 'hdd' ? ' GB' : '') + '`'; })
|
||||
.join('\n');
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'✅ **PocketBase Bot**: Updated install method **`' + typeLabel + '`** for **`' + slug + '`**\n\n' +
|
||||
'✅ **PocketBase Bot**: Updated install method **`' + methodsArr[idx].type + '`** for **`' + slug + '`**\n\n' +
|
||||
'**Changes applied:**\n' + changesLines + '\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
@@ -386,247 +538,6 @@ jobs:
|
||||
'**Value set:**\n```\n' + preview + '\n```\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
const noteArgsStr = rest.substring(noteMatch[0].length).trim();
|
||||
|
||||
// Load note types from PocketBase
|
||||
const noteTypeToId = {};
|
||||
const noteTypeToName = {};
|
||||
try {
|
||||
const ntRes = await request(apiBase + '/collections/z_ref_note_types/records?perPage=500', { headers: { 'Authorization': token } });
|
||||
if (ntRes.ok) {
|
||||
JSON.parse(ntRes.body).items.forEach(function (item) {
|
||||
if (item.type != null) {
|
||||
noteTypeToId[item.type.toLowerCase()] = item.id;
|
||||
noteTypeToName[item.id] = item.type;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) { console.warn('z_ref_note_types:', e.message); }
|
||||
|
||||
const VALID_NOTE_TYPES = Object.keys(noteTypeToId);
|
||||
|
||||
// Token parser: unquoted-word OR "quoted string" (supports \" escapes)
|
||||
function parseNoteTokens(str) {
|
||||
const tokens = [];
|
||||
let pos = 0;
|
||||
while (pos < str.length) {
|
||||
while (pos < str.length && /\s/.test(str[pos])) pos++;
|
||||
if (pos >= str.length) break;
|
||||
if (str[pos] === '"') {
|
||||
pos++;
|
||||
let start = pos;
|
||||
while (pos < str.length && str[pos] !== '"') {
|
||||
if (str[pos] === '\\') pos++;
|
||||
pos++;
|
||||
}
|
||||
tokens.push(str.substring(start, pos).replace(/\\"/g, '"'));
|
||||
if (pos < str.length) pos++;
|
||||
} else {
|
||||
let start = pos;
|
||||
while (pos < str.length && !/\s/.test(str[pos])) pos++;
|
||||
tokens.push(str.substring(start, pos));
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// Helper: fetch record with expanded notes relation
|
||||
async function fetchExpandedNotes() {
|
||||
const r = await request(recordsUrl + '/' + record.id + '?expand=notes', { headers: { 'Authorization': token } });
|
||||
const rec = JSON.parse(r.body);
|
||||
return { rec, notes: (rec.expand && rec.expand.notes) || [] };
|
||||
}
|
||||
|
||||
// Helper: format notes list for display
|
||||
function formatNotesList(notes) {
|
||||
if (notes.length === 0) return '*None*';
|
||||
return notes.map(function (n, i) {
|
||||
return (i + 1) + '. **`' + (noteTypeToName[n.type] || n.type) + '`**: ' + n.text;
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
if (noteAction === 'list') {
|
||||
// ── note list ────────────────────────────────────────────────
|
||||
const { notes } = await fetchExpandedNotes();
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'ℹ️ **PocketBase Bot**: Notes for **`' + slug + '`** (' + notes.length + ' total)\n\n' +
|
||||
formatNotesList(notes)
|
||||
);
|
||||
|
||||
} else if (noteAction === 'add') {
|
||||
// ── note add <type> "<text>" ──────────────────────────────────
|
||||
const tokens = parseNoteTokens(noteArgsStr);
|
||||
if (tokens.length < 2) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: `note add` requires `<type>` and `"<text>"`.\n\n' +
|
||||
'**Usage:** `/pocketbase ' + slug + ' note add <type> "<text>"`\n' +
|
||||
'**Valid types:** `' + VALID_NOTE_TYPES.join('`, `') + '`'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const noteType = tokens[0].toLowerCase();
|
||||
const noteText = tokens.slice(1).join(' ');
|
||||
const typeId = noteTypeToId[noteType];
|
||||
if (!typeId) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: Unknown note type `' + noteType + '`.\n' +
|
||||
'**Valid types:** `' + VALID_NOTE_TYPES.join('`, `') + '`'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
// POST new note to script_notes
|
||||
const postNoteRes = await request(apiBase + '/collections/script_notes/records', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text: noteText, type: typeId, script: record.id })
|
||||
});
|
||||
if (!postNoteRes.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: Failed to create note:\n```\n' + postNoteRes.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
const newNoteId = JSON.parse(postNoteRes.body).id;
|
||||
// PATCH script to include new note in relation
|
||||
const existingNoteIds = Array.isArray(record.notes) ? record.notes : [];
|
||||
const patchLinkRes = await request(recordsUrl + '/' + record.id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ notes: [...existingNoteIds, newNoteId] })
|
||||
});
|
||||
if (!patchLinkRes.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: Note created but failed to link to script:\n```\n' + patchLinkRes.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'✅ **PocketBase Bot**: Added note to **`' + slug + '`**\n\n' +
|
||||
'- **Type:** `' + noteType + '`\n' +
|
||||
'- **Text:** ' + noteText + '\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
|
||||
} else if (noteAction === 'edit') {
|
||||
// ── note edit <type> "<old text>" "<new text>" ────────────────
|
||||
const tokens = parseNoteTokens(noteArgsStr);
|
||||
if (tokens.length < 3) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: `note edit` requires `<type>`, `"<old text>"`, and `"<new text>"`.\n\n' +
|
||||
'**Usage:** `/pocketbase ' + slug + ' note edit <type> "<old text>" "<new text>"`\n' +
|
||||
'**Valid types:** `' + VALID_NOTE_TYPES.join('`, `') + '`\n\n' +
|
||||
'Use `/pocketbase ' + slug + ' note list` to see current notes.'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const noteType = tokens[0].toLowerCase();
|
||||
const oldText = tokens[1];
|
||||
const newText = tokens[2];
|
||||
const typeId = noteTypeToId[noteType];
|
||||
if (!typeId) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: Unknown note type `' + noteType + '`.\n' +
|
||||
'**Valid types:** `' + VALID_NOTE_TYPES.join('`, `') + '`'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const { notes } = await fetchExpandedNotes();
|
||||
const matchingNote = notes.find(function (n) { return n.type === typeId && n.text === oldText; });
|
||||
if (!matchingNote) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: No `' + noteType + '` note found with that exact text.\n\n' +
|
||||
'**Current notes for `' + slug + '`:**\n' + formatNotesList(notes)
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
// PATCH the note record directly
|
||||
const patchNoteRes = await request(apiBase + '/collections/script_notes/records/' + matchingNote.id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text: newText })
|
||||
});
|
||||
if (!patchNoteRes.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: Failed to update note:\n```\n' + patchNoteRes.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'✅ **PocketBase Bot**: Edited note in **`' + slug + '`**\n\n' +
|
||||
'- **Type:** `' + noteType + '`\n' +
|
||||
'- **Old:** ' + oldText + '\n' +
|
||||
'- **New:** ' + newText + '\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
|
||||
} else if (noteAction === 'remove') {
|
||||
// ── note remove <type> "<text>" ───────────────────────────────
|
||||
const tokens = parseNoteTokens(noteArgsStr);
|
||||
if (tokens.length < 2) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: `note remove` requires `<type>` and `"<text>"`.\n\n' +
|
||||
'**Usage:** `/pocketbase ' + slug + ' note remove <type> "<text>"`\n' +
|
||||
'**Valid types:** `' + VALID_NOTE_TYPES.join('`, `') + '`\n\n' +
|
||||
'Use `/pocketbase ' + slug + ' note list` to see current notes.'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const noteType = tokens[0].toLowerCase();
|
||||
const noteText = tokens[1];
|
||||
const typeId = noteTypeToId[noteType];
|
||||
if (!typeId) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: Unknown note type `' + noteType + '`.\n' +
|
||||
'**Valid types:** `' + VALID_NOTE_TYPES.join('`, `') + '`'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const { rec: expandedRecord, notes } = await fetchExpandedNotes();
|
||||
const matchingNote = notes.find(function (n) { return n.type === typeId && n.text === noteText; });
|
||||
if (!matchingNote) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: No `' + noteType + '` note found with that exact text.\n\n' +
|
||||
'**Current notes for `' + slug + '`:**\n' + formatNotesList(notes)
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
// PATCH script to remove note ID from relation, then DELETE the note record
|
||||
const existingNoteIds = Array.isArray(expandedRecord.notes) ? expandedRecord.notes : [];
|
||||
const patchRes = await request(recordsUrl + '/' + record.id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ notes: existingNoteIds.filter(function (id) { return id !== matchingNote.id; }) })
|
||||
});
|
||||
if (!patchRes.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: Failed to unlink note from script:\n```\n' + patchRes.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
const delRes = await request(apiBase + '/collections/script_notes/records/' + matchingNote.id, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': token }
|
||||
});
|
||||
if (!delRes.ok && delRes.statusCode !== 204) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: Note unlinked but could not be deleted:\n```\n' + delRes.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'✅ **PocketBase Bot**: Removed note from **`' + slug + '`**\n\n' +
|
||||
'- **Type:** `' + noteType + '`\n' +
|
||||
'- **Text:** ' + noteText + '\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
// ── FIELD=VALUE PATH ─────────────────────────────────────────────
|
||||
@@ -655,6 +566,7 @@ jobs:
|
||||
disable_message: 'string',
|
||||
is_deleted: 'boolean',
|
||||
deleted_message: 'string',
|
||||
version: 'string',
|
||||
};
|
||||
|
||||
// Field=value parser (handles quoted values and empty=null)
|
||||
|
||||
53
.github/workflows/push_json_to_pocketbase.yml
generated
vendored
53
.github/workflows/push_json_to_pocketbase.yml
generated
vendored
@@ -1,6 +1,11 @@
|
||||
name: Push JSON changes to PocketBase
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "json/*.json"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
script_slug:
|
||||
@@ -20,20 +25,52 @@ jobs:
|
||||
- name: Get JSON file for script
|
||||
id: changed
|
||||
run: |
|
||||
script_slug="${{ github.event.inputs.script_slug }}"
|
||||
file="json/${script_slug}.json"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
echo "No JSON file at $file."
|
||||
: > changed_app_jsons.txt
|
||||
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
script_slug="${{ github.event.inputs.script_slug }}"
|
||||
file="json/${script_slug}.json"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
echo "No JSON file at $file."
|
||||
echo "count=0" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
if ! jq -e '.slug' "$file" >/dev/null 2>&1; then
|
||||
echo "File $file has no .slug."
|
||||
echo "count=0" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "$file" > changed_app_jsons.txt
|
||||
echo "count=1" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
changed=$(git diff --name-only "${{ github.event.before }}" "${{ github.event.after }}" -- json/*.json || true)
|
||||
if [[ -z "$changed" ]]; then
|
||||
echo "No JSON files changed under json/*.json."
|
||||
echo "count=0" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
if ! jq -e '.slug' "$file" >/dev/null 2>&1; then
|
||||
echo "File $file has no .slug."
|
||||
|
||||
count=0
|
||||
for file in $changed; do
|
||||
[[ -f "$file" ]] || continue
|
||||
if [[ "$file" == "json/metadata.json" || "$file" == "json/update-apps.json" || "$file" == "json/versions.json" ]]; then
|
||||
continue
|
||||
fi
|
||||
if jq -e '.slug' "$file" >/dev/null 2>&1; then
|
||||
echo "$file" >> changed_app_jsons.txt
|
||||
count=$((count + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $count -eq 0 ]]; then
|
||||
echo "No app JSON files with .slug found in this push."
|
||||
echo "count=0" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "$file" > changed_app_jsons.txt
|
||||
echo "count=1" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "count=$count" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Push to PocketBase
|
||||
if: steps.changed.outputs.count != '0'
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Slaviša Arežina (tremor021)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://wakapi.dev/ | https://github.com/muety/wakapi
|
||||
|
||||
APP="Alpine-Wakapi"
|
||||
var_tags="${var_tags:-code;time-tracking}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-512}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-alpine}"
|
||||
var_version="${var_version:-3.23}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
if [[ ! -d /opt/wakapi ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
RELEASE=$(curl -s https://api.github.com/repos/muety/wakapi/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }')
|
||||
if [ "${RELEASE}" != "$(cat ~/.wakapi 2>/dev/null)" ] || [ ! -f ~/.wakapi ]; then
|
||||
msg_info "Stopping Wakapi Service"
|
||||
$STD rc-service wakapi stop
|
||||
msg_ok "Stopped Wakapi Service"
|
||||
|
||||
msg_info "Updating Wakapi LXC"
|
||||
$STD apk -U upgrade
|
||||
msg_ok "Updated Wakapi LXC"
|
||||
|
||||
msg_info "Creating backup"
|
||||
mkdir -p /opt/wakapi-backup
|
||||
cp /opt/wakapi/config.yml /opt/wakapi/wakapi_db.db /opt/wakapi-backup/
|
||||
msg_ok "Created backup"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "wakapi" "muety/wakapi" "tarball"
|
||||
|
||||
msg_info "Configuring Wakapi"
|
||||
cd /opt/wakapi
|
||||
$STD go mod download
|
||||
$STD go build -o wakapi
|
||||
cp /opt/wakapi-backup/config.yml /opt/wakapi/
|
||||
cp /opt/wakapi-backup/wakapi_db.db /opt/wakapi/
|
||||
rm -rf /opt/wakapi-backup
|
||||
msg_ok "Configured Wakapi"
|
||||
|
||||
msg_info "Starting Service"
|
||||
$STD rc-service wakapi start
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully"
|
||||
else
|
||||
msg_ok "No update required. ${APP} is already at ${RELEASE}"
|
||||
fi
|
||||
exit 0
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}"
|
||||
73
ct/degoog.sh
Normal file
73
ct/degoog.sh
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: vhsdream
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/fccview/degoog
|
||||
|
||||
APP="degoog"
|
||||
var_tags="${var_tags:-search;privacy;plugins}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-6}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/degoog ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "degoog" "fccview/degoog"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop degoog
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Backing up Configuration & Data"
|
||||
[[ -f /opt/degoog/.env ]] && cp /opt/degoog/.env /opt/degoog.env.bak
|
||||
[[ -d /opt/degoog/data ]] && mv /opt/degoog/data /opt/degoog_data_backup
|
||||
msg_ok "Backed up Configuration & Data"
|
||||
|
||||
if ! command -v bun >/dev/null 2>&1; then
|
||||
msg_info "Installing Bun"
|
||||
export BUN_INSTALL="/root/.bun"
|
||||
curl -fsSL https://bun.sh/install | $STD bash
|
||||
ln -sf /root/.bun/bin/bun /usr/local/bin/bun
|
||||
ln -sf /root/.bun/bin/bunx /usr/local/bin/bunx
|
||||
msg_ok "Installed Bun"
|
||||
fi
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "degoog" "fccview/degoog" "prebuild" "latest" "/opt/degoog" "degoog_*_prebuild.tar.gz"
|
||||
|
||||
msg_info "Restoring Configuration & Data"
|
||||
[[ -f /opt/degoog.env.bak ]] && mv /opt/degoog.env.bak /opt/degoog/.env
|
||||
[[ -d /opt/degoog_data_backup ]] && mv /opt/degoog_data_backup /opt/degoog/data
|
||||
msg_ok "Restored Configuration & Data"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start degoog
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed Successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4444${CL}"
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Matthew Stern (sternma) | MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/dmunozv04/iSponsorBlockTV
|
||||
|
||||
APP="iSponsorBlockTV"
|
||||
var_tags="${var_tags:-media;automation}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/isponsorblocktv ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop isponsorblocktv
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV" "singlefile" "latest" "/opt/isponsorblocktv" "iSponsorBlockTV-*-linux"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start isponsorblocktv
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Run the setup wizard inside the container with:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}iSponsorBlockTV setup${CL}"
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Slaviša Arežina (tremor021)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://goteleport.com/
|
||||
|
||||
APP="Teleport"
|
||||
var_tags="${var_tags:-zero-trust}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
if [[ ! -f /etc/teleport.yaml ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
msg_info "Updating Teleport"
|
||||
$STD apt update
|
||||
$STD apt upgrade -y
|
||||
msg_ok "Updated successfully!"
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:3080${CL}"
|
||||
@@ -1,106 +1,142 @@
|
||||
|
||||
# Community Scripts Contribution Guide
|
||||
|
||||
## **Welcome to the communty-scripts Repository!**
|
||||
## Welcome to the community-scripts repository
|
||||
|
||||
📜 These documents outline the essential coding standards for all our scripts and JSON files. Adhering to these standards ensures that our codebase remains consistent, readable, and maintainable. By following these guidelines, we can improve collaboration, reduce errors, and enhance the overall quality of our project.
|
||||
These documents outline the coding standards and contribution flow for the ProxmoxVED repository.
|
||||
|
||||
### Why Coding Standards Matter
|
||||
The important reality check is simple:
|
||||
|
||||
Coding standards are crucial for several reasons:
|
||||
- contributors primarily submit shell scripts
|
||||
- website metadata is **not** contributed as repo JSON files
|
||||
- metadata changes belong to the website / maintainer workflow
|
||||
|
||||
1. **Consistency**: Consistent code is easier to read, understand, and maintain. It helps new team members quickly get up to speed and reduces the learning curve.
|
||||
2. **Readability**: Clear and well-structured code is easier to debug and extend. It allows developers to quickly identify and fix issues.
|
||||
3. **Maintainability**: Code that follows a standard structure is easier to refactor and update. It ensures that changes can be made with minimal risk of introducing new bugs.
|
||||
4. **Collaboration**: When everyone follows the same standards, it becomes easier to collaborate on code. It reduces friction and misunderstandings during code reviews and merges.
|
||||
## Scope of these documents
|
||||
|
||||
### Scope of These Documents
|
||||
This contribution guide covers:
|
||||
|
||||
These documents cover the coding standards for the following types of files in our project:
|
||||
- `ct/$AppName.sh` scripts for container creation and update entrypoints
|
||||
- `install/$AppName-install.sh` scripts for in-container installation logic
|
||||
- the supporting workflow for testing from your fork before opening a PR
|
||||
|
||||
- **`install/$AppName-install.sh` Scripts**: These scripts are responsible for the installation of applications.
|
||||
- **`ct/$AppName.sh` Scripts**: These scripts handle the creation and updating of containers.
|
||||
- **`json/$AppName.json`**: These files store structured data and are used for the website.
|
||||
## Getting started
|
||||
|
||||
Each section provides detailed guidelines on various aspects of coding, including shebang usage, comments, variable naming, function naming, indentation, error handling, command substitution, quoting, script structure, and logging. Additionally, examples are provided to illustrate the application of these standards.
|
||||
Before contributing, set up:
|
||||
|
||||
By following the coding standards outlined in this document, we ensure that our scripts and JSON files are of high quality, making our project more robust and easier to manage. Please refer to this guide whenever you create or update scripts and JSON files to maintain a high standard of code quality across the project. 📚🔍
|
||||
1. Visual Studio Code or another editor with ShellCheck support
|
||||
2. a fork of `community-scripts/ProxmoxVED`
|
||||
3. a local clone of your fork
|
||||
|
||||
Let's work together to keep our codebase clean, efficient, and maintainable! 💪🚀
|
||||
### Recommended extensions
|
||||
|
||||
- [Shell Syntax](https://marketplace.visualstudio.com/items?itemName=bmalehorn.shell-syntax)
|
||||
- [ShellCheck](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck)
|
||||
- [Shell Format](https://marketplace.visualstudio.com/items?itemName=foxundermoon.shell-format)
|
||||
|
||||
## Getting Started
|
||||
### Templates
|
||||
|
||||
Before contributing, please ensure that you have the following setup:
|
||||
Use these templates as your starting point:
|
||||
|
||||
1. **Visual Studio Code** (recommended for script development)
|
||||
2. **Recommended VS Code Extensions:**
|
||||
- [Shell Syntax](https://marketplace.visualstudio.com/items?itemName=bmalehorn.shell-syntax)
|
||||
- [ShellCheck](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck)
|
||||
- [Shell Format](https://marketplace.visualstudio.com/items?itemName=foxundermoon.shell-format)
|
||||
- [CT template: `AppName.sh`](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh)
|
||||
- [Install template: `AppName-install.sh`](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.sh)
|
||||
|
||||
### Important Notes
|
||||
- Use [AppName.sh](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh) and [AppName-install.sh](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.sh) as templates when creating new scripts.
|
||||
## Script types
|
||||
|
||||
---
|
||||
### Application script: `ct/AppName.sh`
|
||||
|
||||
# 🚀 The Application Script (ct/AppName.sh)
|
||||
Reference guide:
|
||||
|
||||
- You can find all coding standards, as well as the structure for this file [here](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md).
|
||||
- These scripts are responsible for container creation, setting the necessary variables and handling the update of the application once installed.
|
||||
- [CT coding guide for `AppName.sh`](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md)
|
||||
|
||||
---
|
||||
This script is responsible for:
|
||||
|
||||
# 🛠 The Installation Script (install/AppName-install.sh)
|
||||
- host-side container orchestration
|
||||
- app variables and defaults
|
||||
- update wiring for the installed app
|
||||
|
||||
- You can find all coding standards, as well as the structure for this file [here](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.md).
|
||||
- These scripts are responsible for the installation of the application.
|
||||
### Installation script: `install/AppName-install.sh`
|
||||
|
||||
---
|
||||
Reference guide:
|
||||
|
||||
## 🚀 Building Your Own Scripts
|
||||
- [Install coding guide for `AppName-install.sh`](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.md)
|
||||
|
||||
Start with the [template script](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.sh)
|
||||
This script is responsible for:
|
||||
|
||||
---
|
||||
- container-internal installation logic
|
||||
- package/runtime setup
|
||||
- final application configuration
|
||||
|
||||
## 🤝 Contribution Process
|
||||
## Contribution process
|
||||
|
||||
### 1. Fork the repository
|
||||
Fork to your GitHub account
|
||||
|
||||
### 2. Clone your fork on your local environment
|
||||
Fork `community-scripts/ProxmoxVED` to your GitHub account.
|
||||
|
||||
### 2. Clone your fork
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yourUserName/ForkName
|
||||
```
|
||||
|
||||
### 3. Create a new branch
|
||||
### 3. Create a branch
|
||||
|
||||
```bash
|
||||
git switch -c your-feature-branch
|
||||
```
|
||||
|
||||
### 4. Change paths in build.func install.func and AppName.sh
|
||||
To be able to develop from your own branch you need to change `https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main` to `https://raw.githubusercontent.com/[USER]/[REPOSITORY]/refs/heads/[BRANCH]`. You need to make this change atleast in misc/build.func misc/install.func and in your ct/AppName.sh. This change is only for testing. Before opening a Pull Request you should change this line change all this back to point to `https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main`.
|
||||
### 4. Configure your fork for testing
|
||||
|
||||
Use the helper script:
|
||||
|
||||
```bash
|
||||
bash docs/contribution/setup-fork.sh --full
|
||||
```
|
||||
|
||||
This prepares the raw GitHub URLs in your working copy so you can test against your own fork instead of the upstream repository.
|
||||
|
||||
### 5. Build and test from your fork
|
||||
|
||||
Use the curl/bash execution model that matches real user behavior, for example:
|
||||
|
||||
```bash
|
||||
bash -c "$(curl -fsSL https://raw.githubusercontent.com/<USER>/<REPO>/refs/heads/<BRANCH>/ct/myapp.sh)"
|
||||
```
|
||||
|
||||
Do **not** document or optimize only for local manual execution if the real path is curl-based execution.
|
||||
|
||||
### 6. Commit only your intended contribution
|
||||
|
||||
### 4. Commit changes (without build.func and install.func!)
|
||||
```bash
|
||||
git commit -m "Your commit message"
|
||||
```
|
||||
|
||||
### 5. Push to your fork
|
||||
### 7. Push your branch
|
||||
|
||||
```bash
|
||||
git push origin your-feature-branch
|
||||
```
|
||||
|
||||
### 6. Create a Pull Request
|
||||
Open a Pull Request from your feature branch to the main repository branch. You must only include your **$AppName.sh**, **$AppName-install.sh** and **$AppName.json** files in the pull request.
|
||||
### 8. Open a pull request
|
||||
|
||||
---
|
||||
Open a PR from your branch to `community-scripts/ProxmoxVED/main`.
|
||||
|
||||
## 📚 Pages
|
||||
Your PR should contain only the files that belong to the script contribution itself, typically:
|
||||
|
||||
- `ct/myapp.sh`
|
||||
- `install/myapp-install.sh`
|
||||
|
||||
## Website metadata
|
||||
|
||||
Website metadata is maintained outside this repository's script contribution flow.
|
||||
|
||||
That means:
|
||||
|
||||
- do not add repo JSON metadata files as part of the normal contribution path
|
||||
- do not assume a `frontend/public/json/...` workflow exists for the live site
|
||||
- route metadata creation or metadata changes through the website / maintainer workflow
|
||||
|
||||
## Pages
|
||||
|
||||
- [CT Template: AppName.sh](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.sh)
|
||||
- [Install Template: AppName-install.sh](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.sh)
|
||||
- [JSON Template: AppName.json](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/json/AppName.json)
|
||||
|
||||
|
||||
- [Fork setup guide](./FORK_SETUP.md)
|
||||
- [Contribution README](./README.md)
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
# Misc Documentation
|
||||
|
||||
This directory contains comprehensive documentation for all function libraries and components of the Proxmox Community Scripts project. Each section is organized as a dedicated subdirectory with detailed references, examples, and integration guides.
|
||||
This directory documents the shared Bash function libraries under `misc/`.
|
||||
|
||||
The important implementation detail is that these libraries are **not independent islands**:
|
||||
|
||||
- `build.func` orchestrates host-side CT creation.
|
||||
- `api.func` is the canonical source of telemetry and exit-code explanations.
|
||||
- `error_handler.func` wraps trap handling and falls back to `explain_exit_code()` if `api.func` was not loaded yet.
|
||||
- `install.func` runs inside the container and bootstraps `core.func` + `error_handler.func` first, then downloads `tools.func` after the OS update stage.
|
||||
- `tools.func` is the large Debian/Ubuntu helper toolbox for repository management, retries, releases, services, language runtimes, databases, GPU helpers, and update workflows.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ **Core Function Libraries**
|
||||
|
||||
### 📁 [build.func/](./build.func/)
|
||||
|
||||
**Core LXC Container Orchestration** - Main orchestrator for Proxmox LXC container creation
|
||||
|
||||
**Contents:**
|
||||
|
||||
- BUILD_FUNC_FLOWCHART.md - Visual execution flows and decision trees
|
||||
- BUILD_FUNC_ARCHITECTURE.md - System architecture and design
|
||||
- BUILD_FUNC_ENVIRONMENT_VARIABLES.md - Complete environment variable reference
|
||||
@@ -23,9 +33,11 @@ This directory contains comprehensive documentation for all function libraries a
|
||||
---
|
||||
|
||||
### 📁 [core.func/](./core.func/)
|
||||
**System Utilities & Foundation** - Essential utility functions and system checks
|
||||
|
||||
**System Utilities & Foundation** - Shared runtime foundation for logging, prompts, validation, and execution control
|
||||
|
||||
**Contents:**
|
||||
|
||||
- CORE_FLOWCHART.md - Visual execution flows
|
||||
- CORE_FUNCTIONS_REFERENCE.md - Complete function reference
|
||||
- CORE_INTEGRATION.md - Integration points
|
||||
@@ -37,9 +49,11 @@ This directory contains comprehensive documentation for all function libraries a
|
||||
---
|
||||
|
||||
### 📁 [error_handler.func/](./error_handler.func/)
|
||||
**Error Handling & Signal Management** - Comprehensive error handling and signal trapping
|
||||
|
||||
**Error Handling & Signal Management** - Trap orchestration, cleanup, and abort telemetry
|
||||
|
||||
**Contents:**
|
||||
|
||||
- ERROR_HANDLER_FLOWCHART.md - Visual error handling flows
|
||||
- ERROR_HANDLER_FUNCTIONS_REFERENCE.md - Function reference
|
||||
- ERROR_HANDLER_INTEGRATION.md - Integration with other components
|
||||
@@ -51,39 +65,45 @@ This directory contains comprehensive documentation for all function libraries a
|
||||
---
|
||||
|
||||
### 📁 [api.func/](./api.func/)
|
||||
**Proxmox API Integration** - API communication and diagnostic reporting
|
||||
|
||||
**Telemetry & Diagnostics Runtime** - Anonymous telemetry reporting, progress tracking, and canonical exit-code mapping
|
||||
|
||||
**Contents:**
|
||||
|
||||
- API_FLOWCHART.md - API communication flows
|
||||
- API_FUNCTIONS_REFERENCE.md - Function reference
|
||||
- API_INTEGRATION.md - Integration points
|
||||
- API_USAGE_EXAMPLES.md - Practical examples
|
||||
- README.md - Overview and quick reference
|
||||
|
||||
**Key Functions**: `post_to_api()`, `post_update_to_api()`, `get_error_description()`
|
||||
**Key Functions**: `post_to_api()`, `post_to_api_vm()`, `post_progress_to_api()`, `post_update_to_api()`, `explain_exit_code()`
|
||||
|
||||
---
|
||||
|
||||
## 📦 **Installation & Setup Function Libraries**
|
||||
|
||||
### 📁 [install.func/](./install.func/)
|
||||
**Container Installation Workflow** - Installation orchestration for container-internal setup
|
||||
|
||||
**Container Installation Workflow** - Container bootstrap inside the LXC
|
||||
|
||||
**Contents:**
|
||||
|
||||
- INSTALL_FUNC_FLOWCHART.md - Installation workflow diagrams
|
||||
- INSTALL_FUNC_FUNCTIONS_REFERENCE.md - Complete function reference
|
||||
- INSTALL_FUNC_INTEGRATION.md - Integration with build and tools
|
||||
- INSTALL_FUNC_USAGE_EXAMPLES.md - Practical examples
|
||||
- README.md - Overview and quick reference
|
||||
|
||||
**Key Functions**: `setting_up_container()`, `network_check()`, `update_os()`, `motd_ssh()`, `cleanup_lxc()`
|
||||
**Key Functions**: `setting_up_container()`, `network_check()`, `update_os()`, `motd_ssh()`, `customize()`
|
||||
|
||||
---
|
||||
|
||||
### 📁 [tools.func/](./tools.func/)
|
||||
**Package & Tool Installation** - Robust package management and 30+ tool installation functions
|
||||
|
||||
**Package & Tool Installation** - Repository, package, release, runtime, and service toolbox
|
||||
|
||||
**Contents:**
|
||||
|
||||
- TOOLS_FUNC_FLOWCHART.md - Package management flows
|
||||
- TOOLS_FUNC_FUNCTIONS_REFERENCE.md - 30+ function reference
|
||||
- TOOLS_FUNC_INTEGRATION.md - Integration with install workflows
|
||||
@@ -91,14 +111,16 @@ This directory contains comprehensive documentation for all function libraries a
|
||||
- TOOLS_FUNC_ENVIRONMENT_VARIABLES.md - Configuration reference
|
||||
- README.md - Overview and quick reference
|
||||
|
||||
**Key Functions**: `setup_nodejs()`, `setup_php()`, `setup_mariadb()`, `setup_docker()`, `setup_deb822_repo()`, `pkg_install()`, `pkg_update()`
|
||||
**Key Functions**: `curl_with_retry()`, `setup_deb822_repo()`, `install_packages_with_retry()`, `setup_mariadb()`, `setup_postgresql()`, `get_latest_github_release()`
|
||||
|
||||
---
|
||||
|
||||
### 📁 [alpine-install.func/](./alpine-install.func/)
|
||||
|
||||
**Alpine Container Setup** - Alpine Linux-specific installation functions
|
||||
|
||||
**Contents:**
|
||||
|
||||
- ALPINE_INSTALL_FUNC_FLOWCHART.md - Alpine setup flows
|
||||
- ALPINE_INSTALL_FUNC_FUNCTIONS_REFERENCE.md - Function reference
|
||||
- ALPINE_INSTALL_FUNC_INTEGRATION.md - Integration points
|
||||
@@ -110,9 +132,11 @@ This directory contains comprehensive documentation for all function libraries a
|
||||
---
|
||||
|
||||
### 📁 [alpine-tools.func/](./alpine-tools.func/)
|
||||
|
||||
**Alpine Tool Installation** - Alpine-specific package and tool installation
|
||||
|
||||
**Contents:**
|
||||
|
||||
- ALPINE_TOOLS_FUNC_FLOWCHART.md - Alpine package flows
|
||||
- ALPINE_TOOLS_FUNC_FUNCTIONS_REFERENCE.md - Function reference
|
||||
- ALPINE_TOOLS_FUNC_INTEGRATION.md - Integration with Alpine workflows
|
||||
@@ -124,9 +148,11 @@ This directory contains comprehensive documentation for all function libraries a
|
||||
---
|
||||
|
||||
### 📁 [cloud-init.func/](./cloud-init.func/)
|
||||
|
||||
**VM Cloud-Init Configuration** - Cloud-init and VM provisioning functions
|
||||
|
||||
**Contents:**
|
||||
|
||||
- CLOUD_INIT_FUNC_FLOWCHART.md - Cloud-init flows
|
||||
- CLOUD_INIT_FUNC_FUNCTIONS_REFERENCE.md - Function reference
|
||||
- CLOUD_INIT_FUNC_INTEGRATION.md - Integration points
|
||||
@@ -145,18 +171,24 @@ This directory contains comprehensive documentation for all function libraries a
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ct/AppName.sh │
|
||||
│ ↓ (sources) │
|
||||
│ ↓ sources │
|
||||
│ build.func │
|
||||
│ ├─ variables() │
|
||||
│ ├─ build_container() │
|
||||
│ └─ advanced_settings() │
|
||||
│ ↓ (calls pct create with) │
|
||||
│ ├─ sources api.func │
|
||||
│ ├─ sources core.func │
|
||||
│ ├─ sources error_handler.func │
|
||||
│ ├─ loads variables/settings/prompts │
|
||||
│ └─ creates container + launch phase │
|
||||
│ ↓ pct exec / lxc-attach │
|
||||
│ install/appname-install.sh │
|
||||
│ ↓ (sources) │
|
||||
│ ├─ core.func (colors, messaging) │
|
||||
│ ├─ error_handler.func (error trapping) │
|
||||
│ ├─ install.func (setup/network) │
|
||||
│ └─ tools.func (packages/tools) │
|
||||
│ ↓ sources install.func │
|
||||
│ ├─ sources core.func │
|
||||
│ ├─ sources error_handler.func │
|
||||
│ ├─ load_functions() │
|
||||
│ ├─ catch_errors() │
|
||||
│ ├─ network_check() │
|
||||
│ ├─ update_os() │
|
||||
│ │ └─ downloads + sources tools.func │
|
||||
│ └─ app install uses tools.func │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
|
||||
@@ -191,17 +223,17 @@ This directory contains comprehensive documentation for all function libraries a
|
||||
|
||||
## 📊 **Documentation Quick Stats**
|
||||
|
||||
| Library | Files | Functions | Status |
|
||||
|---------|:---:|:---:|:---:|
|
||||
| build.func | 7 | 50+ | ✅ Complete |
|
||||
| core.func | 5 | 20+ | ✅ Complete |
|
||||
| error_handler.func | 5 | 10+ | ✅ Complete |
|
||||
| api.func | 5 | 5+ | ✅ Complete |
|
||||
| install.func | 5 | 8+ | ✅ Complete |
|
||||
| tools.func | 6 | 30+ | ✅ Complete |
|
||||
| alpine-install.func | 5 | 6+ | ✅ Complete |
|
||||
| alpine-tools.func | 5 | 15+ | ✅ Complete |
|
||||
| cloud-init.func | 5 | 12+ | ✅ Complete |
|
||||
| Library | Files | Functions | Status |
|
||||
| ------------------- | :---: | :-------: | :---------: |
|
||||
| build.func | 7 | 50+ | ✅ Complete |
|
||||
| core.func | 5 | 20+ | ✅ Complete |
|
||||
| error_handler.func | 5 | 10+ | ✅ Complete |
|
||||
| api.func | 5 | 5+ | ✅ Complete |
|
||||
| install.func | 5 | 8+ | ✅ Complete |
|
||||
| tools.func | 6 | 30+ | ✅ Complete |
|
||||
| alpine-install.func | 5 | 6+ | ✅ Complete |
|
||||
| alpine-tools.func | 5 | 15+ | ✅ Complete |
|
||||
| cloud-init.func | 5 | 12+ | ✅ Complete |
|
||||
|
||||
**Total**: 9 function libraries, 48 documentation files, 150+ functions
|
||||
|
||||
@@ -210,16 +242,20 @@ This directory contains comprehensive documentation for all function libraries a
|
||||
## 🚀 **Getting Started**
|
||||
|
||||
### For Container Creation Scripts
|
||||
Start with: **[build.func/](./build.func/)** → **[tools.func/](./tools.func/)** → **[install.func/](./install.func/)**
|
||||
|
||||
Start with: **[build.func/](./build.func/)** → **[core.func/](./core.func/)** → **[error_handler.func/](./error_handler.func/)** → **[api.func/](./api.func/)** → **[install.func/](./install.func/)** → **[tools.func/](./tools.func/)**
|
||||
|
||||
### For Alpine Containers
|
||||
|
||||
Start with: **[alpine-install.func/](./alpine-install.func/)** → **[alpine-tools.func/](./alpine-tools.func/)**
|
||||
|
||||
### For VM Provisioning
|
||||
|
||||
Start with: **[cloud-init.func/](./cloud-init.func/)**
|
||||
|
||||
### For Troubleshooting
|
||||
Start with: **[error_handler.func/](./error_handler.func/)** → **[EXIT_CODES.md](../EXIT_CODES.md)**
|
||||
|
||||
Start with: **[error_handler.func/](./error_handler.func/)** → **[api.func/](./api.func/)**
|
||||
|
||||
---
|
||||
|
||||
@@ -251,6 +287,7 @@ function-library/
|
||||
```
|
||||
|
||||
**Advantages**:
|
||||
|
||||
- ✅ Consistent navigation across all libraries
|
||||
- ✅ Quick reference sections in each README
|
||||
- ✅ Visual flowcharts for understanding
|
||||
@@ -273,11 +310,12 @@ All documentation follows these standards:
|
||||
|
||||
---
|
||||
|
||||
## ✅ **Last Updated**: December 2025
|
||||
## ✅ **Last Updated**: Based on live `misc/*` code verification
|
||||
|
||||
**Maintainers**: community-scripts team
|
||||
**License**: MIT
|
||||
**Status**: All 9 libraries fully documented and standardized
|
||||
**Status**: Canonical overviews aligned to live code; deeper generated subpages may still require occasional drift cleanup
|
||||
|
||||
---
|
||||
|
||||
*This directory contains specialized documentation for specific components of the Proxmox Community Scripts project.*
|
||||
_When documentation conflicts with the live shell implementation, prefer the files under `ProxmoxVE` / `ProxmoxVED` `misc/`._
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Slaviša Arežina (tremor021)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://wakapi.dev/ | https://github.com/muety/wakapi
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apk add --no-cache \
|
||||
ca-certificates \
|
||||
tzdata
|
||||
$STD update-ca-certificates
|
||||
$STD apk add --no-cache go --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
fetch_and_deploy_gh_release "wakapi" "muety/wakapi" "tarball"
|
||||
|
||||
msg_info "Configuring Wakapi"
|
||||
LOCAL_IP=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1)
|
||||
cd /opt/wakapi
|
||||
$STD go mod download
|
||||
$STD go build -o wakapi
|
||||
cp config.default.yml config.yml
|
||||
sed -i 's/listen_ipv6: ::1/listen_ipv6: "-"/g' config.yml
|
||||
sed -i 's/listen_ipv4: 127.0.0.1/listen_ipv4: "0.0.0.0"/g' config.yml
|
||||
sed -i "s/public_url: http:\/\/localhost:3000/public_url: http:\/\/$LOCAL_IP:3000/g" config.yml
|
||||
msg_ok "Configured Wakapi"
|
||||
|
||||
msg_info "Enabling Wakapi Service"
|
||||
cat <<EOF >/etc/init.d/wakapi
|
||||
#!/sbin/openrc-run
|
||||
description="Wakapi Service"
|
||||
directory="/opt/wakapi"
|
||||
command="/opt/wakapi/wakapi"
|
||||
command_args="-config config.yml"
|
||||
command_background="true"
|
||||
command_user="root"
|
||||
pidfile="/var/run/wakapi.pid"
|
||||
|
||||
depend() {
|
||||
use net
|
||||
}
|
||||
EOF
|
||||
chmod +x /etc/init.d/wakapi
|
||||
$STD rc-update add wakapi default
|
||||
msg_ok "Enabled Wakapi Service"
|
||||
|
||||
msg_info "Starting Wakapi"
|
||||
$STD rc-service wakapi start
|
||||
msg_ok "Started Wakapi"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
88
install/degoog-install.sh
Normal file
88
install/degoog-install.sh
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: vhsdream
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/fccview/degoog
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
git \
|
||||
unzip
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
msg_info "Installing Bun"
|
||||
export BUN_INSTALL="/root/.bun"
|
||||
curl -fsSL https://bun.sh/install | $STD bash
|
||||
ln -sf /root/.bun/bin/bun /usr/local/bin/bun
|
||||
ln -sf /root/.bun/bin/bunx /usr/local/bin/bunx
|
||||
msg_ok "Installed Bun"
|
||||
|
||||
fetch_and_deploy_gh_release "degoog" "fccview/degoog" "prebuild" "latest" "/opt/degoog" "degoog_*_prebuild.tar.gz"
|
||||
|
||||
msg_info "Setting up degoog"
|
||||
mkdir -p /opt/degoog/data/{engines,plugins,themes,store}
|
||||
|
||||
cat <<EOF >/opt/degoog/.env
|
||||
DEGOOG_PORT=4444
|
||||
DEGOOG_ENGINES_DIR=/opt/degoog/data/engines
|
||||
DEGOOG_PLUGINS_DIR=/opt/degoog/data/plugins
|
||||
DEGOOG_THEMES_DIR=/opt/degoog/data/themes
|
||||
DEGOOG_ALIASES_FILE=/opt/degoog/data/aliases.json
|
||||
DEGOOG_PLUGIN_SETTINGS_FILE=/opt/degoog/data/plugin-settings.json
|
||||
# DEGOOG_SETTINGS_PASSWORDS=changeme
|
||||
# DEGOOG_PUBLIC_INSTANCE=false
|
||||
# LOGGER=debug
|
||||
EOF
|
||||
|
||||
if [[ ! -f /opt/degoog/data/aliases.json ]]; then
|
||||
cat <<EOF >/opt/degoog/data/aliases.json
|
||||
{}
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [[ ! -f /opt/degoog/data/plugin-settings.json ]]; then
|
||||
cat <<EOF >/opt/degoog/data/plugin-settings.json
|
||||
{}
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [[ ! -f /opt/degoog/data/repos.json ]]; then
|
||||
cat <<EOF >/opt/degoog/data/repos.json
|
||||
[]
|
||||
EOF
|
||||
fi
|
||||
msg_ok "Set up degoog"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/degoog.service
|
||||
[Unit]
|
||||
Description=degoog
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/degoog
|
||||
EnvironmentFile=/opt/degoog/.env
|
||||
ExecStart=/usr/local/bin/bun run src/server/index.ts
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now degoog
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Matthew Stern (sternma) | MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/dmunozv04/iSponsorBlockTV
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
fetch_and_deploy_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV" "singlefile" "latest" "/opt/isponsorblocktv" "iSponsorBlockTV-*-linux"
|
||||
|
||||
msg_info "Setting up iSponsorBlockTV"
|
||||
install -d /var/lib/isponsorblocktv
|
||||
msg_ok "Set up iSponsorBlockTV"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/isponsorblocktv.service
|
||||
[Unit]
|
||||
Description=iSponsorBlockTV
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
Environment=iSPBTV_data_dir=/var/lib/isponsorblocktv
|
||||
ExecStart=/opt/isponsorblocktv/isponsorblocktv
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q isponsorblocktv
|
||||
msg_ok "Created Service"
|
||||
|
||||
msg_info "Creating CLI wrapper"
|
||||
cat <<EOF >/usr/local/bin/iSponsorBlockTV
|
||||
#!/usr/bin/env bash
|
||||
export iSPBTV_data_dir="/var/lib/isponsorblocktv"
|
||||
|
||||
set +e
|
||||
/opt/isponsorblocktv/isponsorblocktv "$@"
|
||||
status=$?
|
||||
set -e
|
||||
|
||||
case "${1:-}" in
|
||||
setup|setup-cli)
|
||||
systemctl restart isponsorblocktv >/dev/null 2>&1 || true
|
||||
;;
|
||||
esac
|
||||
|
||||
exit $status
|
||||
EOF
|
||||
chmod +x /usr/local/bin/iSponsorBlockTV
|
||||
ln -sf /usr/local/bin/iSponsorBlockTV /usr/bin/iSponsorBlockTV
|
||||
msg_ok "Created CLI wrapper"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Slaviša Arežina (tremor021)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://goteleport.com/
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
setup_deb822_repo \
|
||||
"teleport" \
|
||||
"https://deb.releases.teleport.dev/teleport-pubkey.asc" \
|
||||
"https://apt.releases.teleport.dev/debian" \
|
||||
"trixie" \
|
||||
"stable/v18"
|
||||
|
||||
msg_info "Configuring Teleport"
|
||||
$STD apt install -y teleport
|
||||
$STD teleport configure -o /etc/teleport.yaml
|
||||
systemctl enable -q --now teleport
|
||||
sleep 5
|
||||
tctl users add teleport-admin --roles=editor,access --logins=root >~/teleportadmin.creds
|
||||
sed -i "s|https://[^:]*:3080|https://${LOCAL_IP}:3080|g" ~/teleportadmin.creds
|
||||
msg_ok "Configured Teleport"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"name": "Alpine-Wakapi",
|
||||
"slug": "alpine-wakapi",
|
||||
"categories": [
|
||||
9
|
||||
],
|
||||
"date_created": "2026-02-16",
|
||||
"type": "ct",
|
||||
"updateable": true,
|
||||
"privileged": false,
|
||||
"interface_port": 3000,
|
||||
"documentation": "https://github.com/muety/wakapi/wiki",
|
||||
"website": "https://wakapi.dev/",
|
||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/wakapi.webp",
|
||||
"config_path": "/opt/wakapi/config.yml",
|
||||
"description": "Wakapi is an open-source tool that helps you keep track of the time you have spent coding on different projects in different programming languages and more. Ideal for statistics freaks and anyone else.",
|
||||
"install_methods": [
|
||||
{
|
||||
"type": "default",
|
||||
"script": "ct/alpine-wakapi.sh",
|
||||
"resources": {
|
||||
"cpu": 1,
|
||||
"ram": 512,
|
||||
"hdd": 4,
|
||||
"os": "alpine",
|
||||
"version": "3.23"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_credentials": {
|
||||
"username": null,
|
||||
"password": null
|
||||
},
|
||||
"notes": [
|
||||
{
|
||||
"text": "The first user created will be an admin.",
|
||||
"type": "warning"
|
||||
}
|
||||
]
|
||||
}
|
||||
44
json/degoog.json
Normal file
44
json/degoog.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "degoog",
|
||||
"slug": "degoog",
|
||||
"categories": [
|
||||
0
|
||||
],
|
||||
"date_created": "2026-03-20",
|
||||
"type": "ct",
|
||||
"updateable": true,
|
||||
"privileged": false,
|
||||
"interface_port": 4444,
|
||||
"documentation": "https://fccview.github.io/degoog/",
|
||||
"website": "https://github.com/fccview/degoog",
|
||||
"logo": "https://raw.githubusercontent.com/fccview/degoog/main/src/public/images/degoog-logo.png",
|
||||
"config_path": "/opt/degoog/.env",
|
||||
"description": "Search aggregator that queries multiple engines and supports plugins, themes, and extension repositories.",
|
||||
"install_methods": [
|
||||
{
|
||||
"type": "default",
|
||||
"script": "ct/degoog.sh",
|
||||
"resources": {
|
||||
"cpu": 2,
|
||||
"ram": 2048,
|
||||
"hdd": 6,
|
||||
"os": "Debian",
|
||||
"version": "13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_credentials": {
|
||||
"username": null,
|
||||
"password": null
|
||||
},
|
||||
"notes": [
|
||||
{
|
||||
"text": "Project is currently marked beta upstream and not intended for production use yet.",
|
||||
"type": "warning"
|
||||
},
|
||||
{
|
||||
"text": "Add extension store repositories in Settings after first start (official repo: https://github.com/fccview/fccview-degoog-extensions).",
|
||||
"type": "info"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "iSponsorBlockTV",
|
||||
"slug": "isponsorblocktv",
|
||||
"categories": [
|
||||
13
|
||||
],
|
||||
"date_created": "2026-01-25",
|
||||
"type": "ct",
|
||||
"updateable": true,
|
||||
"privileged": false,
|
||||
"interface_port": null,
|
||||
"documentation": "https://github.com/dmunozv04/iSponsorBlockTV/wiki",
|
||||
"website": "https://github.com/dmunozv04/iSponsorBlockTV",
|
||||
"logo": "https://raw.githubusercontent.com/ajayyy/SponsorBlock/master/public/icons/IconSponsorBlocker512px.png",
|
||||
"config_path": "/var/lib/isponsorblocktv/config.json",
|
||||
"description": "iSponsorBlockTV connects to YouTube TV clients and automatically skips SponsorBlock segments, mutes ads, and presses the Skip Ad button when available.",
|
||||
"install_methods": [
|
||||
{
|
||||
"type": "default",
|
||||
"script": "ct/isponsorblocktv.sh",
|
||||
"resources": {
|
||||
"cpu": 1,
|
||||
"ram": 1024,
|
||||
"hdd": 4,
|
||||
"os": "debian",
|
||||
"version": "13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_credentials": {
|
||||
"username": null,
|
||||
"password": null
|
||||
},
|
||||
"notes": [
|
||||
{
|
||||
"text": "No web UI; run `iSponsorBlockTV setup` inside the container to configure.",
|
||||
"type": "info"
|
||||
},
|
||||
{
|
||||
"text": "SSDP auto-discovery requires multicast on your bridge; manual pairing works without it.",
|
||||
"type": "info"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"name": "Teleport",
|
||||
"slug": "teleport",
|
||||
"categories": [
|
||||
6
|
||||
],
|
||||
"date_created": "2026-03-18",
|
||||
"type": "ct",
|
||||
"updateable": true,
|
||||
"privileged": false,
|
||||
"interface_port": 3080,
|
||||
"documentation": "https://goteleport.com/docs/",
|
||||
"config_path": "/etc/teleport.yaml",
|
||||
"website": "https://goteleport.com/",
|
||||
"github": "https://github.com/gravitational/teleport",
|
||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/teleport.webp",
|
||||
"description": "Teleport unifies identities — humans, machines, and AI — with strong identity implementation to speed up engineering, improve resiliency against identity-based attacks, and control AI in production infrastructure.",
|
||||
"install_methods": [
|
||||
{
|
||||
"type": "default",
|
||||
"script": "ct/teleport.sh",
|
||||
"resources": {
|
||||
"cpu": 1,
|
||||
"ram": 1024,
|
||||
"hdd": 4,
|
||||
"os": "Debian",
|
||||
"version": "13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_credentials": {
|
||||
"username": null,
|
||||
"password": null
|
||||
},
|
||||
"notes": [
|
||||
{
|
||||
"text": "After installation finishes, do `cat ~/teleportadmin.creds` inside LXC to get admin creation URL.",
|
||||
"type": "info"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user