feat: add researchowl

This commit is contained in:
2026-04-27 13:53:42 +00:00
parent 859bed930f
commit 65f93b745d
4 changed files with 268 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-verify
namespace: backup-system
spec:
schedule: "0 4 * * 1"
concurrencyPolicy: Forbid
failedJobsHistoryLimit: 3
successfulJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
nodeSelector:
kubernetes.io/hostname: chemavx-k8
restartPolicy: OnFailure
securityContext:
runAsUser: 0
containers:
- name: verify
image: rclone/rclone:latest
command: ["/bin/sh", "/scripts/verify.sh"]
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 50m
memory: 64Mi
volumeMounts:
- mountPath: /rclone/rclone.conf
name: rclone-conf
- mountPath: /scripts
name: scripts
volumes:
- name: rclone-conf
hostPath:
path: /home/chemavx/.config/rclone/rclone.conf
type: File
- name: scripts
hostPath:
path: /data/backups/scripts
type: Directory
+108
View File
@@ -0,0 +1,108 @@
// n8n Code node — Health Check
// Usa https de Node.js (NODE_FUNCTION_ALLOW_BUILTIN=https)
// Patrón async IIFE: compatible con n8n 2.x task runner
const https = require('https');
return await (async () => {
// ── Configuración ────────────────────────────────────────────────────────
const TELEGRAM_TOKEN = '8611913802:AAFlrFtc0vYISOliO_W8B4c-W1ue0hG9Fio';
const TELEGRAM_CHAT = '5138407666';
const RESTART_WEBHOOK = 'https://n8n.chemavx.xyz/webhook/uptime-kuma-restart';
const SERVICES = [
{ name: 'n8n', url: 'https://n8n.chemavx.xyz' },
{ name: 'openclaw', url: 'https://openclaw.chemavx.xyz' },
{ name: 'vaultwarden', url: 'https://vaultwarden.chemavx.xyz' },
{ name: 'grafana', url: 'https://grafana.chemavx.xyz' },
{ name: 'uptime', url: 'https://uptime.chemavx.xyz' },
{ name: 'auth', url: 'https://auth.chemavx.xyz' },
{ name: 'home', url: 'https://home.chemavx.xyz' },
{ name: 'git', url: 'https://git.chemavx.xyz' },
{ name: 'argocd', url: 'https://argocd.chemavx.xyz' },
{ name: 'polymarket', url: 'https://polymarket.chemavx.xyz' },
{ name: 'ollama', url: 'https://ollama.chemavx.xyz/api/tags' },
];
// ── Helpers ──────────────────────────────────────────────────────────────
const checkService = (service) => {
return new Promise((resolve) => {
const req = https.get(service.url, (res) => {
resolve({ name: service.name, url: service.url, ok: res.statusCode < 400, status: res.statusCode });
res.resume();
});
req.on('error', (err) => {
resolve({ name: service.name, url: service.url, ok: false, status: 0, reason: err.message });
});
req.setTimeout(10000, () => {
req.destroy();
resolve({ name: service.name, url: service.url, ok: false, status: 0, reason: 'timeout' });
});
});
};
const httpsPost = (urlStr, body) => {
return new Promise((resolve) => {
const data = JSON.stringify(body);
const u = new URL(urlStr);
const options = {
hostname: u.hostname,
path: u.pathname + u.search,
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
};
const req = https.request(options, (res) => {
res.resume();
resolve();
});
req.on('error', () => resolve());
req.setTimeout(10000, () => { req.destroy(); resolve(); });
req.write(data);
req.end();
});
};
const sendTelegram = (text) => {
return httpsPost(
'https://api.telegram.org/bot' + TELEGRAM_TOKEN + '/sendMessage',
{ chat_id: TELEGRAM_CHAT, text, parse_mode: 'Markdown' }
);
};
const triggerRestart = (failedServices) => {
return httpsPost(RESTART_WEBHOOK, { failed: failedServices });
};
// ── Main ─────────────────────────────────────────────────────────────────
const results = await Promise.all(SERVICES.map(checkService));
const failed = results.filter(r => !r.ok);
const passed = results.filter(r => r.ok);
if (failed.length > 0) {
const lines = failed
.map(s => '• *' + s.name + '*: ' + (s.reason || ('HTTP ' + s.status)) + ' (' + s.url + ')')
.join('\n');
const message = '⚠️ *Health Check Failed*\n\n' + lines + '\n\n_Auto-restart triggered_';
await sendTelegram(message);
await triggerRestart(failed);
}
// ── Return ───────────────────────────────────────────────────────────────
return [{
json: {
checked: results.length,
passed: passed.length,
failed: failed.length,
allOk: failed.length === 0,
results,
},
}];
})();
+20
View File
@@ -0,0 +1,20 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: researchowl
namespace: argocd
spec:
project: default
source:
repoURL: https://git.chemavx.xyz/chemavx/k8s-manifests
targetRevision: HEAD
path: researchowl
destination:
server: https://kubernetes.default.svc
namespace: researchowl
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
+96
View File
@@ -0,0 +1,96 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: researchowl
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: researchowl-data
namespace: researchowl
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: researchowl
namespace: researchowl
labels:
app: researchowl
spec:
replicas: 1
selector:
matchLabels:
app: researchowl
template:
metadata:
labels:
app: researchowl
spec:
containers:
- name: researchowl
image: git.chemavx.xyz/chemavx/researchowl:latest
imagePullPolicy: Always
env:
- name: TELEGRAM_BOT_TOKEN
valueFrom:
secretKeyRef:
name: researchowl-secrets
key: telegram-bot-token
- name: TELEGRAM_ALLOWED_USERS
valueFrom:
secretKeyRef:
name: researchowl-secrets
key: telegram-allowed-users
- name: OLLAMA_URL
value: "http://ollama.chemavx.xyz"
- name: OLLAMA_MODEL
value: "qwen2.5:3b"
- name: DB_PATH
value: "/data/researchowl.db"
- name: MAX_SOURCES
value: "150"
- name: MAX_DEPTH
value: "3"
- name: QUALITY_THRESHOLD
value: "0.4"
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: researchowl-data
imagePullSecrets:
- name: gitea-registry
---
# Secret template — fill with real values and apply manually
# kubectl create secret generic researchowl-secrets \
# --from-literal=telegram-bot-token=YOUR_TOKEN \
# --from-literal=telegram-allowed-users=YOUR_USER_ID \
# -n researchowl
apiVersion: v1
kind: Secret
metadata:
name: researchowl-secrets
namespace: researchowl
type: Opaque
stringData:
telegram-bot-token: "REPLACE_ME"
telegram-allowed-users: "REPLACE_ME"