-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
117 lines (94 loc) · 3.11 KB
/
server.js
File metadata and controls
117 lines (94 loc) · 3.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
const express = require("express");
const { runMoonGraph } = require("./graph/moonGraph");
const { postMoonSignAndPhase } = require("./bluesky/moon_bot");
const { processMentions } = require("./bluesky/mentions");
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
/**
* 🔎 Request logger (helps identify who/what is hitting endpoints)
*/
app.use((req, res, next) => {
const ip =
req.get("fly-client-ip") ||
(req.get("x-forwarded-for") || "").split(",")[0]?.trim() ||
req.ip;
console.log("REQ", req.method, req.originalUrl, {
ua: req.get("user-agent"),
ip,
});
next();
});
/**
* 🔐 Simple shared-secret protection for cron endpoints
* GitHub sends: Authorization: Bearer <CRON_SECRET>
*/
function requireCronSecret(req, res, next) {
const secret = (process.env.CRON_SECRET || "").trim();
if (!secret) {
console.warn("⚠️ CRON_SECRET is not set; /daily and /mentions are unprotected");
return next();
}
const auth = (req.get("authorization") || "").trim();
const expected = `Bearer ${secret}`;
// 🔍 Debug logging (safe)
console.log("AUTH HEADER PRESENT?", Boolean(auth));
console.log("AUTH HEADER LENGTH:", auth.length);
console.log("AUTH HEADER PREFIX:", auth.slice(0, 10));
console.log("EXPECTED HEADER LENGTH:", (`Bearer ${secret}`).length);
if (auth !== expected) {
console.warn("❌ Unauthorized request to cron endpoint");
return res.status(401).json({ ok: false, error: "unauthorized" });
}
return next();
}
app.get("/", (req, res) => {
res.send("🌙 Moon Cycle Bot is alive");
});
app.post("/interpret", async (req, res) => {
try {
const { risingSign } = req.body;
if (!risingSign) {
return res.status(400).json({
error: "Missing required field: risingSign",
});
}
console.log("rising sign", risingSign);
const result = await runMoonGraph({ risingSign });
console.log("results", result);
if (!result) {
return res.status(500).json({
error: "LangGraph returned no output",
});
}
return res.json({ message: result });
} catch (err) {
console.error("[/interpret] Error invoking moonGraph:", err);
return res.status(500).json({
error: "Internal server error during interpretation",
});
}
});
// 🔹 Daily posting route (protected)
app.post("/daily", requireCronSecret, async (req, res) => {
try {
await postMoonSignAndPhase();
return res.status(200).json({ message: "Daily moon post sent to Bluesky!" });
} catch (err) {
console.error("❌ [/daily] Error in daily post:", err);
return res.status(500).json({ error: "Failed to post daily moon update" });
}
});
// 🔹 Mentions processing route (protected)
app.post("/mentions", requireCronSecret, async (req, res) => {
try {
const out = await processMentions({ limit: 50 });
return res.status(200).json(out);
} catch (err) {
console.error("[/mentions] error", err);
return res.status(500).json({ ok: false, error: "failed to process mentions" });
}
});
app.listen(PORT, () => {
console.log(`🚀 Server listening on port ${PORT}`);
});