Merge branch 'main' of https://git.coastit.co.za/caitlin/CRM_ENVR
This commit is contained in:
+61
-26
@@ -1,41 +1,76 @@
|
||||
import http from "http"
|
||||
|
||||
const AI_SERVICE = process.env.AI_SERVICE_URL || "http://localhost:3001"
|
||||
|
||||
function parseUrl(url: string) {
|
||||
const u = new URL(url)
|
||||
return { hostname: u.hostname, port: parseInt(u.port) || 80, path: u.pathname + u.search }
|
||||
}
|
||||
|
||||
export async function chatWithAI(message: string, jwtToken: string) {
|
||||
const res = await fetch(`${AI_SERVICE}/ai/chat`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${jwtToken}` },
|
||||
body: JSON.stringify({ message }),
|
||||
const body = JSON.stringify({ message })
|
||||
const { hostname, port, path } = parseUrl(`${AI_SERVICE}/ai/chat`)
|
||||
|
||||
const text = await new Promise<string>((resolve, reject) => {
|
||||
const req = http.request(
|
||||
{ hostname, port, path, method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${jwtToken}`, "Content-Length": Buffer.byteLength(body) } },
|
||||
(res) => {
|
||||
let data = ""
|
||||
res.on("data", (chunk) => data += chunk)
|
||||
res.on("end", () => {
|
||||
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(data)
|
||||
} else {
|
||||
reject(new Error(`AI error ${res.statusCode}: ${data.substring(0, 200)}`))
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
req.on("error", reject)
|
||||
req.write(body)
|
||||
req.end()
|
||||
})
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text()
|
||||
throw new Error(`AI service error (${res.status}): ${text}`)
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
const data = JSON.parse(text)
|
||||
return data.response || ""
|
||||
}
|
||||
|
||||
export async function fetchJobs() {
|
||||
export async function checkAiServiceStatus() {
|
||||
const { hostname, port, path } = parseUrl(`${AI_SERVICE}/health`)
|
||||
try {
|
||||
const res = await fetch(`${AI_SERVICE}/ai/jobs`)
|
||||
if (!res.ok) return []
|
||||
const data = await res.json()
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const req = http.get({ hostname, port, path, timeout: 3000 }, (res) => {
|
||||
let data = ""
|
||||
res.on("data", (chunk) => data += chunk)
|
||||
res.on("end", () => {
|
||||
try { resolve(JSON.parse(data).status === "ok" ? undefined : reject(new Error("not ok"))) } catch { reject(new Error("bad response")) }
|
||||
})
|
||||
})
|
||||
req.on("error", reject)
|
||||
req.on("timeout", () => { req.destroy(); reject(new Error("timeout")) })
|
||||
})
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchJobs() {
|
||||
const { hostname, port, path } = parseUrl(`${AI_SERVICE}/ai/jobs`)
|
||||
try {
|
||||
const text = await new Promise<string>((resolve, reject) => {
|
||||
const req = http.get({ hostname, port, path, timeout: 5000 }, (res) => {
|
||||
let data = ""
|
||||
res.on("data", (chunk) => data += chunk)
|
||||
res.on("end", () => resolve(data))
|
||||
})
|
||||
req.on("error", reject)
|
||||
req.on("timeout", () => { req.destroy(); reject(new Error("timeout")) })
|
||||
})
|
||||
const data = JSON.parse(text)
|
||||
return data.jobs || []
|
||||
} catch {
|
||||
console.warn("Failed to fetch AI jobs")
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkAiServiceStatus() {
|
||||
try {
|
||||
const res = await fetch(`${AI_SERVICE}/health`)
|
||||
if (!res.ok) return false
|
||||
const data = await res.json()
|
||||
return data.status === "ok"
|
||||
} catch {
|
||||
console.warn("Failed to check AI service status")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
+24
-2
@@ -17,6 +17,7 @@ export interface SessionUser {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
phone: string | null;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
role: string;
|
||||
@@ -85,7 +86,7 @@ export async function getUserByUsername(username: string) {
|
||||
|
||||
export async function getUserById(id: string) {
|
||||
const result = await query(
|
||||
` SELECT u.id, u.username, u.email, u.first_name, u.last_name,
|
||||
` SELECT u.id, u.username, u.email, u.phone, u.first_name, u.last_name,
|
||||
u.is_active, u.avatar_url,
|
||||
r.name AS role_name
|
||||
FROM users u
|
||||
@@ -185,6 +186,7 @@ export function mapDbUserToSessionUser(
|
||||
id: dbUser.id as string,
|
||||
username: dbUser.username as string,
|
||||
email: dbUser.email as string,
|
||||
phone: (dbUser.phone as string) || null,
|
||||
firstName: dbUser.first_name as string,
|
||||
lastName: dbUser.last_name as string,
|
||||
role: roleMapping[roleName] || roleName.toLowerCase(),
|
||||
@@ -201,7 +203,6 @@ export async function createSession(userId: string, role: string) {
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "strict",
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24, // 24 hours
|
||||
});
|
||||
|
||||
return token;
|
||||
@@ -218,6 +219,27 @@ export async function destroySession() {
|
||||
});
|
||||
}
|
||||
|
||||
export async function encryptPassword(password: string): Promise<string> {
|
||||
const result = await query("SELECT encrypt_password($1) AS encrypted", [password]);
|
||||
return result.rows[0]?.encrypted;
|
||||
}
|
||||
|
||||
export async function decryptPassword(encrypted: string): Promise<string | null> {
|
||||
try {
|
||||
const result = await query("SELECT decrypt_password($1) AS decrypted", [encrypted]);
|
||||
return result.rows[0]?.decrypted || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function setSessionContext(userId: string, ip?: string) {
|
||||
await query("SELECT set_config('app.current_user_id', $1, true)", [userId]);
|
||||
if (ip) {
|
||||
await query("SELECT set_config('app.current_ip', $1, true)", [ip]);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSessionUser(): Promise<SessionUser | null> {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
|
||||
Reference in New Issue
Block a user