feat: Enhance provider components with detailed documentation and comments

- Added comprehensive comments and JSDoc-style documentation to NotificationProvider, ThemeProvider, UserProvider, and WebsiteThemeProvider for better clarity and maintainability.
- Improved type definitions in index.ts for better code understanding and usage.
- Introduced Docker support with Dockerfiles for various services including AI server, signaling server, and browser-use service.
- Created a docker-compose.yml file to orchestrate multiple services including PostgreSQL, AI, scraper, and frontend.
- Added a startup guide (startup.txt) for setting up the CRM environment on Ubuntu with Docker.
- Included a .dockerignore file to exclude unnecessary files from Docker builds.
This commit is contained in:
Ace
2026-07-13 13:05:30 +02:00
parent dba4c84cd5
commit d35c806d5b
167 changed files with 3474 additions and 102 deletions
+12
View File
@@ -0,0 +1,12 @@
FROM node:20-slim
RUN apt-get update && apt-get install -y --no-install-recommends postgresql-client && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci --omit=dev
COPY ai-server/ ./ai-server/
COPY splash.html ./
COPY data/ ./data/
COPY scripts/run-migrations.mjs ./scripts/run-migrations.mjs
COPY database/ ./database/
EXPOSE 3001
CMD ["node", "ai-server/index.mjs"]
+9
View File
@@ -60,6 +60,7 @@ let pullProgress = { status: "idle", progress: 0, message: "" }
// ── Job loading ─────────────────────────────────────────────────
// Loads job categories from a JSONL file (one JSON object per line).
// Used as context for the AI sales coach chat responses.
/** Load job categories from the JSONL file at JOBS_PATH. Returns an array of parsed job objects. */
function loadJobs() {
try {
const content = fs.readFileSync(JOBS_PATH, "utf-8")
@@ -86,6 +87,7 @@ function loadJobs() {
// ── ai.md management ────────────────────────────────────────────
// ai.md is a Markdown file containing system instructions for the AI.
// It can be read, written, or appended to via the API.
/** Read the full contents of ai.md. Returns empty string if the file doesn't exist yet. */
function readInstructions() {
try {
return fs.readFileSync(AI_MD_PATH, "utf-8")
@@ -94,11 +96,13 @@ function readInstructions() {
}
}
/** Overwrite ai.md with new content. Returns the content that was written. */
function writeInstructions(content) {
fs.writeFileSync(AI_MD_PATH, content, "utf-8")
return content
}
/** Append a timestamped entry to the ## Improvement Log section of ai.md. Creates the section if it doesn't exist. */
function appendToImprovementLog(entry) {
// Adds a timestamped entry to the ## Improvement Log section of ai.md
const current = readInstructions()
@@ -153,6 +157,7 @@ async function scrapeFacebook() {
}
}
/** Format scraped Facebook leads into a human-readable Markdown string for the AI chat response. */
function formatLeads(leads) {
if (!leads || leads.length === 0) return "No leads found from the latest scrape."
let output = `**${leads.length} leads found:**\n\n`
@@ -239,6 +244,7 @@ Provide concise, actionable sales advice. When asked about a specific job catego
// ── PG pool (lazy init) ────────────────────────────────────────
// PostgreSQL connection pool for storing conversation history.
// Lazy-initialized so the server starts even without a DB.
/** Lazy-initialize the PostgreSQL connection pool. Non-blocking — server works without a database. */
let pgPool = null
async function initPg() {
if (!DATABASE_URL) return
@@ -255,11 +261,13 @@ async function initPg() {
// ── Request router ─────────────────────────────────────────────
const loadedJobs = loadJobs()
/** Write a JSON response with the given HTTP status code. */
function sendJSON(res, status, data) {
res.writeHead(status, { "Content-Type": "application/json" })
res.end(JSON.stringify(data))
}
/** Parse the JSON body of an incoming HTTP request. */
function parseBody(req) {
return new Promise((resolve, reject) => {
let body = ""
@@ -275,6 +283,7 @@ function parseBody(req) {
})
}
/** Parse a request URL into its pathname and search params. Falls back to localhost if no Host header is present. */
function parseURL(req) {
const url = new URL(req.url, `http://${req.headers.host || "localhost"}`)
return { pathname: url.pathname, searchParams: url.searchParams }