Skip to content

Commit 8c242f9

Browse files
authored
Merge pull request #34 from Datakult0r/devin/1769830272-saas-production-ready
feat: SaaS Production Ready - Version/Metrics Endpoints & Documentation Updates
2 parents 2d8ff5d + d4f5194 commit 8c242f9

7 files changed

Lines changed: 493 additions & 26 deletions

File tree

backend/src/config/supabase.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Re-export supabase client from database.js for backward compatibility
2+
export { supabase } from './database.js';

backend/src/server.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,42 @@ app.get("/health", (req, res) => {
5555
});
5656
});
5757

58+
// Version endpoint
59+
app.get("/version", (req, res) => {
60+
res.json({
61+
version: "1.0.0",
62+
name: "ai-grant-crawler-backend",
63+
buildTime: process.env.BUILD_TIME || new Date().toISOString(),
64+
nodeVersion: process.version,
65+
environment: env.nodeEnv,
66+
});
67+
});
68+
69+
// Metrics endpoint (basic)
70+
const startTime = Date.now();
71+
let requestCount = 0;
72+
app.use((req, res, next) => {
73+
requestCount++;
74+
next();
75+
});
76+
77+
app.get("/metrics", (req, res) => {
78+
const uptime = Math.floor((Date.now() - startTime) / 1000);
79+
const memoryUsage = process.memoryUsage();
80+
81+
res.json({
82+
uptime: uptime,
83+
uptimeFormatted: `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m ${uptime % 60}s`,
84+
requestCount: requestCount,
85+
memory: {
86+
heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024) + " MB",
87+
heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024) + " MB",
88+
rss: Math.round(memoryUsage.rss / 1024 / 1024) + " MB",
89+
},
90+
environment: env.nodeEnv,
91+
});
92+
});
93+
5894
// API routes
5995
app.use("/api/grants", grantsRouter);
6096
app.use("/api/crawler", crawlerRouter);

backend/src/services/emailService.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,19 @@ export async function sendEmail(to, subject, html) {
229229
return { success: false, error: err.message };
230230
}
231231
}
232+
233+
// Wrapper functions for notification scheduler
234+
export async function notifyDeadlineApproaching(email, grant, daysRemaining, unsubscribeToken = 'default') {
235+
const { subject, html } = createDeadlineAlertEmail(grant, daysRemaining, unsubscribeToken);
236+
return sendEmail(email, subject, html);
237+
}
238+
239+
export async function notifyNewHighRelevanceGrant(email, grant, unsubscribeToken = 'default') {
240+
const { subject, html } = createNewGrantAlertEmail([grant], unsubscribeToken);
241+
return sendEmail(email, subject, html);
242+
}
243+
244+
export async function sendWeeklyDigest(email, grants, proposals, stats, unsubscribeToken = 'default') {
245+
const { subject, html } = createWeeklyDigestEmail(stats, grants.slice(0, 5), unsubscribeToken);
246+
return sendEmail(email, subject, html);
247+
}

backend/src/services/firecrawl.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,30 @@ dotenv.config();
44

55
const apiKey = process.env.FIRECRAWL_API_KEY;
66
if (!apiKey) {
7-
console.warn("⚠️ FIRECRAWL_API_KEY missing in .env");
7+
console.warn("⚠️ FIRECRAWL_API_KEY missing in .env - Firecrawl features will be disabled");
88
}
99

10-
const firecrawl = new FirecrawlApp({ apiKey: apiKey });
10+
// Only initialize Firecrawl if API key is available
11+
const firecrawl = apiKey ? new FirecrawlApp({ apiKey: apiKey }) : null;
1112

1213
export class FirecrawlService {
14+
/**
15+
* Check if Firecrawl is available
16+
*/
17+
static isAvailable() {
18+
return firecrawl !== null;
19+
}
20+
1321
/**
1422
* Scrape a single grant page to get its content.
1523
* @param {string} url
1624
* @returns {Promise<Object>} Formatted markdown and metadata
1725
*/
1826
static async scrapeGrantPage(url) {
27+
if (!firecrawl) {
28+
console.warn('[FIRECRAWL] Service not available - API key missing');
29+
return { success: false, error: 'Firecrawl API key not configured' };
30+
}
1931
try {
2032
const scrapeResult = await firecrawl.scrapeUrl(url, {
2133
formats: ["markdown"],
@@ -34,6 +46,10 @@ export class FirecrawlService {
3446
* @returns {Promise<Array>} List of discovered URLs
3547
*/
3648
static async crawlGrantSite(url, limit = 10) {
49+
if (!firecrawl) {
50+
console.warn('[FIRECRAWL] Service not available - API key missing');
51+
return { success: false, error: 'Firecrawl API key not configured' };
52+
}
3753
try {
3854
const crawlResponse = await firecrawl.crawlUrl(url, {
3955
limit: limit,
@@ -62,6 +78,10 @@ export class FirecrawlService {
6278
* @returns {Promise<Object>} Search results
6379
*/
6480
static async searchGrantContext(query) {
81+
if (!firecrawl) {
82+
console.warn('[FIRECRAWL] Service not available - API key missing');
83+
return { success: false, error: 'Firecrawl API key not configured' };
84+
}
6585
try {
6686
const params = {
6787
pageOptions: {

docs/DEPLOYMENT.md

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ This guide covers deploying the AI Grant Crawler application to production.
77
The application consists of two main components:
88

99
1. **Frontend** (SvelteKit) - Deployed to Vercel (automatic via GitHub integration)
10-
2. **Backend** (Node.js/Express) - Deployed to Fly.io (manual setup required)
10+
2. **Backend** (Node.js/Express) - Deployed to Railway or Fly.io
1111

1212
## Prerequisites
1313

14-
- [Fly.io CLI](https://fly.io/docs/hands-on/install-flyctl/) installed
15-
- Fly.io account created
14+
- Railway account OR [Fly.io CLI](https://fly.io/docs/hands-on/install-flyctl/) installed
1615
- Supabase project with database schema applied
1716
- API keys for Gemini (required) and OpenRouter (optional)
1817

@@ -28,7 +27,46 @@ Set these in your Vercel project settings:
2827
PUBLIC_API_URL=https://your-backend.fly.dev/api
2928
```
3029

31-
## Backend Deployment (Fly.io)
30+
## Backend Deployment (Railway) - Recommended
31+
32+
Railway is the recommended deployment platform for the backend. A `railway.json` configuration file is already included.
33+
34+
### 1. Connect Repository
35+
36+
1. Go to [Railway Dashboard](https://railway.app/dashboard)
37+
2. Click "New Project" > "Deploy from GitHub repo"
38+
3. Select `Datakult0r/ai-grant-crawler-a2a-pro`
39+
4. Choose the `backend` directory as the root
40+
41+
### 2. Set Environment Variables
42+
43+
In Railway Dashboard > Project > Variables:
44+
45+
```
46+
# Required
47+
SUPABASE_URL=https://your-project.supabase.co
48+
SUPABASE_ANON_KEY=your-anon-key
49+
GEMINI_API_KEY=your-gemini-key
50+
PORT=3000
51+
52+
# Optional
53+
OPENROUTER_API_KEY=your-openrouter-key
54+
LOW_COST_MODE=true
55+
AI_RESEARCHER_ENABLED=false
56+
IS_DEMO=false
57+
```
58+
59+
### 3. Deploy
60+
61+
Railway will automatically deploy when you push to the connected branch. You can also trigger manual deploys from the dashboard.
62+
63+
### 4. Get Your Backend URL
64+
65+
After deployment, Railway provides a URL like `https://your-app.up.railway.app`. Use this for the frontend's `PUBLIC_API_URL`.
66+
67+
---
68+
69+
## Backend Deployment (Fly.io) - Alternative
3270

3371
### 1. Install Fly CLI
3472

@@ -130,9 +168,23 @@ Access your app's metrics and logs at: https://fly.io/apps/your-app-name
130168

131169
### Health Check
132170

133-
The backend exposes a `/health` endpoint that returns:
134-
- Database connectivity status
135-
- API key configuration status
171+
The backend exposes several monitoring endpoints:
172+
173+
**`/health`** - Basic health check
174+
- Returns: `{ status: "ok", timestamp, environment }`
175+
176+
**`/version`** - Application version info
177+
- Returns: `{ version, name, buildTime, nodeVersion, environment }`
178+
179+
**`/metrics`** - Runtime metrics
180+
- Returns: `{ uptime, uptimeFormatted, requestCount, memory: { heapUsed, heapTotal, rss }, environment }`
181+
182+
Example:
183+
```bash
184+
curl https://your-app.fly.dev/health
185+
curl https://your-app.fly.dev/version
186+
curl https://your-app.fly.dev/metrics
187+
```
136188

137189
## Troubleshooting
138190

0 commit comments

Comments
 (0)