mirror of
https://git.coastit.co.za/caitlin/CRM_ENVR.git
synced 2026-07-10 11:15:43 +02:00
adding a wizard for setup
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import http from "node:http"
|
||||
import fs from "node:fs"
|
||||
import path from "node:path"
|
||||
import { spawn } from "node:child_process"
|
||||
import { fileURLToPath } from "node:url"
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
@@ -34,6 +35,10 @@ const DATABASE_URL = process.env.DATABASE_URL
|
||||
const JOBS_PATH = process.env.JOBS_PATH || path.join(ROOT, "data", "ai", "jobs.jsonl")
|
||||
const AI_MD_PATH = process.env.AI_MD_PATH || path.join(ROOT, "data", "ai", "ai.md")
|
||||
|
||||
// ── Setup state ──────────────────────────────────────────────────
|
||||
let pullProcess = null
|
||||
let pullProgress = { status: "idle", progress: 0, message: "" }
|
||||
|
||||
// ── Job loading ─────────────────────────────────────────────────
|
||||
function loadJobs() {
|
||||
try {
|
||||
@@ -297,6 +302,140 @@ const server = http.createServer(async (req, res) => {
|
||||
return sendJSON(res, 200, results)
|
||||
}
|
||||
|
||||
// ── Setup endpoints ─────────────────────────────────────────
|
||||
|
||||
// GET /setup/status — check environment
|
||||
if (req.method === "GET" && pathname === "/setup/status") {
|
||||
const envExists = fs.existsSync(path.join(ROOT, ".env.local"))
|
||||
|
||||
// Ollama check
|
||||
let ollamaRunning = false
|
||||
try {
|
||||
await fetch(`${OLLAMA_URL}/api/tags`, { signal: AbortSignal.timeout(3000) })
|
||||
ollamaRunning = true
|
||||
} catch {}
|
||||
|
||||
// Model check
|
||||
let modelAvailable = false
|
||||
if (ollamaRunning) {
|
||||
try {
|
||||
const r = await fetch(`${OLLAMA_URL}/api/show`, {
|
||||
method: "POST", body: JSON.stringify({ name: MODEL }),
|
||||
signal: AbortSignal.timeout(5000),
|
||||
})
|
||||
modelAvailable = r.ok
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Profile auto-detect
|
||||
let profilePath = process.env.FX_PROFILE || ""
|
||||
let profileDetected = !!profilePath
|
||||
if (!profileDetected) {
|
||||
try {
|
||||
const r = await fetch("http://127.0.0.1:3008/health", { signal: AbortSignal.timeout(2000) })
|
||||
if (r.ok) {
|
||||
const diag = await (await fetch("http://127.0.0.1:3008/setup/profile", { signal: AbortSignal.timeout(5000) })).json()
|
||||
if (diag.path) { profilePath = diag.path; profileDetected = true }
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Login check
|
||||
let facebookLoggedIn = false
|
||||
if (profileDetected) {
|
||||
try {
|
||||
const r = await fetch("http://127.0.0.1:3008/setup/check-login", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ profile_path: profilePath }),
|
||||
signal: AbortSignal.timeout(15000),
|
||||
})
|
||||
if (r.ok) { const d = await r.json(); facebookLoggedIn = d.logged_in === true }
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const firstRun = !envExists || !ollamaRunning || !profileDetected || !modelAvailable
|
||||
|
||||
return sendJSON(res, 200, {
|
||||
first_run: firstRun,
|
||||
env_exists: envExists,
|
||||
ollama_running: ollamaRunning,
|
||||
model_available: modelAvailable,
|
||||
model_name: MODEL,
|
||||
profile_detected: profileDetected,
|
||||
profile_path: profilePath || null,
|
||||
facebook_logged_in: facebookLoggedIn,
|
||||
})
|
||||
}
|
||||
|
||||
// POST /setup/profile — save profile path to .env.local
|
||||
if (req.method === "POST" && pathname === "/setup/profile") {
|
||||
const body = await parseBody(req)
|
||||
const profilePath = (body.path || "").trim()
|
||||
if (!profilePath) return sendJSON(res, 400, { error: "Path required" })
|
||||
if (!fs.existsSync(profilePath)) return sendJSON(res, 400, { error: "Path does not exist" })
|
||||
|
||||
const envPath = path.join(ROOT, ".env.local")
|
||||
let content = ""
|
||||
try { content = fs.readFileSync(envPath, "utf-8") } catch {}
|
||||
const lines = content.split("\n")
|
||||
let found = false
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].trim().startsWith("FX_PROFILE=")) {
|
||||
lines[i] = `FX_PROFILE=${profilePath}`
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) lines.push(`FX_PROFILE=${profilePath}`)
|
||||
fs.writeFileSync(envPath, lines.join("\n"), "utf-8")
|
||||
process.env.FX_PROFILE = profilePath
|
||||
return sendJSON(res, 200, { success: true, path: profilePath })
|
||||
}
|
||||
|
||||
// POST /setup/check-login — verify Facebook login in the given profile
|
||||
if (req.method === "POST" && pathname === "/setup/check-login") {
|
||||
const body = await parseBody(req)
|
||||
const profilePath = body.profile_path || process.env.FX_PROFILE || ""
|
||||
if (!profilePath) return sendJSON(res, 200, { logged_in: false, reason: "no_profile" })
|
||||
try {
|
||||
const r = await fetch("http://127.0.0.1:3008/setup/check-login", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ profile_path: profilePath }),
|
||||
signal: AbortSignal.timeout(20000),
|
||||
})
|
||||
if (r.ok) { const d = await r.json(); return sendJSON(res, 200, d) }
|
||||
} catch {}
|
||||
return sendJSON(res, 200, { logged_in: false, reason: "scraper_unavailable" })
|
||||
}
|
||||
|
||||
// POST /setup/ollama/pull — start pulling the model
|
||||
if (req.method === "POST" && pathname === "/setup/ollama/pull") {
|
||||
if (pullProcess) return sendJSON(res, 200, { status: "already_running" })
|
||||
pullProgress = { status: "downloading", progress: 0, message: "Starting..." }
|
||||
const isWin = process.platform === "win32"
|
||||
const cmd = isWin ? "ollama.exe" : "ollama"
|
||||
pullProcess = spawn(cmd, ["pull", MODEL], { stdio: ["ignore", "pipe", "pipe"] })
|
||||
|
||||
pullProcess.stdout.on("data", (data) => {
|
||||
const text = data.toString()
|
||||
pullProgress.message = text.trim()
|
||||
// Extract percentage from patterns like "pulling xxxx... 45%"
|
||||
const m = text.match(/(\d+)%/)
|
||||
if (m) pullProgress.progress = parseInt(m[1], 10)
|
||||
})
|
||||
pullProcess.on("close", (code) => {
|
||||
pullProcess = null
|
||||
pullProgress.status = code === 0 ? "done" : "failed"
|
||||
if (code === 0) pullProgress.progress = 100
|
||||
})
|
||||
return sendJSON(res, 200, { status: "started" })
|
||||
}
|
||||
|
||||
// GET /setup/ollama/pull/progress
|
||||
if (req.method === "GET" && pathname === "/setup/ollama/pull/progress") {
|
||||
return sendJSON(res, 200, pullProgress)
|
||||
}
|
||||
|
||||
// GET /ai/jobs
|
||||
if (req.method === "GET" && pathname === "/ai/jobs") {
|
||||
return sendJSON(res, 200, { jobs: loadedJobs })
|
||||
|
||||
Reference in New Issue
Block a user