Compare commits
10 Commits
delete_fil
...
arm64-dev-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61446fb0fa | ||
|
|
e2425b5eb6 | ||
|
|
c14b73d346 | ||
|
|
20ae91a95c | ||
|
|
53d84b11fe | ||
|
|
26054d7c1b | ||
|
|
e515b844c3 | ||
|
|
fa78e82800 | ||
|
|
cb413b48a6 | ||
|
|
70ef0b792c |
7
.gitattributes
vendored
7
.gitattributes
vendored
@@ -10,6 +10,11 @@
|
||||
# Treat Golang files as Go (for /api/)
|
||||
api/**/*.go linguist-language=Go
|
||||
|
||||
# ---------------------------------------
|
||||
# Treat frontend as JavaScript/TypeScript (optional)
|
||||
frontend/**/*.ts linguist-language=TypeScript
|
||||
frontend/**/*.js linguist-language=JavaScript
|
||||
|
||||
# ---------------------------------------
|
||||
# Exclude documentation from stats
|
||||
*.md linguist-documentation
|
||||
@@ -21,7 +26,7 @@ SECURITY.md linguist-documentation
|
||||
# ---------------------------------------
|
||||
# Exclude generated/config files
|
||||
*.json linguist-generated
|
||||
json/*.json linguist-generated=false
|
||||
frontend/public/json/*.json linguist-generated=false
|
||||
*.lock linguist-generated
|
||||
*.yml linguist-generated
|
||||
*.yaml linguist-generated
|
||||
|
||||
2
.github/autolabeler-config.json
generated
vendored
2
.github/autolabeler-config.json
generated
vendored
@@ -97,7 +97,7 @@
|
||||
{
|
||||
"fileStatus": "modified",
|
||||
"includeGlobs": [
|
||||
"json/**"
|
||||
"frontend/public/json/**"
|
||||
],
|
||||
"excludeGlobs": []
|
||||
}
|
||||
|
||||
3
.github/pull_request_template.md
generated
vendored
3
.github/pull_request_template.md
generated
vendored
@@ -49,6 +49,3 @@ Link: #
|
||||
- [ ] The application has **600+ GitHub stars**
|
||||
- [ ] Official **release tarballs** are published
|
||||
- [ ] I understand that not all scripts will be accepted due to various reasons and criteria by the community-scripts ORG
|
||||
|
||||
## 🌐 Source
|
||||
<!-- Add any sources and github links. -->
|
||||
|
||||
2
.github/workflows/bak/get-versions-from-gh.yaml
generated
vendored
2
.github/workflows/bak/get-versions-from-gh.yaml
generated
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "GitHub Actions[bot]"
|
||||
git checkout -b update_versions || git checkout update_versions
|
||||
git add json/versions.json
|
||||
git add frontend/public/json/versions.json
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes detected."
|
||||
echo "changed=false" >> "$GITHUB_ENV"
|
||||
|
||||
4
.github/workflows/bak/get-versions-from-newreleases.yaml
generated
vendored
4
.github/workflows/bak/get-versions-from-newreleases.yaml
generated
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
run: |
|
||||
page=1
|
||||
projects_file="project_json"
|
||||
output_file="json/versions.json"
|
||||
output_file="frontend/public/json/versions.json"
|
||||
|
||||
echo "[]" > $output_file
|
||||
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "GitHub Actions[bot]"
|
||||
git checkout -b update_versions || git checkout update_versions
|
||||
git add json/versions.json
|
||||
git add frontend/public/json/versions.json
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes detected."
|
||||
echo "changed=false" >> "$GITHUB_ENV"
|
||||
|
||||
4
.github/workflows/bak/update-versions-github.yml
generated
vendored
4
.github/workflows/bak/update-versions-github.yml
generated
vendored
@@ -11,8 +11,8 @@ permissions:
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
SOURCES_FILE: json/version-sources.json
|
||||
VERSIONS_FILE: json/versions.json
|
||||
SOURCES_FILE: frontend/public/json/version-sources.json
|
||||
VERSIONS_FILE: frontend/public/json/versions.json
|
||||
|
||||
jobs:
|
||||
update-versions:
|
||||
|
||||
16
.github/workflows/create-ready-for-testing-message.yml
generated
vendored
16
.github/workflows/create-ready-for-testing-message.yml
generated
vendored
@@ -7,7 +7,6 @@ on:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
post_to_discord:
|
||||
@@ -60,19 +59,19 @@ jobs:
|
||||
FILES=(
|
||||
"ct/${TITLE}.sh"
|
||||
"install/${TITLE}-install.sh"
|
||||
"json/${TITLE}.json"
|
||||
"frontend/public/json/${TITLE}.json"
|
||||
)
|
||||
;;
|
||||
vm)
|
||||
FILES=(
|
||||
"vm/${TITLE}.sh"
|
||||
"json/${TITLE}.json"
|
||||
"frontend/public/json/${TITLE}.json"
|
||||
)
|
||||
;;
|
||||
addon)
|
||||
FILES=(
|
||||
"tools/addon/${TITLE}.sh"
|
||||
"json/${TITLE}.json"
|
||||
"frontend/public/json/${TITLE}.json"
|
||||
)
|
||||
;;
|
||||
pve)
|
||||
@@ -123,7 +122,7 @@ jobs:
|
||||
JSON_FILE=""
|
||||
case "$SCRIPT_TYPE" in
|
||||
ct|vm|addon)
|
||||
JSON_FILE="json/${TITLE}.json"
|
||||
JSON_FILE="frontend/public/json/${TITLE}.json"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -218,10 +217,3 @@ jobs:
|
||||
echo -e "$MESSAGE" > comment.txt
|
||||
sed -i '/Discussion & issue tracking:/,$d' comment.txt
|
||||
gh issue comment ${{ github.event.issue.number }} --repo ${{ github.repository }} --body-file comment.txt
|
||||
|
||||
- name: Push script JSON to PocketBase
|
||||
if: env.SCRIPT_TYPE == 'ct' || env.SCRIPT_TYPE == 'vm' || env.SCRIPT_TYPE == 'addon'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh workflow run push_json_to_pocketbase.yml --repo ${{ github.repository }} -f script_slug=${{ env.TITLE }}
|
||||
|
||||
8
.github/workflows/delete_new_script.yaml
generated
vendored
8
.github/workflows/delete_new_script.yaml
generated
vendored
@@ -124,20 +124,20 @@ jobs:
|
||||
rm -f "ct/${TITLE}.sh"
|
||||
rm -f "ct/headers/${TITLE}"
|
||||
rm -f "install/${TITLE}-install.sh"
|
||||
rm -f "json/${TITLE}.json"
|
||||
rm -f "frontend/public/json/${TITLE}.json"
|
||||
# Also try alpine variant
|
||||
if [[ "$TITLE" == alpine-* ]]; then
|
||||
stripped="${TITLE#alpine-}"
|
||||
rm -f "json/${stripped}.json"
|
||||
rm -f "frontend/public/json/${stripped}.json"
|
||||
fi
|
||||
;;
|
||||
vm)
|
||||
rm -f "vm/${TITLE}.sh"
|
||||
rm -f "json/${TITLE}.json"
|
||||
rm -f "frontend/public/json/${TITLE}.json"
|
||||
;;
|
||||
addon)
|
||||
rm -f "tools/addon/${TITLE}.sh"
|
||||
rm -f "json/${TITLE}.json"
|
||||
rm -f "frontend/public/json/${TITLE}.json"
|
||||
;;
|
||||
pve)
|
||||
rm -f "tools/pve/${TITLE}.sh"
|
||||
|
||||
77
.github/workflows/frontend-cicd.yml
generated
vendored
Normal file
77
.github/workflows/frontend-cicd.yml
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
# Based on https://github.com/actions/starter-workflows/blob/main/pages/nextjs.yml
|
||||
|
||||
name: Frontend CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- frontend/**
|
||||
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
paths:
|
||||
- frontend/**
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: pages-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.repository == 'community-scripts/ProxmoxVED'
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: frontend # Set default working directory for all run steps
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: npm
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --prefer-offline --legacy-peer-deps
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
- name: Configure Next.js for pages
|
||||
uses: actions/configure-pages@v5
|
||||
with:
|
||||
static_site_generator: next
|
||||
|
||||
- name: Build with Next.js
|
||||
run: npm run build
|
||||
|
||||
- name: Upload artifact
|
||||
if: github.ref == 'refs/heads/main'
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: frontend/out
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: github.ref == 'refs/heads/main' && github.repository == 'community-scripts/ProxmoxVED'
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
59
.github/workflows/move-to-main-repo.yaml
generated
vendored
59
.github/workflows/move-to-main-repo.yaml
generated
vendored
@@ -39,22 +39,11 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
echo "Resolving issue with label Migration To ProxmoxVE"
|
||||
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]')
|
||||
|
||||
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
|
||||
if [ "$filtered_issue" == "null" ] || [ -z "$filtered_issue" ]; then
|
||||
echo "No issues found with label 'Migration To ProxmoxVE'."
|
||||
exit 1
|
||||
fi
|
||||
@@ -111,6 +100,26 @@ jobs:
|
||||
files_found="false"
|
||||
missing_files+="install/${script_name}-install.sh "
|
||||
fi
|
||||
# JSON check with alpine fallback
|
||||
json_file="frontend/public/json/${script_name}.json"
|
||||
if [[ ! -f "$json_file" ]]; then
|
||||
if [[ "$script_name" == alpine-* ]]; then
|
||||
stripped_name="${script_name#alpine-}"
|
||||
alt_json="frontend/public/json/${stripped_name}.json"
|
||||
if [[ -f "$alt_json" ]]; then
|
||||
echo "Using alpine fallback JSON: $alt_json"
|
||||
echo "json_fallback=$alt_json" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "json file not found: $json_file"
|
||||
files_found="false"
|
||||
missing_files+="$json_file "
|
||||
fi
|
||||
else
|
||||
echo "json file not found: $json_file"
|
||||
files_found="false"
|
||||
missing_files+="$json_file "
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
vm)
|
||||
if [[ ! -f "vm/${script_name}.sh" ]]; then
|
||||
@@ -118,6 +127,11 @@ jobs:
|
||||
files_found="false"
|
||||
missing_files+="vm/${script_name}.sh "
|
||||
fi
|
||||
# JSON is optional for VMs but check anyway
|
||||
json_file="frontend/public/json/${script_name}.json"
|
||||
if [[ ! -f "$json_file" ]]; then
|
||||
echo "json file not found (optional): $json_file"
|
||||
fi
|
||||
;;
|
||||
addon)
|
||||
if [[ ! -f "tools/addon/${script_name}.sh" ]]; then
|
||||
@@ -125,6 +139,11 @@ jobs:
|
||||
files_found="false"
|
||||
missing_files+="tools/addon/${script_name}.sh "
|
||||
fi
|
||||
# JSON is optional for addons
|
||||
json_file="frontend/public/json/${script_name}.json"
|
||||
if [[ ! -f "$json_file" ]]; then
|
||||
echo "json file not found (optional): $json_file"
|
||||
fi
|
||||
;;
|
||||
pve)
|
||||
if [[ ! -f "tools/pve/${script_name}.sh" ]]; then
|
||||
@@ -171,6 +190,7 @@ jobs:
|
||||
run: |
|
||||
script_name="${{ steps.list_issues.outputs.script_name }}"
|
||||
script_type="${{ steps.list_issues.outputs.script_type }}"
|
||||
json_fallback="${{ steps.check_files.outputs.json_fallback }}"
|
||||
|
||||
git clone https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/community-scripts/ProxmoxVE.git ProxmoxVE
|
||||
cd ProxmoxVE
|
||||
@@ -207,6 +227,13 @@ jobs:
|
||||
cp ../ct/headers/${script_name} ct/headers/ 2>/dev/null || true
|
||||
cp ../install/${script_name}-install.sh install/
|
||||
|
||||
# Handle JSON with alpine fallback
|
||||
if [[ -n "$json_fallback" ]]; then
|
||||
cp ../${json_fallback} frontend/public/json/ || true
|
||||
else
|
||||
cp ../frontend/public/json/${script_name}.json frontend/public/json/ 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Update URLs in ct script
|
||||
sed -i "s|https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func|https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func|" ct/${script_name}.sh
|
||||
sed -i "s|https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func|https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func|" ct/${script_name}.sh
|
||||
@@ -215,6 +242,7 @@ jobs:
|
||||
;;
|
||||
vm)
|
||||
cp ../vm/${script_name}.sh vm/
|
||||
cp ../frontend/public/json/${script_name}.json frontend/public/json/ 2>/dev/null || true
|
||||
|
||||
# Update URLs in vm script
|
||||
sed -i "s|https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func|https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func|" vm/${script_name}.sh
|
||||
@@ -223,6 +251,7 @@ jobs:
|
||||
addon)
|
||||
mkdir -p tools/addon
|
||||
cp ../tools/addon/${script_name}.sh tools/addon/
|
||||
cp ../frontend/public/json/${script_name}.json frontend/public/json/ 2>/dev/null || true
|
||||
|
||||
# Update URLs in addon script
|
||||
sed -i "s|community-scripts/ProxmoxVED|community-scripts/ProxmoxVE|g" tools/addon/${script_name}.sh
|
||||
|
||||
676
.github/workflows/pocketbase-bot.yml
generated
vendored
676
.github/workflows/pocketbase-bot.yml
generated
vendored
@@ -1,676 +0,0 @@
|
||||
name: PocketBase Bot
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
pocketbase-bot:
|
||||
runs-on: self-hosted
|
||||
|
||||
# Only act on /pocketbase commands
|
||||
if: startsWith(github.event.comment.body, '/pocketbase')
|
||||
|
||||
steps:
|
||||
- name: Execute PocketBase bot command
|
||||
env:
|
||||
POCKETBASE_URL: ${{ secrets.POCKETBASE_URL }}
|
||||
POCKETBASE_COLLECTION: ${{ secrets.POCKETBASE_COLLECTION }}
|
||||
POCKETBASE_ADMIN_EMAIL: ${{ secrets.POCKETBASE_ADMIN_EMAIL }}
|
||||
POCKETBASE_ADMIN_PASSWORD: ${{ secrets.POCKETBASE_ADMIN_PASSWORD }}
|
||||
COMMENT_BODY: ${{ github.event.comment.body }}
|
||||
COMMENT_ID: ${{ github.event.comment.id }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
REPO_OWNER: ${{ github.repository_owner }}
|
||||
REPO_NAME: ${{ github.event.repository.name }}
|
||||
ACTOR: ${{ github.event.comment.user.login }}
|
||||
ACTOR_ASSOCIATION: ${{ github.event.comment.author_association }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
node << 'ENDSCRIPT'
|
||||
(async function () {
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
const url = require('url');
|
||||
|
||||
// ── HTTP helper with redirect following ────────────────────────────
|
||||
function request(fullUrl, opts, redirectCount) {
|
||||
redirectCount = redirectCount || 0;
|
||||
return new Promise(function (resolve, reject) {
|
||||
const u = url.parse(fullUrl);
|
||||
const isHttps = u.protocol === 'https:';
|
||||
const body = opts.body;
|
||||
const options = {
|
||||
hostname: u.hostname,
|
||||
port: u.port || (isHttps ? 443 : 80),
|
||||
path: u.path,
|
||||
method: opts.method || 'GET',
|
||||
headers: opts.headers || {}
|
||||
};
|
||||
if (body) options.headers['Content-Length'] = Buffer.byteLength(body);
|
||||
const lib = isHttps ? https : http;
|
||||
const req = lib.request(options, function (res) {
|
||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl));
|
||||
const redirectUrl = url.resolve(fullUrl, res.headers.location);
|
||||
res.resume();
|
||||
resolve(request(redirectUrl, opts, redirectCount + 1));
|
||||
return;
|
||||
}
|
||||
let data = '';
|
||||
res.on('data', function (chunk) { data += chunk; });
|
||||
res.on('end', function () {
|
||||
resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode, body: data });
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// ── GitHub API helpers ─────────────────────────────────────────────
|
||||
const owner = process.env.REPO_OWNER;
|
||||
const repo = process.env.REPO_NAME;
|
||||
const issueNumber = parseInt(process.env.ISSUE_NUMBER, 10);
|
||||
const commentId = parseInt(process.env.COMMENT_ID, 10);
|
||||
const actor = process.env.ACTOR;
|
||||
|
||||
function ghRequest(path, method, body) {
|
||||
const headers = {
|
||||
'Authorization': 'Bearer ' + process.env.GITHUB_TOKEN,
|
||||
'Accept': 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
'User-Agent': 'PocketBase-Bot'
|
||||
};
|
||||
const bodyStr = body ? JSON.stringify(body) : undefined;
|
||||
if (bodyStr) headers['Content-Type'] = 'application/json';
|
||||
return request('https://api.github.com' + path, { method: method || 'GET', headers, body: bodyStr });
|
||||
}
|
||||
|
||||
async function addReaction(content) {
|
||||
try {
|
||||
await ghRequest(
|
||||
'/repos/' + owner + '/' + repo + '/issues/comments/' + commentId + '/reactions',
|
||||
'POST', { content }
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn('Could not add reaction:', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function postComment(text) {
|
||||
const res = await ghRequest(
|
||||
'/repos/' + owner + '/' + repo + '/issues/' + issueNumber + '/comments',
|
||||
'POST', { body: text }
|
||||
);
|
||||
if (!res.ok) console.warn('Could not post comment:', res.body);
|
||||
}
|
||||
|
||||
// ── Permission check ───────────────────────────────────────────────
|
||||
// author_association: OWNER = repo/org owner, MEMBER = org member (includes Contributors team)
|
||||
const association = process.env.ACTOR_ASSOCIATION;
|
||||
if (association !== 'OWNER' && association !== 'MEMBER') {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: @' + actor + ' is not authorized to use this command.\n' +
|
||||
'Only org members (Contributors team) can use `/pocketbase`.'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// ── Acknowledge ────────────────────────────────────────────────────
|
||||
await addReaction('eyes');
|
||||
|
||||
// ── Parse command ──────────────────────────────────────────────────
|
||||
// Formats (first line of comment):
|
||||
// /pocketbase <slug> field=value [field=value ...] ← field updates (simple values)
|
||||
// /pocketbase <slug> set <field> ← value from code block below
|
||||
// /pocketbase <slug> note list|add|edit|remove ... ← note management
|
||||
// /pocketbase <slug> method list ← list install methods
|
||||
// /pocketbase <slug> method <type> cpu=N ram=N hdd=N ← edit install method resources
|
||||
const commentBody = process.env.COMMENT_BODY || '';
|
||||
const lines = commentBody.trim().split('\n');
|
||||
const firstLine = lines[0].trim();
|
||||
const withoutCmd = firstLine.replace(/^\/pocketbase\s+/, '').trim();
|
||||
|
||||
// Extract code block content from comment body (```...``` or ```lang\n...```)
|
||||
function extractCodeBlock(body) {
|
||||
const m = body.match(/```[^\n]*\n([\s\S]*?)```/);
|
||||
return m ? m[1].trim() : null;
|
||||
}
|
||||
const codeBlockValue = extractCodeBlock(commentBody);
|
||||
|
||||
const HELP_TEXT =
|
||||
'**Field update (simple):** `/pocketbase <slug> field=value [field=value ...]`\n\n' +
|
||||
'**Field update (HTML/multiline) — value from code block:**\n' +
|
||||
'````\n' +
|
||||
'/pocketbase <slug> set description\n' +
|
||||
'```html\n' +
|
||||
'<p>Your <b>HTML</b> or multi-line content here</p>\n' +
|
||||
'```\n' +
|
||||
'````\n\n' +
|
||||
'**Note management:**\n' +
|
||||
'```\n' +
|
||||
'/pocketbase <slug> note list\n' +
|
||||
'/pocketbase <slug> note add <type> "<text>"\n' +
|
||||
'/pocketbase <slug> note edit <type> "<old text>" "<new text>"\n' +
|
||||
'/pocketbase <slug> note remove <type> "<text>"\n' +
|
||||
'```\n\n' +
|
||||
'**Install method resources:**\n' +
|
||||
'```\n' +
|
||||
'/pocketbase <slug> method list\n' +
|
||||
'/pocketbase <slug> method <type> hdd=10\n' +
|
||||
'/pocketbase <slug> method <type> cpu=4 ram=2048 hdd=20\n' +
|
||||
'```\n\n' +
|
||||
'**Editable fields:** `name` `description` `logo` `documentation` `website` `project_url` `github` ' +
|
||||
'`config_path` `port` `default_user` `default_passwd` ' +
|
||||
'`updateable` `privileged` `has_arm` `is_dev` ' +
|
||||
'`is_disabled` `disable_message` `is_deleted` `deleted_message`';
|
||||
|
||||
if (!withoutCmd) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: No slug or command specified.\n\n' + HELP_TEXT);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const spaceIdx = withoutCmd.indexOf(' ');
|
||||
const slug = (spaceIdx === -1 ? withoutCmd : withoutCmd.substring(0, spaceIdx)).trim();
|
||||
const rest = spaceIdx === -1 ? '' : withoutCmd.substring(spaceIdx + 1).trim();
|
||||
|
||||
if (!rest) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: No command specified for slug `' + slug + '`.\n\n' + HELP_TEXT);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// ── Allowed fields and their types ─────────────────────────────────
|
||||
// ── PocketBase: authenticate (shared by all paths) ─────────────────
|
||||
const raw = process.env.POCKETBASE_URL.replace(/\/$/, '');
|
||||
const apiBase = /\/api$/i.test(raw) ? raw : raw + '/api';
|
||||
const coll = process.env.POCKETBASE_COLLECTION;
|
||||
|
||||
const authRes = await request(apiBase + '/collections/users/auth-with-password', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
identity: process.env.POCKETBASE_ADMIN_EMAIL,
|
||||
password: process.env.POCKETBASE_ADMIN_PASSWORD
|
||||
})
|
||||
});
|
||||
if (!authRes.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: PocketBase authentication failed. CC @' + owner + '/maintainers');
|
||||
process.exit(1);
|
||||
}
|
||||
const token = JSON.parse(authRes.body).token;
|
||||
|
||||
// ── PocketBase: find record by slug (shared by all paths) ──────────
|
||||
const recordsUrl = apiBase + '/collections/' + encodeURIComponent(coll) + '/records';
|
||||
const filter = "(slug='" + slug.replace(/'/g, "''") + "')";
|
||||
const listRes = await request(recordsUrl + '?filter=' + encodeURIComponent(filter) + '&perPage=1', {
|
||||
headers: { 'Authorization': token }
|
||||
});
|
||||
const list = JSON.parse(listRes.body);
|
||||
const record = list.items && list.items[0];
|
||||
|
||||
if (!record) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: No record found for slug `' + slug + '`.\n\n' +
|
||||
'Make sure the script was already pushed to PocketBase (JSON must exist and have been synced).'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// ── Route: dispatch to subcommand handler ──────────────────────────
|
||||
const noteMatch = rest.match(/^note\s+(list|add|edit|remove)\b/i);
|
||||
const methodMatch = rest.match(/^method\b/i);
|
||||
const setMatch = rest.match(/^set\s+(\S+)/i);
|
||||
|
||||
if (noteMatch) {
|
||||
// ── NOTE SUBCOMMAND (reads/writes notes_json 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
|
||||
// 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;
|
||||
}
|
||||
|
||||
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');
|
||||
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+(.+)$/);
|
||||
if (!methodParts) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: Invalid `method` syntax.\n\n' +
|
||||
'**Usage:**\n```\n/pocketbase ' + slug + ' method list\n/pocketbase ' + slug + ' method <type> hdd=10\n/pocketbase ' + slug + ' method <type> cpu=4 ram=2048 hdd=20\n```'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const targetType = methodParts[1].toLowerCase();
|
||||
const resourcesStr = methodParts[2];
|
||||
|
||||
// Parse resource fields (only cpu/ram/hdd allowed)
|
||||
const RESOURCE_FIELDS = { cpu: true, ram: true, hdd: true };
|
||||
const resourceChanges = {};
|
||||
const rePairs = /([a-z]+)=(\d+)/gi;
|
||||
let m;
|
||||
while ((m = rePairs.exec(resourcesStr)) !== null) {
|
||||
const key = m[1].toLowerCase();
|
||||
if (RESOURCE_FIELDS[key]) resourceChanges[key] = parseInt(m[2], 10);
|
||||
}
|
||||
if (Object.keys(resourceChanges).length === 0) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: No valid resource fields found. Use `cpu=N`, `ram=N`, `hdd=N`.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Find matching method by type name (case-insensitive)
|
||||
const idx = methodsArr.findIndex(function (im) {
|
||||
return (im.type || '').toLowerCase() === targetType;
|
||||
});
|
||||
if (idx === -1) {
|
||||
await addReaction('-1');
|
||||
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.length ? availableTypes.join('`, `') : '(none)') + '`\n\n' +
|
||||
'Use `/pocketbase ' + slug + ' method list` to see all methods.'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
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 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 **`' + methodsArr[idx].type + '`** for **`' + slug + '`**\n\n' +
|
||||
'**Changes applied:**\n' + changesLines + '\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
}
|
||||
|
||||
} else if (setMatch) {
|
||||
// ── SET SUBCOMMAND (multi-line / HTML / special chars via code block) ──
|
||||
const fieldName = setMatch[1].toLowerCase();
|
||||
const SET_ALLOWED = {
|
||||
name: 'string', description: 'string', logo: 'string',
|
||||
documentation: 'string', website: 'string', project_url: 'string', github: 'string',
|
||||
config_path: 'string', disable_message: 'string', deleted_message: 'string'
|
||||
};
|
||||
if (!SET_ALLOWED[fieldName]) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: `set` only supports text fields.\n\n' +
|
||||
'**Allowed:** `' + Object.keys(SET_ALLOWED).join('`, `') + '`\n\n' +
|
||||
'For boolean/number fields use `field=value` syntax instead.'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
if (!codeBlockValue) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: `set` requires a code block with the value.\n\n' +
|
||||
'**Usage:**\n````\n/pocketbase ' + slug + ' set ' + fieldName + '\n```\nYour content here (HTML, multiline, special chars all fine)\n```\n````'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
const setPayload = {};
|
||||
setPayload[fieldName] = codeBlockValue;
|
||||
const setPatchRes = await request(recordsUrl + '/' + record.id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(setPayload)
|
||||
});
|
||||
if (!setPatchRes.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: PATCH failed for `' + slug + '`:\n```\n' + setPatchRes.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
const preview = codeBlockValue.length > 300 ? codeBlockValue.substring(0, 300) + '…' : codeBlockValue;
|
||||
await addReaction('+1');
|
||||
await postComment(
|
||||
'✅ **PocketBase Bot**: Set `' + fieldName + '` for **`' + slug + '`**\n\n' +
|
||||
'**Value set:**\n```\n' + preview + '\n```\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
|
||||
} else {
|
||||
// ── FIELD=VALUE PATH ─────────────────────────────────────────────
|
||||
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)
|
||||
const ALLOWED_FIELDS = {
|
||||
name: 'string',
|
||||
description: 'string',
|
||||
logo: 'string',
|
||||
documentation: 'string',
|
||||
website: 'string',
|
||||
project_url: 'string',
|
||||
github: 'string',
|
||||
config_path: 'string',
|
||||
port: 'number',
|
||||
default_user: 'nullable_string',
|
||||
default_passwd: 'nullable_string',
|
||||
updateable: 'boolean',
|
||||
privileged: 'boolean',
|
||||
has_arm: 'boolean',
|
||||
is_dev: 'boolean',
|
||||
is_disabled: 'boolean',
|
||||
disable_message: 'string',
|
||||
is_deleted: 'boolean',
|
||||
deleted_message: 'string',
|
||||
version: 'string',
|
||||
};
|
||||
|
||||
// Field=value parser (handles quoted values and empty=null)
|
||||
function parseFields(str) {
|
||||
const fields = {};
|
||||
let pos = 0;
|
||||
while (pos < str.length) {
|
||||
while (pos < str.length && /\s/.test(str[pos])) pos++;
|
||||
if (pos >= str.length) break;
|
||||
let keyStart = pos;
|
||||
while (pos < str.length && str[pos] !== '=' && !/\s/.test(str[pos])) pos++;
|
||||
const key = str.substring(keyStart, pos).trim();
|
||||
if (!key || pos >= str.length || str[pos] !== '=') { pos++; continue; }
|
||||
pos++;
|
||||
let value;
|
||||
if (str[pos] === '"') {
|
||||
pos++;
|
||||
let valStart = pos;
|
||||
while (pos < str.length && str[pos] !== '"') {
|
||||
if (str[pos] === '\\') pos++;
|
||||
pos++;
|
||||
}
|
||||
value = str.substring(valStart, pos).replace(/\\"/g, '"');
|
||||
if (pos < str.length) pos++;
|
||||
} else {
|
||||
let valStart = pos;
|
||||
while (pos < str.length && !/\s/.test(str[pos])) pos++;
|
||||
value = str.substring(valStart, pos);
|
||||
}
|
||||
fields[key] = value;
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
const parsedFields = parseFields(fieldsStr);
|
||||
|
||||
const unknownFields = Object.keys(parsedFields).filter(function (f) { return !ALLOWED_FIELDS[f]; });
|
||||
if (unknownFields.length > 0) {
|
||||
await addReaction('-1');
|
||||
await postComment(
|
||||
'❌ **PocketBase Bot**: Unknown field(s): `' + unknownFields.join('`, `') + '`\n\n' +
|
||||
'**Allowed fields:** `' + Object.keys(ALLOWED_FIELDS).join('`, `') + '`'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (Object.keys(parsedFields).length === 0) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: Could not parse any valid `field=value` pairs.\n\n' + HELP_TEXT);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Cast values to correct types
|
||||
const payload = {};
|
||||
for (const [key, rawVal] of Object.entries(parsedFields)) {
|
||||
const type = ALLOWED_FIELDS[key];
|
||||
if (type === 'boolean') {
|
||||
if (rawVal === 'true') payload[key] = true;
|
||||
else if (rawVal === 'false') payload[key] = false;
|
||||
else {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: `' + key + '` must be `true` or `false`, got: `' + rawVal + '`');
|
||||
process.exit(0);
|
||||
}
|
||||
} else if (type === 'number') {
|
||||
const n = parseInt(rawVal, 10);
|
||||
if (isNaN(n)) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: `' + key + '` must be a number, got: `' + rawVal + '`');
|
||||
process.exit(0);
|
||||
}
|
||||
payload[key] = n;
|
||||
} else if (type === 'nullable_string') {
|
||||
payload[key] = rawVal === '' ? null : rawVal;
|
||||
} else {
|
||||
payload[key] = rawVal;
|
||||
}
|
||||
}
|
||||
|
||||
const patchRes = await request(recordsUrl + '/' + record.id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
if (!patchRes.ok) {
|
||||
await addReaction('-1');
|
||||
await postComment('❌ **PocketBase Bot**: PATCH failed for `' + slug + '`:\n```\n' + patchRes.body + '\n```');
|
||||
process.exit(1);
|
||||
}
|
||||
await addReaction('+1');
|
||||
const changesLines = Object.entries(payload)
|
||||
.map(function ([k, v]) { return '- `' + k + '` → `' + JSON.stringify(v) + '`'; })
|
||||
.join('\n');
|
||||
await postComment(
|
||||
'✅ **PocketBase Bot**: Updated **`' + slug + '`** successfully!\n\n' +
|
||||
'**Changes applied:**\n' + changesLines + '\n\n' +
|
||||
'*Executed by @' + actor + '*'
|
||||
);
|
||||
}
|
||||
|
||||
console.log('Done.');
|
||||
})().catch(function (e) {
|
||||
console.error('Fatal error:', e.message || e);
|
||||
process.exit(1);
|
||||
});
|
||||
ENDSCRIPT
|
||||
shell: bash
|
||||
39
.github/workflows/push-to-gitea.yml
generated
vendored
Normal file
39
.github/workflows/push-to-gitea.yml
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Sync to Gitea
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
if: github.repository == 'community-scripts/ProxmoxVED'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout source repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set Git identity for actions
|
||||
run: |
|
||||
git config --global user.name "Push From Github"
|
||||
git config --global user.email "actions@github.com"
|
||||
- name: Add Gitea remote
|
||||
run: git remote add gitea https://$GITEA_USER:$GITEA_TOKEN@git.community-scripts.org/community-scripts/ProxmoxVED.git
|
||||
env:
|
||||
GITEA_USER: ${{ secrets.GITEA_USERNAME }}
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
- name: Pull Gitea changes
|
||||
run: |
|
||||
git fetch gitea
|
||||
git merge --strategy=ours gitea/main
|
||||
env:
|
||||
GITEA_USER: ${{ secrets.GITEA_USERNAME }}
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
|
||||
- name: Push to Gitea
|
||||
run: git push gitea main --force
|
||||
env:
|
||||
GITEA_USER: ${{ secrets.GITEA_USERNAME }}
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
160
.github/workflows/push_json_to_pocketbase.yml
generated
vendored
160
.github/workflows/push_json_to_pocketbase.yml
generated
vendored
@@ -5,13 +5,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "json/*.json"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
script_slug:
|
||||
description: "Script slug (e.g. my-app)"
|
||||
required: true
|
||||
type: string
|
||||
- "frontend/public/json/**"
|
||||
|
||||
jobs:
|
||||
push-json:
|
||||
@@ -22,55 +16,23 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get JSON file for script
|
||||
- name: Get changed JSON files with slug
|
||||
id: changed
|
||||
run: |
|
||||
: > 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
|
||||
|
||||
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
|
||||
changed=$(git diff --name-only "${{ github.event.before }}" "${{ github.event.after }}" -- frontend/public/json/ | grep '\.json$' || true)
|
||||
with_slug=""
|
||||
for f in $changed; do
|
||||
[[ -f "$f" ]] || continue
|
||||
jq -e '.slug' "$f" >/dev/null 2>&1 && with_slug="$with_slug $f"
|
||||
done
|
||||
|
||||
if [[ $count -eq 0 ]]; then
|
||||
echo "No app JSON files with .slug found in this push."
|
||||
with_slug=$(echo $with_slug | xargs -n1)
|
||||
if [[ -z "$with_slug" ]]; then
|
||||
echo "No app JSON files changed (or no files with slug)."
|
||||
echo "count=0" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "count=$count" >> "$GITHUB_OUTPUT"
|
||||
echo "$with_slug" > changed_app_jsons.txt
|
||||
echo "count=$(echo "$with_slug" | wc -w)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Push to PocketBase
|
||||
if: steps.changed.outputs.count != '0'
|
||||
@@ -86,8 +48,7 @@ jobs:
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
const url = require('url');
|
||||
function request(fullUrl, opts, redirectCount) {
|
||||
redirectCount = redirectCount || 0;
|
||||
function request(fullUrl, opts) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
const u = url.parse(fullUrl);
|
||||
const isHttps = u.protocol === 'https:';
|
||||
@@ -102,13 +63,6 @@ jobs:
|
||||
if (body) options.headers['Content-Length'] = Buffer.byteLength(body);
|
||||
const lib = isHttps ? https : http;
|
||||
const req = lib.request(options, function(res) {
|
||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl));
|
||||
const redirectUrl = url.resolve(fullUrl, res.headers.location);
|
||||
res.resume();
|
||||
resolve(request(redirectUrl, opts, redirectCount + 1));
|
||||
return;
|
||||
}
|
||||
let data = '';
|
||||
res.on('data', function(chunk) { data += chunk; });
|
||||
res.on('end', function() {
|
||||
@@ -142,7 +96,7 @@ jobs:
|
||||
const recordsUrl = apiBase + '/collections/' + encodeURIComponent(coll) + '/records';
|
||||
let categoryIdToName = {};
|
||||
try {
|
||||
const metadata = JSON.parse(fs.readFileSync('json/metadata.json', 'utf8'));
|
||||
const metadata = JSON.parse(fs.readFileSync('frontend/public/json/metadata.json', 'utf8'));
|
||||
(metadata.categories || []).forEach(function(cat) { categoryIdToName[cat.id] = cat.name; });
|
||||
} catch (e) { console.warn('Could not load metadata.json:', e.message); }
|
||||
let typeValueToId = {};
|
||||
@@ -171,32 +125,22 @@ jobs:
|
||||
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; }
|
||||
});
|
||||
if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.type != null) noteTypeToId[item.type] = 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; }
|
||||
});
|
||||
if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.type != null) installMethodTypeToId[item.type] = 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; }
|
||||
});
|
||||
if (res.ok) JSON.parse(res.body).items?.forEach(function(item) { if (item.os != null) osToId[item.os] = 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;
|
||||
}
|
||||
if (osName != null && item.version != null) osVersionToId[osName + '|' + item.version] = item.id;
|
||||
});
|
||||
}
|
||||
} catch (e) { console.warn('z_ref_os_version:', e.message); }
|
||||
@@ -206,40 +150,11 @@ jobs:
|
||||
if (!fs.existsSync(file)) continue;
|
||||
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
||||
if (!data.slug) { console.log('Skipping', file, '(no slug)'); continue; }
|
||||
// execute_in: map type to canonical value
|
||||
var executeInMap = { ct: 'lxc', lxc: 'lxc', turnkey: 'turnkey', pve: 'pve', addon: 'addon', vm: 'vm' };
|
||||
var executeIn = data.type ? (executeInMap[data.type.toLowerCase()] || data.type.toLowerCase()) : 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$/, '');
|
||||
}
|
||||
// last_update_commit: last commit touching the actual script files (ct/slug.sh, install/slug-install.sh, vm/slug.sh, etc.)
|
||||
var lastCommit = null;
|
||||
try {
|
||||
var cp = require('child_process');
|
||||
var scriptFiles = [];
|
||||
// primary script from install_methods[].script (e.g. "ct/teleport.sh", "vm/teleport.sh")
|
||||
(data.install_methods || []).forEach(function(im) {
|
||||
if (im.script) scriptFiles.push(im.script);
|
||||
});
|
||||
// derive install script from slug (install/slug-install.sh)
|
||||
scriptFiles.push('install/' + data.slug + '-install.sh');
|
||||
// filter to only files that actually exist in git
|
||||
var existingFiles = scriptFiles.filter(function(f) {
|
||||
try { cp.execSync('git ls-files --error-unmatch ' + f, { stdio: 'ignore' }); return true; } catch(e) { return false; }
|
||||
});
|
||||
if (existingFiles.length > 0) {
|
||||
lastCommit = cp.execSync('git log -1 --format=%H -- ' + existingFiles.join(' ')).toString().trim() || null;
|
||||
}
|
||||
} catch(e) { console.warn('Could not get last commit:', e.message); }
|
||||
var payload = {
|
||||
name: data.name,
|
||||
slug: data.slug,
|
||||
script_created: data.date_created || data.script_created,
|
||||
script_updated: new Date().toISOString().split('T')[0],
|
||||
script_updated: data.date_created || data.script_updated,
|
||||
updateable: data.updateable,
|
||||
privileged: data.privileged,
|
||||
port: data.interface_port != null ? data.interface_port : data.port,
|
||||
@@ -248,16 +163,10 @@ jobs:
|
||||
logo: data.logo,
|
||||
description: data.description,
|
||||
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 || []),
|
||||
default_user: (data.default_credentials && data.default_credentials.username) || data.default_user,
|
||||
default_passwd: (data.default_credentials && data.default_credentials.password) || data.default_passwd,
|
||||
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'];
|
||||
if (resolvedType) payload.type = resolvedType;
|
||||
@@ -276,27 +185,23 @@ jobs:
|
||||
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 typeId = noteTypeToId[note.type];
|
||||
if (typeId == null) 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 })
|
||||
body: JSON.stringify({ text: note.text || '', type: typeId })
|
||||
});
|
||||
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 typeId = installMethodTypeToId[im.type];
|
||||
var res = im.resources || {};
|
||||
var osId = osToId[res.os] || (res.os && osToId[res.os.toLowerCase()]);
|
||||
var osId = osToId[res.os];
|
||||
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 osVersionId = osVersionKey ? osVersionToId[osVersionKey] : null;
|
||||
var imBody = {
|
||||
script: scriptId,
|
||||
resources_cpu: res.cpu != null ? res.cpu : 0,
|
||||
@@ -312,7 +217,6 @@ jobs:
|
||||
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 };
|
||||
}
|
||||
@@ -321,7 +225,6 @@ jobs:
|
||||
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' },
|
||||
@@ -338,19 +241,12 @@ jobs:
|
||||
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)
|
||||
body: JSON.stringify({ install_methods: resolved.installMethodIds, notes: resolved.noteIds })
|
||||
});
|
||||
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.');
|
||||
|
||||
2
.github/workflows/scripts/get-gh-release.sh
generated
vendored
2
.github/workflows/scripts/get-gh-release.sh
generated
vendored
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
INPUT_FILE=".github/workflows/scripts/repos.txt"
|
||||
OUTPUT_FILE="json/versions.json"
|
||||
OUTPUT_FILE="frontend/public/json/versions.json"
|
||||
TMP_FILE="releases_tmp.json"
|
||||
|
||||
if [ -f "$OUTPUT_FILE" ]; then
|
||||
|
||||
41
.github/workflows/trigger_github_pages_redirect.yml
generated
vendored
41
.github/workflows/trigger_github_pages_redirect.yml
generated
vendored
@@ -1,41 +0,0 @@
|
||||
name: Pages Redirect
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Create redirect page
|
||||
run: |
|
||||
mkdir site
|
||||
cat <<EOF > site/index.html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="refresh" content="0; url=https://community-scripts.org/">
|
||||
<link rel="canonical" href="https://community-scripts.org/">
|
||||
<title>Redirecting...</title>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting...
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
- uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: site
|
||||
|
||||
- name: Deploy
|
||||
uses: actions/deploy-pages@v4
|
||||
89
.github/workflows/unmet-pr-close.yml
generated
vendored
89
.github/workflows/unmet-pr-close.yml
generated
vendored
@@ -1,89 +0,0 @@
|
||||
name: PR Script Requirements Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate-script-requirements:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Validate new script requirements
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const body = context.payload.pull_request.body || "";
|
||||
const lines = body.split("\n");
|
||||
|
||||
function checkboxChecked(line) {
|
||||
return /\[\s*x\s*\]/i.test(line);
|
||||
}
|
||||
|
||||
function findLine(text) {
|
||||
return lines.find(l => l.includes(text));
|
||||
}
|
||||
|
||||
// detect if "New script" is checked
|
||||
const newScriptLine = findLine("🆕 **New script**");
|
||||
if (!newScriptLine || !checkboxChecked(newScriptLine)) {
|
||||
console.log("Not a new script PR — skipping requirement check.");
|
||||
return;
|
||||
}
|
||||
|
||||
const requirements = [
|
||||
"The application is **at least 6 months old**",
|
||||
"The application is **actively maintained**",
|
||||
"The application has **600+ GitHub stars**",
|
||||
"Official **release tarballs** are published",
|
||||
"I understand that not all scripts will be accepted"
|
||||
];
|
||||
|
||||
const missing = [];
|
||||
|
||||
for (const req of requirements) {
|
||||
const line = findLine(req);
|
||||
if (!line || !checkboxChecked(line)) {
|
||||
missing.push(req);
|
||||
}
|
||||
}
|
||||
|
||||
if (missing.length > 0) {
|
||||
|
||||
let list = "";
|
||||
for (const m of missing) {
|
||||
list += "- " + m + "\n";
|
||||
}
|
||||
|
||||
const message =
|
||||
"❌ **Pull Request Closed – Application Requirements Not Met**\n\n" +
|
||||
"This pull request is marked as **🆕 New script**, but the required application criteria were not confirmed.\n\n" +
|
||||
"The following requirement confirmations are missing:\n\n" +
|
||||
list +
|
||||
"\nNew application submissions must meet the project requirements before being considered.\n" +
|
||||
"Please wait until the application satisfies the criteria before submitting a new PR.\n\n" +
|
||||
"---\n\n" +
|
||||
"⚠ **Maintainer note**\n\n" +
|
||||
"The team periodically reviews closed submissions. If a project is still considered valuable to the ecosystem, maintainers may reopen the PR even if it does not fully meet the thresholds.\n\n" +
|
||||
"**Please do not ping or repeatedly contact maintainers to reopen PRs.**";
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: message
|
||||
});
|
||||
|
||||
await github.rest.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number,
|
||||
state: "closed"
|
||||
});
|
||||
|
||||
core.setFailed("Application requirements checklist incomplete.");
|
||||
}
|
||||
218
.github/workflows/update-github-versions.yml
generated
vendored
Normal file
218
.github/workflows/update-github-versions.yml
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
name: Update GitHub Versions (New)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Runs 4x daily: 00:00, 06:00, 12:00, 18:00 UTC
|
||||
- cron: "0 0,6,12,18 * * *"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
VERSIONS_FILE: frontend/public/json/github-versions.json
|
||||
|
||||
jobs:
|
||||
update-github-versions:
|
||||
if: github.repository == 'community-scripts/ProxmoxVED'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Extract GitHub versions from install scripts
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "========================================="
|
||||
echo " Extracting GitHub versions from scripts"
|
||||
echo "========================================="
|
||||
|
||||
# Initialize versions array
|
||||
versions_json="[]"
|
||||
|
||||
# Function to add a version entry
|
||||
add_version() {
|
||||
local slug="$1"
|
||||
local repo="$2"
|
||||
local version="$3"
|
||||
local pinned="$4"
|
||||
local date="$5"
|
||||
|
||||
versions_json=$(echo "$versions_json" | jq \
|
||||
--arg slug "$slug" \
|
||||
--arg repo "$repo" \
|
||||
--arg version "$version" \
|
||||
--argjson pinned "$pinned" \
|
||||
--arg date "$date" \
|
||||
'. += [{"slug": $slug, "repo": $repo, "version": $version, "pinned": $pinned, "date": $date}]')
|
||||
}
|
||||
|
||||
# Get list of slugs from JSON files
|
||||
echo ""
|
||||
echo "=== Scanning JSON files for slugs ==="
|
||||
|
||||
for json_file in frontend/public/json/*.json; do
|
||||
[[ ! -f "$json_file" ]] && continue
|
||||
|
||||
# Skip non-app JSON files
|
||||
basename_file=$(basename "$json_file")
|
||||
case "$basename_file" in
|
||||
metadata.json|versions.json|github-versions.json|dependency-check.json|update-apps.json)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
# Extract slug from JSON
|
||||
slug=$(jq -r '.slug // empty' "$json_file" 2>/dev/null)
|
||||
[[ -z "$slug" ]] && continue
|
||||
|
||||
# Find corresponding install script
|
||||
install_script="install/${slug}-install.sh"
|
||||
[[ ! -f "$install_script" ]] && continue
|
||||
|
||||
# Look for fetch_and_deploy_gh_release calls
|
||||
# Pattern: fetch_and_deploy_gh_release "app" "owner/repo" ["mode"] ["version"]
|
||||
while IFS= read -r line; do
|
||||
# Skip commented lines
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
|
||||
# Extract repo and version from fetch_and_deploy_gh_release
|
||||
if [[ "$line" =~ fetch_and_deploy_gh_release[[:space:]]+\"[^\"]*\"[[:space:]]+\"([^\"]+)\"([[:space:]]+\"([^\"]+)\")?([[:space:]]+\"([^\"]+)\")? ]]; then
|
||||
repo="${BASH_REMATCH[1]}"
|
||||
mode="${BASH_REMATCH[3]:-tarball}"
|
||||
pinned_version="${BASH_REMATCH[5]:-latest}"
|
||||
|
||||
# Check if version is pinned (not "latest" and not empty)
|
||||
is_pinned=false
|
||||
target_version=""
|
||||
|
||||
if [[ -n "$pinned_version" && "$pinned_version" != "latest" ]]; then
|
||||
is_pinned=true
|
||||
target_version="$pinned_version"
|
||||
fi
|
||||
|
||||
# Fetch version from GitHub
|
||||
if [[ "$is_pinned" == "true" ]]; then
|
||||
# For pinned versions, verify it exists and get date
|
||||
response=$(gh api "repos/${repo}/releases/tags/${target_version}" 2>/dev/null || echo '{}')
|
||||
if echo "$response" | jq -e '.tag_name' > /dev/null 2>&1; then
|
||||
version=$(echo "$response" | jq -r '.tag_name')
|
||||
date=$(echo "$response" | jq -r '.published_at // empty')
|
||||
add_version "$slug" "$repo" "$version" "true" "$date"
|
||||
echo "[$slug] ✓ $version (pinned)"
|
||||
else
|
||||
echo "[$slug] ⚠ pinned version $target_version not found"
|
||||
fi
|
||||
else
|
||||
# Fetch latest release
|
||||
response=$(gh api "repos/${repo}/releases/latest" 2>/dev/null || echo '{}')
|
||||
if echo "$response" | jq -e '.tag_name' > /dev/null 2>&1; then
|
||||
version=$(echo "$response" | jq -r '.tag_name')
|
||||
date=$(echo "$response" | jq -r '.published_at // empty')
|
||||
add_version "$slug" "$repo" "$version" "false" "$date"
|
||||
echo "[$slug] ✓ $version"
|
||||
else
|
||||
# Try tags as fallback
|
||||
version=$(gh api "repos/${repo}/tags" --jq '.[0].name // empty' 2>/dev/null || echo "")
|
||||
if [[ -n "$version" ]]; then
|
||||
add_version "$slug" "$repo" "$version" "false" ""
|
||||
echo "[$slug] ✓ $version (from tags)"
|
||||
else
|
||||
echo "[$slug] ⚠ no version found"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
break # Only first match per script
|
||||
fi
|
||||
done < <(grep 'fetch_and_deploy_gh_release' "$install_script" 2>/dev/null || true)
|
||||
|
||||
done
|
||||
|
||||
# Save versions file
|
||||
echo "$versions_json" | jq --arg date "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
'{generated: $date, versions: (. | sort_by(.slug))}' > "$VERSIONS_FILE"
|
||||
|
||||
total=$(echo "$versions_json" | jq 'length')
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo " Total versions extracted: $total"
|
||||
echo "========================================="
|
||||
|
||||
- name: Check for changes
|
||||
id: check-changes
|
||||
run: |
|
||||
# Check if file is new (untracked) or has changes
|
||||
if [[ ! -f "$VERSIONS_FILE" ]]; then
|
||||
echo "changed=false" >> "$GITHUB_OUTPUT"
|
||||
echo "Versions file was not created"
|
||||
elif ! git ls-files --error-unmatch "$VERSIONS_FILE" &>/dev/null; then
|
||||
# File exists but is not tracked - it's new
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
echo "New file created: $VERSIONS_FILE"
|
||||
elif git diff --quiet "$VERSIONS_FILE" 2>/dev/null; then
|
||||
echo "changed=false" >> "$GITHUB_OUTPUT"
|
||||
echo "No changes detected"
|
||||
else
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
echo "Changes detected:"
|
||||
git diff --stat "$VERSIONS_FILE" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Create Pull Request
|
||||
if: steps.check-changes.outputs.changed == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
BRANCH_NAME="automated/update-github-versions-$(date +%Y%m%d)"
|
||||
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "GitHub Actions[bot]"
|
||||
|
||||
# Check if branch exists and delete it
|
||||
git push origin --delete "$BRANCH_NAME" 2>/dev/null || true
|
||||
|
||||
git checkout -b "$BRANCH_NAME"
|
||||
git add "$VERSIONS_FILE"
|
||||
git commit -m "chore: update github-versions.json
|
||||
|
||||
Total versions: $(jq '.versions | length' "$VERSIONS_FILE")
|
||||
Pinned versions: $(jq '[.versions[] | select(.pinned == true)] | length' "$VERSIONS_FILE")
|
||||
Generated: $(jq -r '.generated' "$VERSIONS_FILE")"
|
||||
|
||||
git push origin "$BRANCH_NAME" --force
|
||||
|
||||
# Check if PR already exists
|
||||
existing_pr=$(gh pr list --head "$BRANCH_NAME" --state open --json number --jq '.[0].number // empty')
|
||||
|
||||
if [[ -n "$existing_pr" ]]; then
|
||||
echo "PR #$existing_pr already exists, updating..."
|
||||
else
|
||||
gh pr create \
|
||||
--title "[Automated] Update GitHub versions" \
|
||||
--body "This PR updates version information from GitHub releases.
|
||||
|
||||
## How it works
|
||||
1. Scans all JSON files in \`frontend/public/json/\` for slugs
|
||||
2. Finds corresponding \`install/{slug}-install.sh\` scripts
|
||||
3. Extracts \`fetch_and_deploy_gh_release\` calls
|
||||
4. Fetches latest (or pinned) version from GitHub
|
||||
|
||||
## Stats
|
||||
- Total versions: $(jq '.versions | length' "$VERSIONS_FILE")
|
||||
- Pinned versions: $(jq '[.versions[] | select(.pinned == true)] | length' "$VERSIONS_FILE")
|
||||
- Latest versions: $(jq '[.versions[] | select(.pinned == false)] | length' "$VERSIONS_FILE")
|
||||
|
||||
---
|
||||
*Automatically generated from install scripts*" \
|
||||
--base main \
|
||||
--head "$BRANCH_NAME" \
|
||||
--label "automated pr"
|
||||
fi
|
||||
175
.github/workflows/update-timestamp-on-db.yml
generated
vendored
175
.github/workflows/update-timestamp-on-db.yml
generated
vendored
@@ -1,175 +0,0 @@
|
||||
name: Update script timestamp on .sh changes
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "ct/**/*.sh"
|
||||
- "install/**/*.sh"
|
||||
- "tools/**/*.sh"
|
||||
- "turnkey/**/*.sh"
|
||||
- "vm/**/*.sh"
|
||||
|
||||
jobs:
|
||||
update-script-timestamp:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get changed .sh files and derive slugs
|
||||
id: slugs
|
||||
run: |
|
||||
changed=$(git diff --name-only "${{ github.event.before }}" "${{ github.event.after }}" -- ct/ install/ tools/ turnkey/ vm/ | grep '\.sh$' || true)
|
||||
if [[ -z "$changed" ]]; then
|
||||
echo "No .sh files changed in ct/, install/, tools/, turnkey/, or vm/."
|
||||
echo "count=0" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
declare -A seen
|
||||
slugs=""
|
||||
for f in $changed; do
|
||||
[[ -f "$f" ]] || continue
|
||||
base="${f##*/}"
|
||||
base="${base%.sh}"
|
||||
if [[ "$f" == install/* && "$base" == *-install ]]; then
|
||||
slug="${base%-install}"
|
||||
else
|
||||
slug="$base"
|
||||
fi
|
||||
if [[ -z "${seen[$slug]:-}" ]]; then
|
||||
seen[$slug]=1
|
||||
slugs="$slugs $slug"
|
||||
fi
|
||||
done
|
||||
slugs=$(echo $slugs | xargs -n1 | sort -u)
|
||||
if [[ -z "$slugs" ]]; then
|
||||
echo "No slugs to update."
|
||||
echo "count=0" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "$slugs" > changed_slugs.txt
|
||||
echo "count=$(echo "$slugs" | wc -w)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Parse PR number from merge commit
|
||||
id: pr
|
||||
run: |
|
||||
re='#([0-9]+)'
|
||||
if [[ "$COMMIT_MSG" =~ $re ]]; then
|
||||
echo "number=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "number=" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
env:
|
||||
COMMIT_MSG: ${{ github.event.head_commit.message }}
|
||||
|
||||
- name: Update script timestamps in PocketBase
|
||||
if: steps.slugs.outputs.count != '0'
|
||||
env:
|
||||
POCKETBASE_URL: ${{ secrets.POCKETBASE_URL }}
|
||||
POCKETBASE_COLLECTION: ${{ secrets.POCKETBASE_COLLECTION }}
|
||||
POCKETBASE_ADMIN_EMAIL: ${{ secrets.POCKETBASE_ADMIN_EMAIL }}
|
||||
POCKETBASE_ADMIN_PASSWORD: ${{ secrets.POCKETBASE_ADMIN_PASSWORD }}
|
||||
COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}
|
||||
PR_URL: ${{ steps.pr.outputs.number != '' && format('{0}/{1}/pull/{2}', github.server_url, github.repository, steps.pr.outputs.number) || '' }}
|
||||
run: |
|
||||
node << 'ENDSCRIPT'
|
||||
(async function() {
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
const url = require('url');
|
||||
|
||||
function request(fullUrl, opts, redirectCount) {
|
||||
redirectCount = redirectCount || 0;
|
||||
return new Promise(function(resolve, reject) {
|
||||
const u = url.parse(fullUrl);
|
||||
const isHttps = u.protocol === 'https:';
|
||||
const body = opts.body;
|
||||
const options = {
|
||||
hostname: u.hostname,
|
||||
port: u.port || (isHttps ? 443 : 80),
|
||||
path: u.path,
|
||||
method: opts.method || 'GET',
|
||||
headers: opts.headers || {}
|
||||
};
|
||||
if (body) options.headers['Content-Length'] = Buffer.byteLength(body);
|
||||
const lib = isHttps ? https : http;
|
||||
const req = lib.request(options, function(res) {
|
||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
if (redirectCount >= 5) return reject(new Error('Too many redirects from ' + fullUrl));
|
||||
const redirectUrl = url.resolve(fullUrl, res.headers.location);
|
||||
res.resume();
|
||||
resolve(request(redirectUrl, opts, redirectCount + 1));
|
||||
return;
|
||||
}
|
||||
let data = '';
|
||||
res.on('data', function(chunk) { data += chunk; });
|
||||
res.on('end', function() {
|
||||
resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode, body: data });
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (body) req.write(body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
const raw = process.env.POCKETBASE_URL.replace(/\/$/, '');
|
||||
const apiBase = /\/api$/i.test(raw) ? raw : raw + '/api';
|
||||
const coll = process.env.POCKETBASE_COLLECTION;
|
||||
const slugsText = fs.readFileSync('changed_slugs.txt', 'utf8').trim();
|
||||
const slugs = slugsText ? slugsText.split(/\s+/).filter(Boolean) : [];
|
||||
if (slugs.length === 0) {
|
||||
console.log('No slugs to update.');
|
||||
return;
|
||||
}
|
||||
|
||||
const authUrl = apiBase + '/collections/users/auth-with-password';
|
||||
const authBody = JSON.stringify({
|
||||
identity: process.env.POCKETBASE_ADMIN_EMAIL,
|
||||
password: process.env.POCKETBASE_ADMIN_PASSWORD
|
||||
});
|
||||
const authRes = await request(authUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: authBody
|
||||
});
|
||||
if (!authRes.ok) {
|
||||
throw new Error('Auth failed: ' + authRes.body);
|
||||
}
|
||||
const token = JSON.parse(authRes.body).token;
|
||||
const recordsUrl = apiBase + '/collections/' + encodeURIComponent(coll) + '/records';
|
||||
|
||||
for (const slug of slugs) {
|
||||
const filter = "(slug='" + slug.replace(/'/g, "''") + "')";
|
||||
const listRes = await request(recordsUrl + '?filter=' + encodeURIComponent(filter) + '&perPage=1', {
|
||||
headers: { 'Authorization': token }
|
||||
});
|
||||
const list = JSON.parse(listRes.body);
|
||||
const record = list.items && list.items[0];
|
||||
if (!record) {
|
||||
console.log('Slug not in DB, skipping: ' + slug);
|
||||
continue;
|
||||
}
|
||||
const patchRes = await request(recordsUrl + '/' + record.id, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Authorization': token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: record.name || record.slug,
|
||||
last_update_commit: process.env.PR_URL || process.env.COMMIT_URL || ''
|
||||
})
|
||||
});
|
||||
if (!patchRes.ok) {
|
||||
console.warn('PATCH failed for slug ' + slug + ': ' + patchRes.body);
|
||||
continue;
|
||||
}
|
||||
console.log('Updated timestamp for slug: ' + slug);
|
||||
}
|
||||
console.log('Done.');
|
||||
})().catch(e => { console.error(e); process.exit(1); });
|
||||
ENDSCRIPT
|
||||
shell: bash
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,3 +13,4 @@ vm/debian-13-vm.sh
|
||||
vm/vm-manager.sh
|
||||
vm/vm-manager.sh
|
||||
vm/debian-13-vm.sh
|
||||
.DS_Store
|
||||
|
||||
50
ct/alpine-ntfy.sh
Normal file
50
ct/alpine-ntfy.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/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: cobalt (cobaltgit)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://ntfy.sh/
|
||||
|
||||
APP="Alpine-ntfy"
|
||||
var_tags="${var_tags:-notification}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-256}"
|
||||
var_disk="${var_disk:-2}"
|
||||
var_os="${var_os:-alpine}"
|
||||
var_version="${var_version:-3.22}"
|
||||
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 /etc/ntfy ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
msg_info "Updating ntfy LXC"
|
||||
$STD apk -U upgrade
|
||||
setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy
|
||||
msg_ok "Updated ntfy LXC"
|
||||
|
||||
msg_info "Restarting ntfy"
|
||||
rc-service ntfy restart
|
||||
msg_ok "Restarted ntfy"
|
||||
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}http://${IP}${CL}"
|
||||
67
ct/anytype-server.sh
Normal file
67
ct/anytype-server.sh
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/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: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://anytype.io
|
||||
|
||||
APP="Anytype-Server"
|
||||
var_tags="${var_tags:-notes;productivity;sync}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-4096}"
|
||||
var_disk="${var_disk:-16}"
|
||||
var_os="${var_os:-ubuntu}"
|
||||
var_version="${var_version:-24.04}"
|
||||
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 /opt/anytype/any-sync-bundle ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "anytype" "grishy/any-sync-bundle"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop anytype
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Backing up Data"
|
||||
cp -r /opt/anytype/data /opt/anytype_data_backup
|
||||
msg_ok "Backed up Data"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "anytype" "grishy/any-sync-bundle" "prebuild" "latest" "/opt/anytype" "any-sync-bundle_*_linux_amd64.tar.gz"
|
||||
chmod +x /opt/anytype/any-sync-bundle
|
||||
|
||||
msg_info "Restoring Data"
|
||||
cp -r /opt/anytype_data_backup/. /opt/anytype/data
|
||||
rm -rf /opt/anytype_data_backup
|
||||
msg_ok "Restored Data"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start anytype
|
||||
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}:33010${CL}"
|
||||
echo -e "${INFO}${YW} Client config file:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}/opt/anytype/data/client-config.yml${CL}"
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# Source: https://adguard.com/
|
||||
|
||||
APP="Adguard"
|
||||
var_tags="${var_tags:-adblock}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-512}"
|
||||
var_disk="${var_disk:-2}"
|
||||
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/AdGuardHome ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
msg_error "Adguard Home can only be updated via the user interface."
|
||||
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}:3000${CL}"
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# Source: https://www.bazarr.media/
|
||||
|
||||
APP="Bazarr"
|
||||
var_tags="${var_tags:-arr}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
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 /var/lib/bazarr/ ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
if check_for_gh_release "bazarr" "morpheus65535/bazarr"; then
|
||||
apt-get install -y libicu76 &>/dev/null
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop bazarr
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
PYTHON_VERSION="3.12" setup_uv
|
||||
fetch_and_deploy_gh_release "bazarr" "morpheus65535/bazarr" "prebuild" "latest" "/opt/bazarr" "bazarr.zip"
|
||||
|
||||
msg_info "Setup Bazarr"
|
||||
mkdir -p /var/lib/bazarr/
|
||||
chmod 775 /opt/bazarr /var/lib/bazarr/
|
||||
# Always ensure venv exists
|
||||
if [[ ! -d /opt/bazarr/venv/ ]]; then
|
||||
$STD uv venv --clear /opt/bazarr/venv --python 3.12
|
||||
fi
|
||||
|
||||
# Always check and fix service file if needed
|
||||
if [[ -f /etc/systemd/system/bazarr.service ]] && grep -q "ExecStart=/usr/bin/python3" /etc/systemd/system/bazarr.service; then
|
||||
sed -i "s|ExecStart=/usr/bin/python3 /opt/bazarr/bazarr.py|ExecStart=/opt/bazarr/venv/bin/python3 /opt/bazarr/bazarr.py|g" /etc/systemd/system/bazarr.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
sed -i.bak 's/--only-binary=Pillow//g' /opt/bazarr/requirements.txt
|
||||
$STD uv pip install -r /opt/bazarr/requirements.txt --python /opt/bazarr/venv/bin/python3
|
||||
msg_ok "Setup Bazarr"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start bazarr
|
||||
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}:6767${CL}"
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: vhsdream
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# Source: https://github.com/alam00000/bentopdf
|
||||
|
||||
APP="BentoPDF"
|
||||
var_tags="${var_tags:-pdf-editor}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-4096}"
|
||||
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/bentopdf ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
NODE_VERSION="24" setup_nodejs
|
||||
|
||||
if check_for_gh_release "bentopdf" "alam00000/bentopdf"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop bentopdf
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "bentopdf" "alam00000/bentopdf" "tarball" "latest" "/opt/bentopdf"
|
||||
|
||||
msg_info "Updating BentoPDF"
|
||||
cd /opt/bentopdf
|
||||
$STD npm ci --no-audit --no-fund
|
||||
export SIMPLE_MODE=true
|
||||
$STD npm run build -- --mode production
|
||||
msg_ok "Updated BentoPDF"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start bentopdf
|
||||
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}:8080${CL}"
|
||||
@@ -1,109 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# Source: https://www.home-assistant.io/
|
||||
|
||||
APP="Home Assistant"
|
||||
var_tags="${var_tags:-automation;smarthome}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-16}"
|
||||
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 /var/lib/docker/volumes/hass_config/_data ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
UPD=$(msg_menu "Home Assistant Update Options" \
|
||||
"1" "Update ALL Containers" \
|
||||
"2" "Remove ALL Unused Images" \
|
||||
"3" "Install HACS" \
|
||||
"4" "Install FileBrowser")
|
||||
|
||||
if [ "$UPD" == "1" ]; then
|
||||
msg_info "Updating All Containers"
|
||||
CONTAINER_LIST="${1:-$(docker ps -q)}"
|
||||
for container in ${CONTAINER_LIST}; do
|
||||
CONTAINER_IMAGE="$(docker inspect --format "{{.Config.Image}}" --type container "${container}")"
|
||||
RUNNING_IMAGE="$(docker inspect --format "{{.Image}}" --type container "${container}")"
|
||||
docker pull "${CONTAINER_IMAGE}"
|
||||
LATEST_IMAGE="$(docker inspect --format "{{.Id}}" --type image "${CONTAINER_IMAGE}")"
|
||||
if [[ "${RUNNING_IMAGE}" != "${LATEST_IMAGE}" ]]; then
|
||||
pip install -U runlike
|
||||
echo "Updating ${container} image ${CONTAINER_IMAGE}"
|
||||
DOCKER_COMMAND="$(runlike --use-volume-id "${container}")"
|
||||
docker rm --force "${container}"
|
||||
eval "${DOCKER_COMMAND}"
|
||||
fi
|
||||
done
|
||||
msg_ok "Updated All Containers"
|
||||
exit
|
||||
fi
|
||||
if [ "$UPD" == "2" ]; then
|
||||
msg_info "Removing ALL Unused Images"
|
||||
docker image prune -af
|
||||
msg_ok "Removed ALL Unused Images"
|
||||
exit
|
||||
fi
|
||||
if [ "$UPD" == "3" ]; then
|
||||
msg_info "Installing Home Assistant Community Store (HACS)"
|
||||
$STD apt update
|
||||
cd /var/lib/docker/volumes/hass_config/_data
|
||||
$STD bash <(curl -fsSL https://get.hacs.xyz)
|
||||
msg_ok "Installed Home Assistant Community Store (HACS)"
|
||||
echo -e "\n Reboot Home Assistant and clear browser cache then Add HACS integration.\n"
|
||||
exit
|
||||
fi
|
||||
if [ "$UPD" == "4" ]; then
|
||||
msg_info "Installing FileBrowser"
|
||||
RELEASE=$(curl -fsSL https://api.github.com/repos/filebrowser/filebrowser/releases/latest | grep -o '"tag_name": ".*"' | sed 's/"//g' | sed 's/tag_name: //g')
|
||||
$STD curl -fsSL https://github.com/filebrowser/filebrowser/releases/download/v2.23.0/linux-arm64-filebrowser.tar.gz | tar -xzv -C /usr/local/bin
|
||||
$STD filebrowser config init -a '0.0.0.0'
|
||||
$STD filebrowser config set -a '0.0.0.0'
|
||||
$STD filebrowser users add admin helper-scripts.com --perm.admin
|
||||
msg_ok "Installed FileBrowser"
|
||||
|
||||
msg_info "Creating Service"
|
||||
service_path="/etc/systemd/system/filebrowser.service"
|
||||
echo "[Unit]
|
||||
Description=Filebrowser
|
||||
After=network-online.target
|
||||
[Service]
|
||||
User=root
|
||||
WorkingDirectory=/root/
|
||||
ExecStart=/usr/local/bin/filebrowser -r /
|
||||
[Install]
|
||||
WantedBy=default.target" >$service_path
|
||||
|
||||
$STD systemctl enable --now filebrowser
|
||||
msg_ok "Created Service"
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "FileBrowser should be reachable by going to the following URL.
|
||||
${BL}http://$LOCAL_IP:8080${CL} admin|helper-scripts.com\n"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
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}HA: http://${IP}:8123${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}Portainer: https://${IP}:9443${CL}"
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# Source: https://jellyfin.org/
|
||||
|
||||
APP="Jellyfin"
|
||||
var_tags="${var_tags:-media}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-16}"
|
||||
var_os="${var_os:-ubuntu}"
|
||||
var_version="${var_version:-24.04}"
|
||||
var_unprivileged="${var_unprivileged:-0}"
|
||||
var_gpu="${var_gpu:-yes}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
if [[ ! -d /usr/lib/jellyfin ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
msg_info "Updating Jellyfin"
|
||||
ensure_dependencies libjemalloc2
|
||||
if [[ ! -f /usr/lib/libjemalloc.so ]]; then
|
||||
ln -sf /usr/lib/aarch64-linux-gnu/libjemalloc.so.2 /usr/lib/libjemalloc.so
|
||||
fi
|
||||
$STD apt update
|
||||
$STD apt -y upgrade
|
||||
$STD apt -y --with-new-pkgs upgrade jellyfin jellyfin-server
|
||||
msg_ok "Updated Jellyfin"
|
||||
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}http://${IP}:8096${CL}"
|
||||
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# Source: https://github.com/Chevron7Locked/kima-hub
|
||||
|
||||
APP="Kima-Hub"
|
||||
var_tags="${var_tags:-music;streaming;media}"
|
||||
var_cpu="${var_cpu:-4}"
|
||||
var_ram="${var_ram:-8192}"
|
||||
var_disk="${var_disk:-20}"
|
||||
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/kima-hub ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "kima-hub" "Chevron7Locked/kima-hub"; then
|
||||
msg_info "Stopping Services"
|
||||
systemctl stop kima-frontend kima-backend kima-analyzer kima-analyzer-clap
|
||||
msg_ok "Stopped Services"
|
||||
|
||||
msg_info "Backing up Data"
|
||||
cp /opt/kima-hub/backend/.env /opt/kima-hub-backend-env.bak
|
||||
cp /opt/kima-hub/frontend/.env /opt/kima-hub-frontend-env.bak
|
||||
msg_ok "Backed up Data"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kima-hub" "Chevron7Locked/kima-hub" "tarball"
|
||||
|
||||
msg_info "Restoring Data"
|
||||
cp /opt/kima-hub-backend-env.bak /opt/kima-hub/backend/.env
|
||||
cp /opt/kima-hub-frontend-env.bak /opt/kima-hub/frontend/.env
|
||||
rm -f /opt/kima-hub-backend-env.bak /opt/kima-hub-frontend-env.bak
|
||||
msg_ok "Restored Data"
|
||||
|
||||
msg_info "Rebuilding Backend"
|
||||
cd /opt/kima-hub/backend
|
||||
$STD npm install
|
||||
$STD npm run build
|
||||
$STD npx prisma generate
|
||||
$STD npx prisma migrate deploy
|
||||
msg_ok "Rebuilt Backend"
|
||||
|
||||
msg_info "Rebuilding Frontend"
|
||||
cd /opt/kima-hub/frontend
|
||||
$STD npm install
|
||||
$STD npm run build
|
||||
msg_ok "Rebuilt Frontend"
|
||||
|
||||
msg_info "Starting Services"
|
||||
systemctl start kima-backend kima-frontend kima-analyzer kima-analyzer-clap
|
||||
msg_ok "Started Services"
|
||||
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}:3030${CL}"
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: kristocopani
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# Source: https://lubelogger.com/
|
||||
|
||||
APP="LubeLogger"
|
||||
var_tags="${var_tags:-vehicle;car}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-512}"
|
||||
var_disk="${var_disk:-2}"
|
||||
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/systemd/system/lubelogger.service ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
if check_for_gh_release "lubelogger" "hargata/lubelog"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop lubelogger
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Backing up data"
|
||||
mkdir -p /tmp/lubeloggerData/data
|
||||
cp /opt/lubelogger/appsettings.json /tmp/lubeloggerData/appsettings.json
|
||||
cp -r /opt/lubelogger/data/ /tmp/lubeloggerData/
|
||||
|
||||
# Lubelogger has moved multiples folders to the 'data' folder, and we need to move them before the update to keep the user data
|
||||
# Github Discussion: https://github.com/hargata/lubelog/discussions/787
|
||||
[[ -e /opt/lubelogger/config ]] && cp -r /opt/lubelogger/config /tmp/lubeloggerData/data/
|
||||
[[ -e /opt/lubelogger/wwwroot/translations ]] && cp -r /opt/lubelogger/wwwroot/translations /tmp/lubeloggerData/data/
|
||||
[[ -e /opt/lubelogger/wwwroot/documents ]] && cp -r /opt/lubelogger/wwwroot/documents /tmp/lubeloggerData/data/
|
||||
[[ -e /opt/lubelogger/wwwroot/images ]] && cp -r /opt/lubelogger/wwwroot/images /tmp/lubeloggerData/data/
|
||||
[[ -e /opt/lubelogger/wwwroot/temp ]] && cp -r /opt/lubelogger/wwwroot/temp /tmp/lubeloggerData/data/
|
||||
[[ -e /opt/lubelogger/log ]] && cp -r /opt/lubelogger/log /tmp/lubeloggerData/
|
||||
rm -rf /opt/lubelogger
|
||||
msg_ok "Backed up data"
|
||||
|
||||
fetch_and_deploy_gh_release "lubelogger" "hargata/lubelog" "prebuild" "latest" "/opt/lubelogger" "LubeLogger*linux_x64.zip"
|
||||
|
||||
msg_info "Configuring LubeLogger"
|
||||
chmod 700 /opt/lubelogger/CarCareTracker
|
||||
cp -rf /tmp/lubeloggerData/* /opt/lubelogger/
|
||||
rm -rf /tmp/lubeloggerData
|
||||
msg_ok "Configured LubeLogger"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start lubelogger
|
||||
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}:5000${CL}"
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# Source: https://pi-hole.net/
|
||||
|
||||
APP="Pihole"
|
||||
var_tags="${var_tags:-adblock}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-512}"
|
||||
var_disk="${var_disk:-2}"
|
||||
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 /etc/pihole ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
msg_info "Updating PiHole"
|
||||
set +e
|
||||
$STD apt update
|
||||
$STD apt upgrade -y
|
||||
/usr/local/bin/pihole -up
|
||||
msg_ok "Updated PiHole"
|
||||
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}http://${IP}/admin${CL}"
|
||||
@@ -1,119 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# Source: https://github.com/dani-garcia/vaultwarden
|
||||
|
||||
APP="Vaultwarden"
|
||||
var_tags="${var_tags:-password-manager}"
|
||||
var_cpu="${var_cpu:-4}"
|
||||
var_ram="${var_ram:-6144}"
|
||||
var_disk="${var_disk:-20}"
|
||||
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/systemd/system/vaultwarden.service ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
VAULT=$(get_latest_github_release "dani-garcia/vaultwarden")
|
||||
WVRELEASE=$(get_latest_github_release "dani-garcia/bw_web_builds")
|
||||
|
||||
UPD=$(msg_menu "Vaultwarden Update Options" \
|
||||
"1" "Update VaultWarden + Web-Vault" \
|
||||
"2" "Set Admin Token")
|
||||
|
||||
if [ "$UPD" == "1" ]; then
|
||||
if check_for_gh_release "vaultwarden" "dani-garcia/vaultwarden"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop vaultwarden
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
fetch_and_deploy_gh_release "vaultwarden" "dani-garcia/vaultwarden" "tarball" "latest" "/tmp/vaultwarden-src"
|
||||
|
||||
msg_info "Updating VaultWarden to $VAULT (Patience)"
|
||||
cd /tmp/vaultwarden-src
|
||||
VW_VERSION="$VAULT"
|
||||
export VW_VERSION
|
||||
$STD cargo build --features "sqlite,mysql,postgresql" --release
|
||||
if [[ -f /usr/bin/vaultwarden ]]; then
|
||||
cp target/release/vaultwarden /usr/bin/
|
||||
else
|
||||
cp target/release/vaultwarden /opt/vaultwarden/bin/
|
||||
fi
|
||||
cd ~ && rm -rf /tmp/vaultwarden-src
|
||||
msg_ok "Updated VaultWarden to ${VAULT}"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start vaultwarden
|
||||
msg_ok "Started Service"
|
||||
else
|
||||
msg_ok "VaultWarden is already up-to-date"
|
||||
fi
|
||||
|
||||
if check_for_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop vaultwarden
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Updating Web-Vault to $WVRELEASE"
|
||||
rm -rf /opt/vaultwarden/web-vault
|
||||
mkdir -p /opt/vaultwarden/web-vault
|
||||
|
||||
fetch_and_deploy_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds" "prebuild" "latest" "/opt/vaultwarden/web-vault" "bw_web_*.tar.gz"
|
||||
|
||||
chown -R root:root /opt/vaultwarden/web-vault/
|
||||
msg_ok "Updated Web-Vault to ${WVRELEASE}"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start vaultwarden
|
||||
msg_ok "Started Service"
|
||||
else
|
||||
msg_ok "Web-Vault is already up-to-date"
|
||||
fi
|
||||
|
||||
msg_ok "Updated successfully!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "$UPD" == "2" ]; then
|
||||
if [[ "${PHS_SILENT:-0}" == "1" ]]; then
|
||||
msg_warn "Set Admin Token requires interactive mode, skipping."
|
||||
exit
|
||||
fi
|
||||
read -r -s -p "Set the ADMIN_TOKEN: " NEWTOKEN
|
||||
echo ""
|
||||
if [[ -n "$NEWTOKEN" ]]; then
|
||||
ensure_dependencies argon2
|
||||
TOKEN=$(echo -n "${NEWTOKEN}" | argon2 "$(openssl rand -base64 32)" -t 2 -m 16 -p 4 -l 64 -e)
|
||||
sed -i "s|ADMIN_TOKEN=.*|ADMIN_TOKEN='${TOKEN}'|" /opt/vaultwarden/.env
|
||||
if [[ -f /opt/vaultwarden/data/config.json ]]; then
|
||||
sed -i "s|\"admin_token\":.*|\"admin_token\": \"${TOKEN}\"|" /opt/vaultwarden/data/config.json
|
||||
fi
|
||||
systemctl restart vaultwarden
|
||||
msg_ok "Admin token updated"
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
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}:8000${CL}"
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster) | Co-Author: MickLesk (Canbiz) | Co-Author: CrazyWolf13
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://homarr.dev/
|
||||
|
||||
APP="alpine-homarr"
|
||||
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (Canbiz)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/community-scripts/ProxmoxVE
|
||||
|
||||
APP="Docspell"
|
||||
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Nícolas Pastorello (opastorello)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/jumpserver/jumpserver
|
||||
|
||||
APP="JumpServer"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://petio.tv/
|
||||
|
||||
APP="Petio"
|
||||
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://nginxproxymanager.com/
|
||||
|
||||
APP="Nginx Proxy Manager"
|
||||
|
||||
4
ct/deferred/opencloud.json
generated
4
ct/deferred/opencloud.json
generated
@@ -53,7 +53,7 @@
|
||||
"type": "info"
|
||||
},
|
||||
{
|
||||
"text": "**Optional Full-text Search with Apache Tika**: requires your own Tika LXC. See `https://community-scripts.github.io/ProxmoxVED/scripts?id=apache-tika`",
|
||||
"text": "**Optional Full-text Search with Apache Tika**: requires your own Tika LXC. See `https://community-scripts.github.io/ProxmoxVE/scripts?id=apache-tika`",
|
||||
"type": "info"
|
||||
},
|
||||
{
|
||||
@@ -61,4 +61,4 @@
|
||||
"type": "info"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source:
|
||||
|
||||
APP="Roundcubemail"
|
||||
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (Canbiz)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source:
|
||||
|
||||
APP="Squirrel Servers Manager"
|
||||
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: SunFlowerOwl
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/haugene/docker-transmission-openvpn
|
||||
|
||||
APP="transmission-openvpn"
|
||||
|
||||
73
ct/degoog.sh
73
ct/degoog.sh
@@ -1,73 +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: 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}"
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Simon Friedrich
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://forgejo.org/
|
||||
|
||||
APP="Forgejo-Runner"
|
||||
|
||||
75
ct/immichframe.sh
Normal file
75
ct/immichframe.sh
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main}"
|
||||
source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/build.func")
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: Thiago Canozzo Lahr (tclahr)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/immichFrame/ImmichFrame
|
||||
|
||||
APP="ImmichFrame"
|
||||
var_tags="${var_tags:-photos;slideshow}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-8}"
|
||||
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/immichframe ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "immichframe" "immichFrame/ImmichFrame"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop immichframe
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "immichframe" "immichFrame/ImmichFrame" "tarball" "latest" "/tmp/immichframe"
|
||||
|
||||
msg_info "Building Application"
|
||||
cd /tmp/immichframe
|
||||
$STD dotnet publish ImmichFrame.WebApi/ImmichFrame.WebApi.csproj \
|
||||
--configuration Release \
|
||||
--runtime linux-x64 \
|
||||
--self-contained false \
|
||||
--output /opt/immichframe
|
||||
|
||||
cd /tmp/immichframe/immichFrame.Web
|
||||
$STD npm ci --silent
|
||||
$STD npm run build
|
||||
rm -rf /opt/immichframe/wwwroot/*
|
||||
cp -r build/* /opt/immichframe/wwwroot
|
||||
rm -rf /tmp/immichframe
|
||||
chown -R immichframe:immichframe /opt/immichframe
|
||||
msg_ok "Application Built"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start immichframe
|
||||
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}:8080${CL}"
|
||||
echo -e "${INFO}${YW} Configuration file location:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}/opt/immichframe/Config/Settings.yml${CL}"
|
||||
echo -e "${INFO}${YW} Edit the config file and set ImmichServerUrl and ApiKey before use!${CL}"
|
||||
@@ -1,71 +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: vhsdream
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/iv-org/invidious
|
||||
|
||||
APP="Invidious"
|
||||
var_tags="${var_tags:-streaming}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
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/invidious ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "Invidious" "iv-org/invidious"; then
|
||||
msg_info "Stopping services"
|
||||
$STD systemctl stop invidious-companion invidious
|
||||
msg_ok "Stopped services"
|
||||
|
||||
msg_info "Backing up config"
|
||||
cp /opt/invidious/config/config.yml /opt/invidious-config.yml
|
||||
msg_ok "Backed up config"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Invidious" "iv-org/invidious" "tarball" "latest" "/opt/invidious"
|
||||
if check_for_gh_release "Invidious-Companion" "iv-org/invidious-companion"; then
|
||||
CLEAN_INSTALL fetch_and_deploy_gh_release "Invidious-Companion" "iv-org/invidious-companion" "prebuild" "latest" "/opt/invidious-companion" "invidious_companion-x86_64-unknown-linux-gnu.tar.gz"
|
||||
fi
|
||||
|
||||
msg_info "Updating Invidious"
|
||||
PG_DB_PASS="$(sed -n '/Password:/s/[^:]*:[[:space:]]//p' ~/oxicloud.creds)"
|
||||
cd /opt/oxicloud
|
||||
export DATABASE_URL="postgres://oxicloud:${PG_DB_PASS}@localhost/oxicloud"
|
||||
export RUSTFLAGS="-C target-cpu=native"
|
||||
$STD cargo build --release
|
||||
mv target/release/oxicloud /usr/bin/oxicloud && chmod +x /usr/bin/oxicloud
|
||||
msg_ok "Updated Invidious"
|
||||
|
||||
msg_info "Starting Invidious"
|
||||
$STD systemctl start oxicloud
|
||||
msg_ok "Started Invidious"
|
||||
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}:8086${CL}"
|
||||
75
ct/isponsorblocktv.sh
Normal file
75
ct/isponsorblocktv.sh
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/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)
|
||||
# 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"
|
||||
|
||||
if [[ -d /var/lib/isponsorblocktv ]]; then
|
||||
msg_info "Backing up Data"
|
||||
cp -r /var/lib/isponsorblocktv /var/lib/isponsorblocktv_data_backup
|
||||
msg_ok "Backed up Data"
|
||||
fi
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "isponsorblocktv" "dmunozv04/iSponsorBlockTV"
|
||||
|
||||
msg_info "Setting up iSponsorBlockTV"
|
||||
$STD python3 -m venv /opt/isponsorblocktv/venv
|
||||
$STD /opt/isponsorblocktv/venv/bin/pip install --upgrade pip
|
||||
$STD /opt/isponsorblocktv/venv/bin/pip install /opt/isponsorblocktv
|
||||
msg_ok "Set up iSponsorBlockTV"
|
||||
|
||||
if [[ -d /var/lib/isponsorblocktv_data_backup ]]; then
|
||||
msg_info "Restoring Data"
|
||||
rm -rf /var/lib/isponsorblocktv
|
||||
cp -r /var/lib/isponsorblocktv_data_backup /var/lib/isponsorblocktv
|
||||
rm -rf /var/lib/isponsorblocktv_data_backup
|
||||
msg_ok "Restored Data"
|
||||
fi
|
||||
|
||||
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}"
|
||||
101
ct/librechat.sh
101
ct/librechat.sh
@@ -1,101 +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: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/danny-avila/LibreChat
|
||||
|
||||
APP="LibreChat"
|
||||
var_tags="${var_tags:-ai;chat}"
|
||||
var_cpu="${var_cpu:-4}"
|
||||
var_ram="${var_ram:-6144}"
|
||||
var_disk="${var_disk:-20}"
|
||||
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/librechat ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_tag "librechat" "danny-avila/LibreChat" "v"; then
|
||||
msg_info "Stopping Services"
|
||||
systemctl stop librechat rag-api
|
||||
msg_ok "Stopped Services"
|
||||
|
||||
msg_info "Backing up Configuration"
|
||||
cp /opt/librechat/.env /opt/librechat.env.bak
|
||||
msg_ok "Backed up Configuration"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "librechat" "danny-avila/LibreChat"
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
cd /opt/librechat
|
||||
$STD npm ci
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
msg_info "Building Frontend"
|
||||
$STD npm run frontend
|
||||
$STD npm prune --production
|
||||
$STD npm cache clean --force
|
||||
msg_ok "Built Frontend"
|
||||
|
||||
msg_info "Restoring Configuration"
|
||||
cp /opt/librechat.env.bak /opt/librechat/.env
|
||||
rm -f /opt/librechat.env.bak
|
||||
msg_ok "Restored Configuration"
|
||||
|
||||
msg_info "Starting Services"
|
||||
systemctl start rag-api librechat
|
||||
msg_ok "Started Services"
|
||||
msg_ok "Updated LibreChat Successfully!"
|
||||
fi
|
||||
|
||||
if check_for_gh_release "rag-api" "danny-avila/rag_api"; then
|
||||
msg_info "Stopping RAG API"
|
||||
systemctl stop rag-api
|
||||
msg_ok "Stopped RAG API"
|
||||
|
||||
msg_info "Backing up RAG API Configuration"
|
||||
cp /opt/rag-api/.env /opt/rag-api.env.bak
|
||||
msg_ok "Backed up RAG API Configuration"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "rag-api" "danny-avila/rag_api" "tarball"
|
||||
|
||||
msg_info "Updating RAG API Dependencies"
|
||||
cd /opt/rag-api
|
||||
$STD .venv/bin/pip install -r requirements.lite.txt
|
||||
msg_ok "Updated RAG API Dependencies"
|
||||
|
||||
msg_info "Restoring RAG API Configuration"
|
||||
cp /opt/rag-api.env.bak /opt/rag-api/.env
|
||||
rm -f /opt/rag-api.env.bak
|
||||
msg_ok "Restored RAG API Configuration"
|
||||
|
||||
msg_info "Starting RAG API"
|
||||
systemctl start rag-api
|
||||
msg_ok "Started RAG API"
|
||||
msg_ok "Updated RAG API 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}:3080${CL}"
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2025 minthcm
|
||||
# Author: MintHCM
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/minthcm/minthcm
|
||||
|
||||
APP="MintHCM"
|
||||
|
||||
53
ct/navidrome.sh
Normal file
53
ct/navidrome.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/arm64-dev-build/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/navidrome/navidrome
|
||||
|
||||
APP="Navidrome"
|
||||
var_tags="${var_tags:-music}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
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 /var/lib/navidrome ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "navidrome" "navidrome/navidrome"; then
|
||||
msg_info "Stopping Services"
|
||||
systemctl stop navidrome
|
||||
msg_ok "Services Stopped"
|
||||
|
||||
fetch_and_deploy_gh_release "navidrome" "navidrome/navidrome" "binary"
|
||||
|
||||
msg_info "Starting Services"
|
||||
systemctl start navidrome
|
||||
msg_ok "Started Services"
|
||||
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}:4533${CL}"
|
||||
@@ -40,10 +40,8 @@ function update_script() {
|
||||
RUST_TOOLCHAIN=$TOOLCHAIN setup_rust
|
||||
|
||||
msg_info "Updating OxiCloud"
|
||||
PG_DB_PASS="$(sed -n '/Password:/s/[^:]*:[[:space:]]//p' ~/oxicloud.creds)"
|
||||
cd /opt/oxicloud
|
||||
export DATABASE_URL="postgres://oxicloud:${PG_DB_PASS}@localhost/oxicloud"
|
||||
export RUSTFLAGS="-C target-cpu=native"
|
||||
export DATABASE_URL="postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost/${PG_DB_NAME}"
|
||||
$STD cargo build --release
|
||||
mv target/release/oxicloud /usr/bin/oxicloud && chmod +x /usr/bin/oxicloud
|
||||
msg_ok "Updated OxiCloud"
|
||||
|
||||
@@ -1,79 +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: Stephen Chin (steveonjava)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/ProtonMail/proton-bridge
|
||||
|
||||
APP="ProtonMail-Bridge"
|
||||
var_tags="${var_tags:-mail;proton}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-8}"
|
||||
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 [[ ! -x /usr/bin/protonmail-bridge ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if check_for_gh_release "protonmail-bridge" "ProtonMail/proton-bridge"; then
|
||||
local -a bridge_units=(
|
||||
protonmail-bridge
|
||||
protonmail-bridge-imap.socket
|
||||
protonmail-bridge-smtp.socket
|
||||
protonmail-bridge-imap-proxy
|
||||
protonmail-bridge-smtp-proxy
|
||||
)
|
||||
local unit
|
||||
declare -A was_active
|
||||
for unit in "${bridge_units[@]}"; do
|
||||
if systemctl is-active --quiet "$unit" 2>/dev/null; then
|
||||
was_active["$unit"]=1
|
||||
else
|
||||
was_active["$unit"]=0
|
||||
fi
|
||||
done
|
||||
|
||||
msg_info "Stopping Services"
|
||||
systemctl stop protonmail-bridge-imap.socket protonmail-bridge-smtp.socket protonmail-bridge-imap-proxy protonmail-bridge-smtp-proxy protonmail-bridge
|
||||
msg_ok "Stopped Services"
|
||||
|
||||
fetch_and_deploy_gh_release "protonmail-bridge" "ProtonMail/proton-bridge" "binary"
|
||||
|
||||
if [[ -f /home/protonbridge/.protonmailbridge-initialized ]]; then
|
||||
msg_info "Starting Services"
|
||||
for unit in "${bridge_units[@]}"; do
|
||||
if [[ "${was_active[$unit]:-0}" == "1" ]]; then
|
||||
systemctl start "$unit"
|
||||
fi
|
||||
done
|
||||
msg_ok "Started Services"
|
||||
else
|
||||
msg_ok "Initialization not completed. Services remain disabled."
|
||||
fi
|
||||
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}One-time configuration is required before Bridge services are enabled.${CL}"
|
||||
echo -e "${INFO}${YW}Run this command in the container: protonmailbridge-configure${CL}"
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/asylumexp/Proxmox/main/misc/build.func)
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/arm64-dev-build/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/asylumexp/Proxmox/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/rogerfar/rdt-client
|
||||
|
||||
APP="RDTClient"
|
||||
@@ -39,9 +39,9 @@ function update_script() {
|
||||
|
||||
fetch_and_deploy_gh_release "rdt-client" "rogerfar/rdt-client" "prebuild" "latest" "/opt/rdtc" "RealDebridClient.zip"
|
||||
cp -R /opt/rdtc-backup/appsettings.json /opt/rdtc/
|
||||
if dpkg-query -W dotnet-sdk-8.0 >/dev/null 2>&1; then
|
||||
$STD apt remove --purge -y dotnet-sdk-8.0
|
||||
ensure_dependencies aspnetcore-runtime-9.0
|
||||
if dpkg-query -W aspnetcore-runtime-9.0 >/dev/null 2>&1; then
|
||||
$STD apt remove --purge -y aspnetcore-runtime-9.0
|
||||
ensure_dependencies aspnetcore-runtime-10.0
|
||||
fi
|
||||
rm -rf /opt/rdtc-backup
|
||||
|
||||
@@ -53,6 +53,30 @@ function update_script() {
|
||||
exit
|
||||
}
|
||||
|
||||
function update_script_arm64() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
if [[ ! -d /opt/rdtc/ ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
if check_for_gh_release "rdt-client" "rogerfar/rdt-client"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop rdtc
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Updating .NET Runtime"
|
||||
rm -rf /usr/share/dotnet /usr/bin/dotnet
|
||||
$STD apt-get install -y libc6 libgcc-s1 libgssapi-krb5-2 liblttng-ust1 libssl3 libstdc++6 zlib1g libicu76
|
||||
curl -fsSL -o /tmp/dotnet.tar.gz "https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.103/dotnet-sdk-10.0.103-linux-arm64.tar.gz"
|
||||
tar -zxf /tmp/dotnet.tar.gz -C /usr/share/dotnet
|
||||
ln -sf /usr/share/dotnet/dotnet /usr/bin/dotnet
|
||||
rm -f /tmp/dotnet.tar.gz
|
||||
msg_ok "Updated .NET Runtime"
|
||||
fi
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
67
ct/split-pro.sh
Normal file
67
ct/split-pro.sh
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/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: johanngrobe
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://github.com/oss-apps/split-pro
|
||||
|
||||
APP="Split-Pro"
|
||||
var_tags="${var_tags:-finance;expense-sharing}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-4096}"
|
||||
var_disk="${var_disk:-6}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/split-pro ]]; then
|
||||
msg_error "No Split Pro Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "split-pro" "oss-apps/split-pro"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop split-pro
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Backing up Data"
|
||||
cp /opt/split-pro/.env /opt/split-pro.env
|
||||
msg_ok "Backed up Data"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "split-pro" "oss-apps/split-pro" "tarball" "latest" "/opt/split-pro"
|
||||
|
||||
msg_info "Building Application"
|
||||
cd /opt/split-pro
|
||||
$STD pnpm install --frozen-lockfile
|
||||
$STD pnpm build
|
||||
cp /opt/split-pro.env /opt/split-pro/.env
|
||||
rm -f /opt/split-pro.env
|
||||
ln -sf /opt/split-pro_data/uploads /opt/split-pro/uploads
|
||||
$STD pnpm exec prisma migrate deploy
|
||||
msg_ok "Built Application"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start split-pro
|
||||
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}:3000${CL}"
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2025 community-scripts ORG
|
||||
# Author: KernelSailor
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://snowflake.torproject.org/
|
||||
|
||||
APP="tor-snowflake"
|
||||
|
||||
@@ -1,54 +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: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/versity/versitygw
|
||||
|
||||
APP="VersityGW"
|
||||
var_tags="${var_tags:-s3;storage;gateway}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-8}"
|
||||
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 /usr/bin/versitygw ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "versitygw" "versity/versitygw"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop versitygw@gateway
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
fetch_and_deploy_gh_release "versitygw" "versity/versitygw" "binary"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start versitygw@gateway
|
||||
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}:7070${CL}"
|
||||
83
ct/yamtrack.sh
Normal file
83
ct/yamtrack.sh
Normal file
@@ -0,0 +1,83 @@
|
||||
#!/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: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/FuzzyGrim/Yamtrack
|
||||
|
||||
APP="Yamtrack"
|
||||
var_tags="${var_tags:-media;tracker;movies;anime}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-8}"
|
||||
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/yamtrack ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "yamtrack" "FuzzyGrim/Yamtrack"; then
|
||||
msg_info "Stopping Services"
|
||||
systemctl stop yamtrack yamtrack-celery
|
||||
msg_ok "Stopped Services"
|
||||
|
||||
msg_info "Backing up Data"
|
||||
cp /opt/yamtrack/src/.env /opt/yamtrack_env.bak
|
||||
msg_ok "Backed up Data"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "yamtrack" "FuzzyGrim/Yamtrack" "tarball"
|
||||
|
||||
msg_info "Installing Python Dependencies"
|
||||
cd /opt/yamtrack
|
||||
$STD uv venv .venv
|
||||
$STD uv pip install --no-cache-dir -r requirements.txt
|
||||
msg_ok "Installed Python Dependencies"
|
||||
|
||||
msg_info "Restoring Data"
|
||||
cp /opt/yamtrack_env.bak /opt/yamtrack/src/.env
|
||||
rm -f /opt/yamtrack_env.bak
|
||||
msg_ok "Restored Data"
|
||||
|
||||
msg_info "Updating Yamtrack"
|
||||
cd /opt/yamtrack/src
|
||||
$STD /opt/yamtrack/.venv/bin/python manage.py migrate
|
||||
$STD /opt/yamtrack/.venv/bin/python manage.py collectstatic --noinput
|
||||
msg_ok "Updated Yamtrack"
|
||||
|
||||
msg_info "Updating Nginx Configuration"
|
||||
cp /opt/yamtrack/nginx.conf /etc/nginx/nginx.conf
|
||||
sed -i 's|user abc;|user www-data;|' /etc/nginx/nginx.conf
|
||||
sed -i 's|/yamtrack/staticfiles/|/opt/yamtrack/src/staticfiles/|' /etc/nginx/nginx.conf
|
||||
$STD systemctl reload nginx
|
||||
msg_ok "Updated Nginx Configuration"
|
||||
|
||||
msg_info "Starting Services"
|
||||
systemctl start yamtrack yamtrack-celery
|
||||
msg_ok "Started Services"
|
||||
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}:8000${CL}"
|
||||
@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: dave-yap (dave-yap) | Co-author: remz1337
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||
# Source: https://zitadel.com/
|
||||
|
||||
APP="Zitadel"
|
||||
|
||||
@@ -705,7 +705,7 @@ cleanup_lxc
|
||||
- [ ] `motd_ssh`, `customize`, `cleanup_lxc` at the end
|
||||
- [ ] No custom download/version-check logic
|
||||
- [ ] No default `(Patience)` text in msg_info labels
|
||||
- [ ] JSON metadata file created in `json/<appname>.json`
|
||||
- [ ] JSON metadata file created in `frontend/public/json/<appname>.json`
|
||||
|
||||
---
|
||||
|
||||
@@ -727,7 +727,7 @@ cleanup_lxc
|
||||
|
||||
## <20> JSON Metadata Files
|
||||
|
||||
Every application requires a JSON metadata file in `json/<appname>.json`.
|
||||
Every application requires a JSON metadata file in `frontend/public/json/<appname>.json`.
|
||||
|
||||
### JSON Structure
|
||||
|
||||
|
||||
@@ -1,142 +1,106 @@
|
||||
|
||||
# Community Scripts Contribution Guide
|
||||
|
||||
## Welcome to the community-scripts repository
|
||||
## **Welcome to the communty-scripts Repository!**
|
||||
|
||||
These documents outline the coding standards and contribution flow for the ProxmoxVED 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.
|
||||
|
||||
The important reality check is simple:
|
||||
### Why Coding Standards Matter
|
||||
|
||||
- contributors primarily submit shell scripts
|
||||
- website metadata is **not** contributed as repo JSON files
|
||||
- metadata changes belong to the website / maintainer workflow
|
||||
Coding standards are crucial for several reasons:
|
||||
|
||||
## Scope of these documents
|
||||
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.
|
||||
|
||||
This contribution guide covers:
|
||||
### Scope of These Documents
|
||||
|
||||
- `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
|
||||
These documents cover the coding standards for the following types of files in our project:
|
||||
|
||||
## Getting started
|
||||
- **`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.
|
||||
|
||||
Before contributing, set up:
|
||||
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.
|
||||
|
||||
1. Visual Studio Code or another editor with ShellCheck support
|
||||
2. a fork of `community-scripts/ProxmoxVED`
|
||||
3. a local clone of your fork
|
||||
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. 📚🔍
|
||||
|
||||
### Recommended extensions
|
||||
Let's work together to keep our codebase clean, efficient, and maintainable! 💪🚀
|
||||
|
||||
- [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)
|
||||
|
||||
### Templates
|
||||
## Getting Started
|
||||
|
||||
Use these templates as your starting point:
|
||||
Before contributing, please ensure that you have the following setup:
|
||||
|
||||
- [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)
|
||||
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)
|
||||
|
||||
## Script types
|
||||
### 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.
|
||||
|
||||
### Application script: `ct/AppName.sh`
|
||||
---
|
||||
|
||||
Reference guide:
|
||||
# 🚀 The Application Script (ct/AppName.sh)
|
||||
|
||||
- [CT coding guide for `AppName.sh`](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/ct/AppName.md)
|
||||
- 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.
|
||||
|
||||
This script is responsible for:
|
||||
---
|
||||
|
||||
- host-side container orchestration
|
||||
- app variables and defaults
|
||||
- update wiring for the installed app
|
||||
# 🛠 The Installation Script (install/AppName-install.sh)
|
||||
|
||||
### Installation script: `install/AppName-install.sh`
|
||||
- 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.
|
||||
|
||||
Reference guide:
|
||||
---
|
||||
|
||||
- [Install coding guide for `AppName-install.sh`](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.md)
|
||||
## 🚀 Building Your Own Scripts
|
||||
|
||||
This script is responsible for:
|
||||
Start with the [template script](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/install/AppName-install.sh)
|
||||
|
||||
- container-internal installation logic
|
||||
- package/runtime setup
|
||||
- final application configuration
|
||||
---
|
||||
|
||||
## Contribution process
|
||||
## 🤝 Contribution Process
|
||||
|
||||
### 1. Fork the repository
|
||||
Fork to your GitHub account
|
||||
|
||||
Fork `community-scripts/ProxmoxVED` to your GitHub account.
|
||||
|
||||
### 2. Clone your fork
|
||||
|
||||
### 2. Clone your fork on your local environment
|
||||
```bash
|
||||
git clone https://github.com/yourUserName/ForkName
|
||||
```
|
||||
|
||||
### 3. Create a branch
|
||||
|
||||
### 3. Create a new branch
|
||||
```bash
|
||||
git switch -c your-feature-branch
|
||||
```
|
||||
|
||||
### 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. 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. Commit changes (without build.func and install.func!)
|
||||
```bash
|
||||
git commit -m "Your commit message"
|
||||
```
|
||||
|
||||
### 7. Push your branch
|
||||
|
||||
### 5. Push to your fork
|
||||
```bash
|
||||
git push origin your-feature-branch
|
||||
```
|
||||
|
||||
### 8. Open a pull request
|
||||
### 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.
|
||||
|
||||
Open a PR from your branch to `community-scripts/ProxmoxVED/main`.
|
||||
---
|
||||
|
||||
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
|
||||
## 📚 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)
|
||||
- [Fork setup guide](./FORK_SETUP.md)
|
||||
- [Contribution README](./README.md)
|
||||
- [JSON Template: AppName.json](https://github.com/community-scripts/ProxmoxVED/blob/main/.github/CONTRIBUTOR_AND_GUIDES/json/AppName.json)
|
||||
|
||||
|
||||
|
||||
@@ -172,7 +172,6 @@ var_unprivileged="1" # 1=unprivileged (secure), 0=privileged (rarely n
|
||||
```
|
||||
|
||||
**Variable Naming Convention**:
|
||||
|
||||
- Variables exposed to user: `var_*` (e.g., `var_cpu`, `var_hostname`, `var_ssh`)
|
||||
- Internal variables: lowercase (e.g., `container_id`, `app_version`)
|
||||
|
||||
@@ -274,7 +273,6 @@ echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}"
|
||||
**Triggered by**: Called automatically at script start
|
||||
|
||||
**Behavior**:
|
||||
|
||||
1. Parse command-line arguments (if any)
|
||||
2. Generate random UUID for session tracking
|
||||
3. Load container storage from Proxmox
|
||||
@@ -286,7 +284,6 @@ echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}"
|
||||
**Purpose**: Launch the container creation menu with 5 installation modes
|
||||
|
||||
**Menu Options**:
|
||||
|
||||
```
|
||||
1. Default Installation (Quick setup, predefined settings)
|
||||
2. Advanced Installation (19-step wizard with full control)
|
||||
@@ -300,7 +297,6 @@ echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}"
|
||||
**Purpose**: Main orchestrator for LXC container creation
|
||||
|
||||
**Operations**:
|
||||
|
||||
1. Validates all variables
|
||||
2. Creates LXC container via `pct create`
|
||||
3. Executes `install/AppName-install.sh` inside container
|
||||
@@ -402,7 +398,6 @@ msg_ok "Completed successfully!\n"
|
||||
**Symptom**: `pct create` exits with error code 209
|
||||
|
||||
**Solution**:
|
||||
|
||||
```bash
|
||||
# Check existing containers
|
||||
pct list | grep CTID
|
||||
@@ -416,7 +411,6 @@ pct destroy CTID
|
||||
### Update Function Doesn't Detect New Version
|
||||
|
||||
**Debug**:
|
||||
|
||||
```bash
|
||||
# Check version file
|
||||
cat /opt/AppName_version.txt
|
||||
@@ -432,7 +426,6 @@ curl -fsSL https://api.github.com/repos/user/repo/releases/latest | grep tag_nam
|
||||
Before submitting a PR:
|
||||
|
||||
### Script Structure
|
||||
|
||||
- [ ] Shebang is `#!/usr/bin/env bash`
|
||||
- [ ] Imports `build.func` from community-scripts repo
|
||||
- [ ] Copyright header with author and source URL
|
||||
@@ -440,20 +433,17 @@ Before submitting a PR:
|
||||
- [ ] `var_tags` are semicolon-separated (no spaces)
|
||||
|
||||
### Default Values
|
||||
|
||||
- [ ] `var_cpu` set appropriately (2-4 for most apps)
|
||||
- [ ] `var_ram` set appropriately (1024-4096 MB minimum)
|
||||
- [ ] `var_disk` sufficient for app + data (5-20 GB)
|
||||
- [ ] `var_os` is realistic
|
||||
|
||||
### Functions
|
||||
|
||||
- [ ] `update_script()` implemented
|
||||
- [ ] Update function checks if app installed
|
||||
- [ ] Proper error handling with `msg_error`
|
||||
|
||||
### Testing
|
||||
|
||||
- [ ] Script tested with default installation
|
||||
- [ ] Script tested with advanced (19-step) installation
|
||||
- [ ] Update function tested on existing installation
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
# Misc Documentation
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ **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
|
||||
@@ -33,11 +23,9 @@ The important implementation detail is that these libraries are **not independen
|
||||
---
|
||||
|
||||
### 📁 [core.func/](./core.func/)
|
||||
|
||||
**System Utilities & Foundation** - Shared runtime foundation for logging, prompts, validation, and execution control
|
||||
**System Utilities & Foundation** - Essential utility functions and system checks
|
||||
|
||||
**Contents:**
|
||||
|
||||
- CORE_FLOWCHART.md - Visual execution flows
|
||||
- CORE_FUNCTIONS_REFERENCE.md - Complete function reference
|
||||
- CORE_INTEGRATION.md - Integration points
|
||||
@@ -49,11 +37,9 @@ The important implementation detail is that these libraries are **not independen
|
||||
---
|
||||
|
||||
### 📁 [error_handler.func/](./error_handler.func/)
|
||||
|
||||
**Error Handling & Signal Management** - Trap orchestration, cleanup, and abort telemetry
|
||||
**Error Handling & Signal Management** - Comprehensive error handling and signal trapping
|
||||
|
||||
**Contents:**
|
||||
|
||||
- ERROR_HANDLER_FLOWCHART.md - Visual error handling flows
|
||||
- ERROR_HANDLER_FUNCTIONS_REFERENCE.md - Function reference
|
||||
- ERROR_HANDLER_INTEGRATION.md - Integration with other components
|
||||
@@ -65,45 +51,39 @@ The important implementation detail is that these libraries are **not independen
|
||||
---
|
||||
|
||||
### 📁 [api.func/](./api.func/)
|
||||
|
||||
**Telemetry & Diagnostics Runtime** - Anonymous telemetry reporting, progress tracking, and canonical exit-code mapping
|
||||
**Proxmox API Integration** - API communication and diagnostic reporting
|
||||
|
||||
**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_to_api_vm()`, `post_progress_to_api()`, `post_update_to_api()`, `explain_exit_code()`
|
||||
**Key Functions**: `post_to_api()`, `post_update_to_api()`, `get_error_description()`
|
||||
|
||||
---
|
||||
|
||||
## 📦 **Installation & Setup Function Libraries**
|
||||
|
||||
### 📁 [install.func/](./install.func/)
|
||||
|
||||
**Container Installation Workflow** - Container bootstrap inside the LXC
|
||||
**Container Installation Workflow** - Installation orchestration for container-internal setup
|
||||
|
||||
**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()`, `customize()`
|
||||
**Key Functions**: `setting_up_container()`, `network_check()`, `update_os()`, `motd_ssh()`, `cleanup_lxc()`
|
||||
|
||||
---
|
||||
|
||||
### 📁 [tools.func/](./tools.func/)
|
||||
|
||||
**Package & Tool Installation** - Repository, package, release, runtime, and service toolbox
|
||||
**Package & Tool Installation** - Robust package management and 30+ tool installation functions
|
||||
|
||||
**Contents:**
|
||||
|
||||
- TOOLS_FUNC_FLOWCHART.md - Package management flows
|
||||
- TOOLS_FUNC_FUNCTIONS_REFERENCE.md - 30+ function reference
|
||||
- TOOLS_FUNC_INTEGRATION.md - Integration with install workflows
|
||||
@@ -111,16 +91,14 @@ The important implementation detail is that these libraries are **not independen
|
||||
- TOOLS_FUNC_ENVIRONMENT_VARIABLES.md - Configuration reference
|
||||
- README.md - Overview and quick reference
|
||||
|
||||
**Key Functions**: `curl_with_retry()`, `setup_deb822_repo()`, `install_packages_with_retry()`, `setup_mariadb()`, `setup_postgresql()`, `get_latest_github_release()`
|
||||
**Key Functions**: `setup_nodejs()`, `setup_php()`, `setup_mariadb()`, `setup_docker()`, `setup_deb822_repo()`, `pkg_install()`, `pkg_update()`
|
||||
|
||||
---
|
||||
|
||||
### 📁 [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
|
||||
@@ -132,11 +110,9 @@ The important implementation detail is that these libraries are **not independen
|
||||
---
|
||||
|
||||
### 📁 [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
|
||||
@@ -148,11 +124,9 @@ The important implementation detail is that these libraries are **not independen
|
||||
---
|
||||
|
||||
### 📁 [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
|
||||
@@ -171,24 +145,18 @@ The important implementation detail is that these libraries are **not independen
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ct/AppName.sh │
|
||||
│ ↓ sources │
|
||||
│ ↓ (sources) │
|
||||
│ build.func │
|
||||
│ ├─ sources api.func │
|
||||
│ ├─ sources core.func │
|
||||
│ ├─ sources error_handler.func │
|
||||
│ ├─ loads variables/settings/prompts │
|
||||
│ └─ creates container + launch phase │
|
||||
│ ↓ pct exec / lxc-attach │
|
||||
│ ├─ variables() │
|
||||
│ ├─ build_container() │
|
||||
│ └─ advanced_settings() │
|
||||
│ ↓ (calls pct create with) │
|
||||
│ install/appname-install.sh │
|
||||
│ ↓ 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 │
|
||||
│ ↓ (sources) │
|
||||
│ ├─ core.func (colors, messaging) │
|
||||
│ ├─ error_handler.func (error trapping) │
|
||||
│ ├─ install.func (setup/network) │
|
||||
│ └─ tools.func (packages/tools) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
|
||||
@@ -223,17 +191,17 @@ The important implementation detail is that these libraries are **not independen
|
||||
|
||||
## 📊 **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
|
||||
|
||||
@@ -242,20 +210,16 @@ The important implementation detail is that these libraries are **not independen
|
||||
## 🚀 **Getting Started**
|
||||
|
||||
### For Container Creation Scripts
|
||||
|
||||
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/)**
|
||||
Start with: **[build.func/](./build.func/)** → **[tools.func/](./tools.func/)** → **[install.func/](./install.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/)** → **[api.func/](./api.func/)**
|
||||
Start with: **[error_handler.func/](./error_handler.func/)** → **[EXIT_CODES.md](../EXIT_CODES.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -287,7 +251,6 @@ function-library/
|
||||
```
|
||||
|
||||
**Advantages**:
|
||||
|
||||
- ✅ Consistent navigation across all libraries
|
||||
- ✅ Quick reference sections in each README
|
||||
- ✅ Visual flowcharts for understanding
|
||||
@@ -310,12 +273,11 @@ All documentation follows these standards:
|
||||
|
||||
---
|
||||
|
||||
## ✅ **Last Updated**: Based on live `misc/*` code verification
|
||||
|
||||
## ✅ **Last Updated**: December 2025
|
||||
**Maintainers**: community-scripts team
|
||||
**License**: MIT
|
||||
**Status**: Canonical overviews aligned to live code; deeper generated subpages may still require occasional drift cleanup
|
||||
**Status**: All 9 libraries fully documented and standardized
|
||||
|
||||
---
|
||||
|
||||
_When documentation conflicts with the live shell implementation, prefer the files under `ProxmoxVE` / `ProxmoxVED` `misc/`._
|
||||
*This directory contains specialized documentation for specific components of the Proxmox Community Scripts project.*
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
## 🤖 PocketBase Bot — Command Reference
|
||||
|
||||
> Available to **org members only** (Contributors team).
|
||||
> Trigger by posting a comment on any Issue or PR.
|
||||
|
||||
---
|
||||
|
||||
### 🔧 Field Updates
|
||||
Simple key=value pairs. Multiple in one line.
|
||||
```
|
||||
/pocketbase <slug> 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`
|
||||
**Number**: `port`
|
||||
**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
|
||||
/pocketbase homeassistant is_dev=false
|
||||
/pocketbase homeassistant default_passwd=
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 📝 set — HTML / Multiline / Special Characters
|
||||
Use a code block for values that contain HTML, links, quotes or newlines.
|
||||
````
|
||||
/pocketbase <slug> set <field>
|
||||
```
|
||||
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`
|
||||
|
||||
---
|
||||
|
||||
### 🗒️ Notes
|
||||
```
|
||||
/pocketbase <slug> note list
|
||||
/pocketbase <slug> note add <type> "<text>"
|
||||
/pocketbase <slug> note edit <type> "<old text>" "<new text>"
|
||||
/pocketbase <slug> note remove <type> "<text>"
|
||||
```
|
||||
Note types come from `z_ref_note_types` in PocketBase (e.g. `info`, `warning`).
|
||||
If text doesn't match exactly, the bot lists all current notes automatically.
|
||||
|
||||
---
|
||||
|
||||
### ⚙️ Install Method Resources
|
||||
```
|
||||
/pocketbase <slug> method list
|
||||
/pocketbase <slug> method <type> hdd=10
|
||||
/pocketbase <slug> method <type> cpu=4 ram=2048 hdd=20
|
||||
```
|
||||
`<type>` 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
|
||||
@@ -1,158 +0,0 @@
|
||||
# App Deployer VM
|
||||
|
||||
Deploy LXC applications inside a full Virtual Machine instead of an LXC container.
|
||||
|
||||
## Overview
|
||||
|
||||
The App Deployer VM bridges the gap between CT install scripts (`install/*.sh`) and VM infrastructure. It leverages the existing install scripts — originally designed for LXC containers — and runs them **live during image build** via `virt-customize --run`.
|
||||
|
||||
### Supported Operating Systems
|
||||
|
||||
| OS | Version | Codename | Cloud-Init |
|
||||
| ------ | --------- | -------- | ---------- |
|
||||
| Debian | 13 | Trixie | Optional |
|
||||
| Debian | 12 | Bookworm | Optional |
|
||||
| Ubuntu | 24.04 LTS | Noble | Required |
|
||||
| Ubuntu | 22.04 LTS | Jammy | Required |
|
||||
|
||||
## Usage
|
||||
|
||||
### Create a new App VM (interactive)
|
||||
|
||||
```bash
|
||||
bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/vm/app-deployer-vm.sh)"
|
||||
```
|
||||
|
||||
### Pre-select application
|
||||
|
||||
```bash
|
||||
APP_SELECT=yamtrack bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/vm/app-deployer-vm.sh)"
|
||||
```
|
||||
|
||||
### Update the application later (inside the VM)
|
||||
|
||||
```bash
|
||||
bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/<app>.sh)"
|
||||
```
|
||||
|
||||
For example, to update Yamtrack:
|
||||
|
||||
```bash
|
||||
bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/yamtrack.sh)"
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Installation Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Proxmox Host │
|
||||
│ │
|
||||
│ 1. Select app (e.g. Yamtrack) │
|
||||
│ 2. Select OS (e.g. Debian 13) │
|
||||
│ 3. Configure VM resources │
|
||||
│ 4. Download cloud image │
|
||||
│ 5. virt-customize: │
|
||||
│ - Install base packages │
|
||||
│ - Inject install.func │
|
||||
│ - Inject tools.func │
|
||||
│ - Run install script LIVE │
|
||||
│ (virt-customize --run) │
|
||||
│ - Configure hostname & SSH │
|
||||
│ 6. Create VM (qm create) │
|
||||
│ 7. Import customized disk │
|
||||
│ 8. Start VM │
|
||||
│ │
|
||||
│ → Application pre-installed! │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Update Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Inside the VM (SSH or console) │
|
||||
│ │
|
||||
│ bash -c "$(curl -fsSL │
|
||||
│ $COMMUNITY_SCRIPTS_URL/ │
|
||||
│ ct/<app>.sh)" │
|
||||
│ │
|
||||
│ → start() detects no pveversion │
|
||||
│ → Shows update/settings menu │
|
||||
│ → Runs update_script() │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
The update mechanism reuses the existing CT script logic. Since `pveversion` is not available inside the VM, the `start()` function automatically enters the update/settings mode — exactly the same as running updates in LXC containers.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Files
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------- | ------------------------------------------- |
|
||||
| `vm/app-deployer-vm.sh` | Main user-facing script |
|
||||
| `misc/vm-app.func` | Core library for VM app deployment |
|
||||
| `misc/vm-core.func` | Shared VM functions (colors, spinner, etc.) |
|
||||
| `misc/cloud-init.func` | Cloud-Init configuration (optional) |
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
1. **Install scripts run unmodified** — The same `install/*.sh` scripts that work in LXC containers work inside VMs. The environment (`FUNCTIONS_FILE_PATH`, exports) is replicated identically.
|
||||
|
||||
2. **Image customization via `virt-customize`** — All dependencies are installed and the app install script runs live inside the qcow2 image during build. No SSH or guest agent required during setup.
|
||||
|
||||
3. **Live installation** — The install script runs during image build (not on first boot), so the application is ready immediately when the VM starts.
|
||||
|
||||
4. **Update via CT script URL** — Run the same `bash -c "$(curl ...ct/<app>.sh)"` command inside the VM, just like in an LXC container.
|
||||
|
||||
### Environment Variables (set during image build)
|
||||
|
||||
| Variable | Description |
|
||||
| ----------------------- | ---------------------------------- |
|
||||
| `FUNCTIONS_FILE_PATH` | Full contents of `install.func` |
|
||||
| `APPLICATION` | App display name (e.g. "Yamtrack") |
|
||||
| `app` | App identifier (e.g. "yamtrack") |
|
||||
| `VERBOSE` | "no" (silent mode) |
|
||||
| `SSH_ROOT` | "yes" |
|
||||
| `PCT_OSTYPE` | OS type (debian/ubuntu) |
|
||||
| `PCT_OSVERSION` | OS version (12/13/22.04/24.04) |
|
||||
| `COMMUNITY_SCRIPTS_URL` | Repository base URL |
|
||||
| `DEPLOY_TARGET` | "vm" (distinguishes from LXC) |
|
||||
|
||||
### VM Directory Structure
|
||||
|
||||
```
|
||||
/opt/community-scripts/
|
||||
├── install.func # Function library
|
||||
└── tools.func # Helper functions
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Alpine-based apps**: Currently only Debian/Ubuntu VMs are supported. Alpine install scripts are not compatible.
|
||||
- **LXC-specific features**: Some CT features (FUSE, TUN, GPU passthrough) are configured differently in VMs.
|
||||
- **`cleanup_lxc`**: This function works fine in VMs (it only cleans package caches), but the name is LXC-centric.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Check build log
|
||||
|
||||
If the installation fails during image build, check the log on the Proxmox host:
|
||||
|
||||
```bash
|
||||
cat /tmp/vm-app-install.log
|
||||
```
|
||||
|
||||
### Re-run installation
|
||||
|
||||
Re-build the VM from scratch — since the app is installed during image build, there is no in-VM reinstall mechanism. Simply delete the VM and run the deployer again.
|
||||
|
||||
### Verify installation worked
|
||||
|
||||
After the VM boots, SSH in and check if the application service is running:
|
||||
|
||||
```bash
|
||||
systemctl status <app-service-name>
|
||||
```
|
||||
5
frontend/.eslintrc.json
generated
Normal file
5
frontend/.eslintrc.json
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": ["next/core-web-vitals"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"]
|
||||
}
|
||||
39
frontend/.gitignore
vendored
Normal file
39
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# wrangler
|
||||
.worker-next
|
||||
.wrangler
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
out
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# # local env files
|
||||
# .env*.local
|
||||
# .env
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
5
frontend/.prettierignore
Normal file
5
frontend/.prettierignore
Normal file
@@ -0,0 +1,5 @@
|
||||
dist
|
||||
node_modules
|
||||
.next
|
||||
build
|
||||
.contentlayer
|
||||
3
frontend/.prettierrc
Normal file
3
frontend/.prettierrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"plugins": ["prettier-plugin-tailwindcss", "prettier-plugin-organize-imports"]
|
||||
}
|
||||
21
frontend/LICENSE
Normal file
21
frontend/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Bram Suurd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
17
frontend/components.json
generated
Normal file
17
frontend/components.json
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "@/styles/globals.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils"
|
||||
}
|
||||
}
|
||||
25
frontend/next.config.mjs
Normal file
25
frontend/next.config.mjs
Normal file
@@ -0,0 +1,25 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
webpack: (config) => {
|
||||
config.resolve.alias.canvas = false;
|
||||
|
||||
return config;
|
||||
},
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "**",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
env: {
|
||||
BASE_PATH: "ProxmoxVED",
|
||||
},
|
||||
|
||||
output: "export",
|
||||
basePath: `/ProxmoxVED`,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
10816
frontend/package-lock.json
generated
Normal file
10816
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
93
frontend/package.json
generated
Normal file
93
frontend/package.json
generated
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"name": "proxmox-helper-scripts-website",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"author": {
|
||||
"name": "Bram Suurd",
|
||||
"url": "https://github.com/community-scripts"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"test": "vitest",
|
||||
"deploy": "next build && touch out/.nojekyll && git add out/ && git commit -m \"Deploy\" && git subtree push --prefix out origin gh-pages",
|
||||
"format:write": "prettier --write \"**/*.{ts,tsx,mdx}\" --cache",
|
||||
"format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-dialog": "^1.1.6",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-label": "^2.1.2",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.5",
|
||||
"@radix-ui/react-popover": "^1.1.6",
|
||||
"@radix-ui/react-select": "^2.1.6",
|
||||
"@radix-ui/react-separator": "^1.1.2",
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@radix-ui/react-switch": "^1.1.3",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@radix-ui/react-tooltip": "^1.1.8",
|
||||
"@tanstack/react-query": "^5.71.1",
|
||||
"chart.js": "^4.4.8",
|
||||
"chartjs-plugin-datalabels": "^2.2.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"framer-motion": "^11.18.2",
|
||||
"fuse.js": "^7.1.0",
|
||||
"lucide-react": "^0.453.0",
|
||||
"mini-svg-data-uri": "^1.4.4",
|
||||
"next": "15.5.7",
|
||||
"next-themes": "^0.3.0",
|
||||
"nuqs": "^2.4.1",
|
||||
"pocketbase": "^0.21.5",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"react": "19.0.0",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-code-blocks": "^0.1.6",
|
||||
"react-datepicker": "^7.6.0",
|
||||
"react-day-picker": "8.10.1",
|
||||
"react-dom": "19.0.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-simple-typewriter": "^5.0.1",
|
||||
"sharp": "^0.33.5",
|
||||
"simple-icons": "^13.21.0",
|
||||
"sonner": "^1.7.4",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tanstack/eslint-plugin-query": "^5.68.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@types/node": "^22.13.16",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.29.0",
|
||||
"@typescript-eslint/parser": "^8.29.0",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-config-next": "15.0.2",
|
||||
"jsdom": "^25.0.1",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tailwindcss-animated": "^1.1.2",
|
||||
"typescript": "^5.8.2",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^3.1.1"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1"
|
||||
}
|
||||
}
|
||||
8
frontend/postcss.config.mjs
Normal file
8
frontend/postcss.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
BIN
frontend/public/defaultimg.png
Normal file
BIN
frontend/public/defaultimg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user