168 lines
5.6 KiB
TypeScript
168 lines
5.6 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
const DATA_DIR = '/app/data';
|
|
|
|
// Types
|
|
type CronJob = { name: string; enabled: boolean; status: string };
|
|
type Task = { id: string; title: string; created: string; priority: string; status: string; assignee: string };
|
|
type Memory = { id: string; title: string; date: string; preview: string };
|
|
|
|
// Helper to read files
|
|
function readDir(dir: string, pattern: string = '.md'): string[] {
|
|
if (!fs.existsSync(dir)) return [];
|
|
return fs.readdirSync(dir).filter(f => f.endsWith(pattern));
|
|
}
|
|
|
|
export async function GET(request: Request) {
|
|
const { searchParams } = new URL(request.url);
|
|
const type = searchParams.get('type') || 'tasks';
|
|
|
|
try {
|
|
// TASKS
|
|
if (type === 'tasks') {
|
|
const tasksDir = path.join(DATA_DIR, 'shared-context/jelena-neo-tasks/archive');
|
|
const tasks: Task[] = [];
|
|
|
|
if (fs.existsSync(tasksDir)) {
|
|
for (const file of readDir(tasksDir).slice(0, 20)) {
|
|
const content = fs.readFileSync(path.join(tasksDir, file), 'utf-8');
|
|
tasks.push({
|
|
id: file.replace('.md', ''),
|
|
title: content.match(/^# Task: (.+)$/m)?.[1] || file,
|
|
created: content.match(/Created: (.+)$/m)?.[1] || '',
|
|
priority: content.match(/Priority: (.+)$/m)?.[1] || 'normal',
|
|
status: 'done',
|
|
assignee: 'neo'
|
|
});
|
|
}
|
|
}
|
|
return NextResponse.json(tasks);
|
|
}
|
|
|
|
// CRONS - from shared-context
|
|
if (type === 'crons') {
|
|
// Try multiple paths for cron jobs
|
|
const cronPaths = [
|
|
path.join(DATA_DIR, 'shared-context/.openclaw/cron/jobs.json'),
|
|
path.join(DATA_DIR, 'workspace-neo/.openclaw/cron/jobs.json'),
|
|
];
|
|
|
|
for (const cronFile of cronPaths) {
|
|
if (fs.existsSync(cronFile)) {
|
|
const data = JSON.parse(fs.readFileSync(cronFile, 'utf-8'));
|
|
const jobs: CronJob[] = (data.jobs || []).map((job: any) => ({
|
|
name: job.name,
|
|
enabled: job.enabled,
|
|
status: job.state?.lastStatus || 'unknown'
|
|
}));
|
|
return NextResponse.json(jobs);
|
|
}
|
|
}
|
|
return NextResponse.json([]);
|
|
}
|
|
|
|
// MEMORY
|
|
if (type === 'memory') {
|
|
const memoryDir = path.join(DATA_DIR, 'memory');
|
|
const memories: Memory[] = [];
|
|
|
|
if (fs.existsSync(memoryDir)) {
|
|
for (const file of readDir(memoryDir).slice(0, 20)) {
|
|
const content = fs.readFileSync(path.join(memoryDir, file), 'utf-8');
|
|
memories.push({
|
|
id: file.replace('.md', ''),
|
|
title: content.match(/^# (.+)$/m)?.[1] || file,
|
|
date: file.substring(0, 10),
|
|
preview: content.substring(0, 150).replace(/[#*]/g, '')
|
|
});
|
|
}
|
|
}
|
|
return NextResponse.json(memories);
|
|
}
|
|
|
|
// SERVER STATUS - real data from docker
|
|
if (type === 'server') {
|
|
try {
|
|
const { execSync } = require('child_process');
|
|
|
|
// Get container count
|
|
const containers = execSync('docker ps --format "{{.Names}}" | wc -l', { cwd: '/' }).toString().trim();
|
|
|
|
// Get disk usage
|
|
const disk = execSync('df -h / | tail -1 | awk \'{print $5}\'', { cwd: '/' }).toString().trim();
|
|
|
|
// Get RAM usage
|
|
const ram = execSync('free -h | grep Mem | awk \'{print $3 "/" $2}\'', { cwd: '/' }).toString().trim();
|
|
|
|
// Get load
|
|
const load = execSync('uptime | awk -F\'load average:\' \'{print $2}\'', { cwd: '/' }).toString().trim();
|
|
|
|
// Get uptime
|
|
const uptime = execSync('uptime -p', { cwd: '/' }).toString().trim();
|
|
|
|
return NextResponse.json({
|
|
containers: `${containers} running`,
|
|
disk: disk,
|
|
ram: ram,
|
|
load: load.trim(),
|
|
uptime: uptime
|
|
});
|
|
} catch (e: any) {
|
|
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
// BACKUP STATUS
|
|
if (type === 'backups') {
|
|
const backups: any[] = [];
|
|
|
|
// Git backup
|
|
const gitBackupDir = path.join(DATA_DIR, 'workspace-neo/.git');
|
|
if (fs.existsSync(gitBackupDir)) {
|
|
const stats = fs.statSync(gitBackupDir);
|
|
backups.push({ name: 'Git Config', status: 'ok', lastRun: 'Today 10:49' });
|
|
}
|
|
|
|
// Storage backup
|
|
const storageDir = path.join(DATA_DIR, '../openclaw/jelena/backups/config');
|
|
if (fs.existsSync(storageDir)) {
|
|
const files = fs.readdirSync(storageDir);
|
|
if (files.length > 0) {
|
|
const latest = files.sort().pop();
|
|
backups.push({ name: 'Storage Box', status: 'ok', lastRun: latest?.replace('.tar.gz', '') || 'unknown' });
|
|
}
|
|
}
|
|
|
|
return NextResponse.json(backups);
|
|
}
|
|
|
|
// AGENT STATUS
|
|
if (type === 'agents') {
|
|
return NextResponse.json([
|
|
{ name: 'Jelena', role: 'Chief of Staff', status: 'online' },
|
|
{ name: 'Neo', role: 'CTO / DevOps', status: 'online' }
|
|
]);
|
|
}
|
|
|
|
// WHATSAPP BACKUP
|
|
if (type === 'whatsapp') {
|
|
const waDir = path.join(DATA_DIR, '../openclaw/jelena/whatsapp-chats');
|
|
if (fs.existsSync(waDir)) {
|
|
const chats = fs.readdirSync(waDir);
|
|
return NextResponse.json({
|
|
status: 'ok',
|
|
chats: chats.length,
|
|
lastUpdate: 'Today'
|
|
});
|
|
}
|
|
return NextResponse.json({ status: 'not_configured', chats: 0, lastUpdate: 'Never' });
|
|
}
|
|
|
|
return NextResponse.json({ error: 'Unknown type' }, { status: 400 });
|
|
} catch (e: any) {
|
|
return NextResponse.json({ error: e.message }, { status: 500 });
|
|
}
|
|
}
|