Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/backend-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Backend Smoke Test

on:
push:
branches: [ main, master, dev ]
pull_request:
branches: [ main, master, dev ]

jobs:
smoke-test:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:15
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: mysecretpassword
POSTGRES_DB: lira_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- uses: actions/checkout@v3

- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: Chat/backend/package-lock.json

- name: Install Dependencies
run: |
cd Chat/backend
npm ci

- name: Setup Database
env:
DATABASE_URL: postgresql://postgres:mysecretpassword@localhost:5432/lira_db
run: |
cd Chat/backend
npx prisma db push

- name: Start Backend
run: |
cd Chat/backend
npm run dev > backend.log 2>&1 &
echo "Waiting for backend to start..."
# Wait for port 4000
timeout 30s bash -c 'until curl -s http://localhost:4000/health > /dev/null; do sleep 1; done'
echo "Backend started!"
env:
DATABASE_URL: postgresql://postgres:mysecretpassword@localhost:5432/lira_db
# Basic env vars needed for server startup
MISTRAL_API_KEY: "dummy_key_for_ci"
PORT: 4000

- name: Run Smoke Test
run: |
cd Chat/backend
npm run test:smoke
2 changes: 2 additions & 0 deletions Chat/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_GEMINI_API_KEY=your_gemini_api_key_here
VITE_API_BASE_URL=http://localhost:4000
117 changes: 117 additions & 0 deletions Chat/backend/diagnose_app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@

const BASE_URL = 'http://localhost:4000';

async function runDiagnosis() {
console.log('Starting System Diagnosis...\n');
const results = {
health: null,
register: null,
login: null,
sessions: null,
memories: null,
apiHealth: null
};

// 1. Check Health
try {
const res = await fetch(`${BASE_URL}/health`);
results.health = { status: res.status, ok: res.ok };
if (res.ok) console.log('✅ /health is UP');
else console.error('❌ /health failed', res.status);
} catch (e) {
console.error('❌ /health error:', e.message);
results.health = { error: e.message };
}

// 2. Register
const testUser = {
email: `diag_${Date.now()}@example.com`,
password: 'password123',
username: 'diaguser'
};

try {
const res = await fetch(`${BASE_URL}/api/auth/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(testUser)
});
const data = await res.json();
results.register = { status: res.status, ok: res.ok };

if (res.ok) {
console.log('✅ Registration successful');
} else {
console.error('❌ Registration failed', data);
}
} catch (e) {
console.error('❌ Registration error:', e.message);
results.register = { error: e.message };
}

// 3. Login
let token = null;
try {
const res = await fetch(`${BASE_URL}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: testUser.email, password: testUser.password })
});
const data = await res.json();
results.login = { status: res.status, ok: res.ok };

if (res.ok && data.token) {
token = data.token;
console.log('✅ Login successful');
} else {
console.error('❌ Login failed', data);
}
} catch (e) {
console.error('❌ Login error:', e.message);
results.login = { error: e.message };
}

if (token) {
const headers = { 'Authorization': `Bearer ${token}` };

// 4. Check Sessions
try {
const res = await fetch(`${BASE_URL}/api/chat/sessions`, { headers });
const data = await res.json();
results.sessions = { status: res.status, ok: res.ok, count: Array.isArray(data) ? data.length : 'unknown' };
if (res.ok) console.log('✅ /api/chat/sessions accessed');
else console.error('❌ /api/chat/sessions failed', res.status);
} catch (e) {
console.error('❌ /api/chat/sessions error:', e.message);
}

// 5. Check Memories
try {
const res = await fetch(`${BASE_URL}/api/memories`, { headers });
const data = await res.json();
results.memories = { status: res.status, ok: res.ok };
if (res.ok) console.log('✅ /api/memories accessed');
else console.error('❌ /api/memories failed', res.status);
} catch (e) {
console.error('❌ /api/memories error:', e.message);
}

// 6. Check /api/health (Protected)
// Note: Previous manual checks showed 404 for authorized /api/health, let's verify programmatically
try {
const res = await fetch(`${BASE_URL}/api/health`, { headers });
results.apiHealth = { status: res.status, ok: res.ok };
if (res.ok) console.log('✅ /api/health accessed (Authorized)');
else console.warn(`⚠️ /api/health returned ${res.status} (Expected if route doesn't exist)`);
} catch (e) {
console.error('❌ /api/health error:', e.message);
}
} else {
console.warn('⚠️ Skipping authenticated checks due to login failure');
}

console.log('\nDiagnosis Complete.');
console.log(JSON.stringify(results, null, 2));
}

runDiagnosis();
1 change: 1 addition & 0 deletions Chat/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"scripts": {
"dev": "node server.js",
"start": "node server.js",
"test:smoke": "node smoke_test.js",
"postinstall": "prisma generate"
},
"dependencies": {
Expand Down
80 changes: 80 additions & 0 deletions Chat/backend/routes/developer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import express from 'express';
import { requireAuth } from '../../middlewares/authMiddleware.js';
import { isAdmin } from '../../authStore.js';
import prisma from '../../prismaClient.js';
import { pcController } from '../../services/pcControllerService.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import modulesRouter from './modules.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const BACKEND_ROOT = path.resolve(__dirname, '../../');

const router = express.Router();

// Middleware: Require Admin
router.use(requireAuth);
router.use(async (req, res, next) => {
const isAdm = await isAdmin(req.userId);
if (!isAdm) return res.status(403).json({ error: 'Developer access only' });
next();
});

// GET /api/developer/stats - Global System Stats
router.get('/stats', async (req, res) => {
try {
const userCount = await prisma.user.count();
const sessionCount = await prisma.session.count();
const memoryCount = await prisma.memory.count();
const activeJobs = await prisma.imageJob.count({ where: { status: 'generating' } });

// System Resource Stats
const systemStats = await pcController.getSystemStats();

res.json({
application: {
users: userCount,
sessions: sessionCount,
memories: memoryCount,
active_jobs: activeJobs
},
system: systemStats,
timestamp: Date.now()
});
} catch (e) {
res.status(500).json({ error: e.message });
}
});

// GET /api/developer/logs - Recent Application Logs
router.get('/logs', async (req, res) => {
try {
const logPath = path.join(BACKEND_ROOT, 'backend.log');
if (fs.existsSync(logPath)) {
// Read last 1000 lines or last 50KB roughly
const content = fs.readFileSync(logPath, 'utf-8');
const lines = content.split('\n').slice(-200); // Last 200 lines
res.json({ logs: lines });
} else {
res.json({ logs: [], message: 'No log file found' });
}
} catch (e) {
res.status(500).json({ error: e.message });
}
});

// GET /api/developer/config - View AI Config (Redacted)
router.get('/config', (req, res) => {
res.json({
mistral_model: process.env.MISTRAL_MODEL || 'mistral-medium',
xiaomi_model: process.env.XIAOMI_MODEL,
voice_provider: process.env.ELEVENLABS_API_KEY ? 'ElevenLabs' : 'XTTS (Local)',
vision_enabled: !!process.env.MISTRAL_PIXTRAL_API_KEY
});
});

router.use('/modules', modulesRouter);

export default router;
67 changes: 67 additions & 0 deletions Chat/backend/routes/developer/modules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Chat/backend/routes/developer/modules.js
import express from 'express';
const router = express.Router();

const modulesData = [
{
id: "lira-os-chat",
name: "LiraOS Chat",
type: "frontend",
root_path: "Chat/client",
main_files: ["src/App.tsx", "src/main.tsx"],
description: "Interface de Chat da LiraOS",
tags: ["react", "vite", "frontend"],
status: "active",
priority: "high"
},
{
id: "lira-os-backend",
name: "LiraOS Backend",
type: "backend",
root_path: "Chat/backend",
main_files: ["server.js", "services/ai.js"],
description: "API e Backend Principal",
tags: ["node", "express", "backend"],
status: "active",
priority: "high"
},
{
id: "lira-gamer",
name: "Lira Gamer",
type: "module",
root_path: "LiraGamer",
main_files: ["main.py", "game_agent.py"],
description: "Módulo de Integração com Jogos",
tags: ["python", "ai", "vision"],
status: "developing",
priority: "medium"
},
{
id: "dashboard",
name: "Lira Developer Dashboard",
type: "tool",
root_path: "lira-developer-dashboard",
main_files: ["src/App.tsx"],
description: "Dashboard de Desenvolvimento",
tags: ["react", "vite", "dashboard"],
status: "active",
priority: "medium"
}
];

// GET /api/developer/modules
router.get('/', (req, res) => {
res.json(modulesData);
});

// GET /api/developer/modules/:id
router.get('/:id', (req, res) => {
const mod = modulesData.find(m => m.id === req.params.id);
if (mod) {
res.json(mod);
} else {
res.status(404).json({ error: "Module not found" });
}
});

export default router;
3 changes: 3 additions & 0 deletions Chat/backend/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import instagramRoutes from './routes/instagram.js';
import imagesRoutes from './routes/images.js';
import todosRoutes from './routes/todos.js';
import googleAuthRoutes from './routes/authGoogle.js';
import developerRoutes from './routes/developer/index.js';

// Services & Utils
import { discordService } from './services/discordService.js';
Expand Down Expand Up @@ -73,6 +74,7 @@ app.use((req, res, next) => {

// Health Check
app.get('/health', (req, res) => res.json({ status: 'ok', timestamp: Date.now() }));
app.get('/api/health', (req, res) => res.json({ status: 'ok', timestamp: Date.now() }));

// Routes Mounting
console.log('[DEBUG] Mounting routes...');
Expand Down Expand Up @@ -114,6 +116,7 @@ app.use('/api/patreon', patreonRoutes);
app.use('/api/instagram', instagramRoutes);
app.use('/api/todos', todosRoutes);
app.use('/api/auth/google', googleAuthRoutes);
app.use('/api/developer', developerRoutes);
// Generic fallback (must be last)
app.use('/api', chatRoutes);
console.log('[DEBUG] All routes mounted successfully');
Expand Down
Loading
Loading