mcmeszi commited on
Commit
d95e11b
·
verified ·
1 Parent(s): a7f26d5

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +88 -64
server.js CHANGED
@@ -39,120 +39,144 @@ function toBookId(name) {
39
 
40
  /* ---------- Simple token-bucket rate-limiter for Bible-API (15 req / 30 s) ---------- */
41
  let tokens = 15;
42
- setInterval(() => {
43
- tokens = 15;
44
- }, 30_000); // reset every 30 s
45
-
46
  async function limitedFetch(url, opts) {
47
  while (tokens === 0) await delay(200);
48
  tokens--;
49
-
50
  const r = await fetch(url, opts);
51
-
52
  if (r.status === 429) {
53
  const retry = parseInt(r.headers.get("Retry-After") || "5", 10) * 1000;
54
- console.warn("429 kaptunk, várok", retry, "ms-t és újrapróbálom →", url);
55
  await delay(retry);
56
- return limitedFetch(url, opts); // single retry
57
  }
58
-
59
  return r;
60
  }
61
 
62
- /* ---------- In-memory verse cache (5 perc) ---------- */
63
- const verseCache = new Map();
64
- function cacheGet(key) {
65
- const v = verseCache.get(key);
66
- return v && v.exp > Date.now() ? v.text : undefined;
 
67
  }
68
- function cacheSet(key, text, ttl = 300_000) {
69
- verseCache.set(key, { text, exp: Date.now() + ttl });
70
  }
71
 
72
  /* ---------- Fetch English KJV verse ---------- */
73
  async function fetchEnglishVerse(book, ch, v) {
74
  const ref = `${book} ${ch}:${v}`;
75
- const cached = cacheGet(ref);
76
  if (cached) return cached;
77
-
78
- const url = "https://bible-api.com/" + encodeURIComponent(ref) + "?translation=kjv";
79
- console.log("Vers lekérése ezzel az URL-lel:", url);
80
-
81
  const r = await limitedFetch(url);
82
- if (!r.ok) {
83
- console.error("Bible API hiba státuszkód:", r.status, r.statusText);
84
- throw new Error("bible-api hiba");
85
- }
86
  const j = await r.json();
87
  const text = j.text.trim();
88
- cacheSet(ref, text);
89
  return text;
90
  }
91
 
92
- /* ---------- /api/analyze ---------- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  app.post("/api/analyze", async (req, res) => {
94
  const { book, chapter, verse } = req.body || {};
95
- console.log("Bemenő /api/analyze kérés:", { book, chapter, verse });
96
-
97
  try {
98
  const engVerse = await fetchEnglishVerse(book, chapter, verse);
99
- const ref = `${book} ${chapter}:${verse}`;
100
-
101
  const aiRes = await fetch("https://api.openai.com/v1/chat/completions", {
102
  method: "POST",
103
  headers: { "Content-Type": "application/json", Authorization: "Bearer " + process.env.OPENAI_KEY },
104
  body: JSON.stringify({
105
  model: "gpt-4o-mini",
106
  messages: [
107
- {
108
- role: "system",
109
- content: "Fordítsd magyarra a megadott Biblia-verset, majd első szám első személyben, legfeljebb két rövid bekezdésben magyarázd el, hogyan kapcsolódik a mai mesterséges intelligencia dilemmáihoz."
110
- },
111
- { role: "user", content: `Verse (KJV): \"${engVerse}\" — ${ref}` }
112
  ],
113
  temperature: 0.9,
114
  max_tokens: 300
115
  })
116
  });
117
-
118
  if (!aiRes.ok) throw new Error("OpenAI API hiba");
119
  const data = await aiRes.json();
120
  res.json({ output: data.choices[0].message.content.trim() });
121
-
122
  } catch (err) {
123
  console.error(err);
124
- res.status(500).json({ error: err.message || "Ismeretlen hiba" });
125
  }
126
  });
127
 
128
- /* ---------- /api/meta – 1 fetch a teljes könyvre ---------- */
129
  app.get("/api/meta", async (req, res) => {
130
- const book = req.query.book;
131
  if (!book) return res.status(400).json({ error: "Missing book" });
132
-
133
  try {
134
- const bookId = toBookId(book);
135
- const url = `https://bible-api.com/data/kjv/${bookId}`;
136
- console.log("Meta lekérése ezzel az URL-lel:", url);
137
-
138
- const r = await limitedFetch(url);
139
- if (!r.ok) throw new Error("meta fetch error");
140
- const j = await r.json();
141
-
142
- // normalize chapters structure to [verseCountPerChapter]
143
- let chapters = [];
144
- if (Array.isArray(j.chapters)) {
145
- chapters = j.chapters.map(c => parseInt(c.verses_count ?? c.verse_count ?? c.verses ?? 0, 10));
146
- } else if (j.chapters && typeof j.chapters === "object") {
147
- chapters = Object.keys(j.chapters)
148
- .sort((a, b) => a - b)
149
- .map(k => parseInt(j.chapters[k].verses_count ?? j.chapters[k].verse_count ?? j.chapters[k].verses ?? 0, 10));
150
- }
151
-
152
- if (!chapters.length || chapters.some(n => isNaN(n) || n === 0)) {
153
- throw new Error("Üres vagy hibás chapters adat");
154
- }
155
-
156
  res.json({ chapters });
157
  } catch (err) {
158
  console.error(err);
 
39
 
40
  /* ---------- Simple token-bucket rate-limiter for Bible-API (15 req / 30 s) ---------- */
41
  let tokens = 15;
42
+ setInterval(() => (tokens = 15), 30_000);
 
 
 
43
  async function limitedFetch(url, opts) {
44
  while (tokens === 0) await delay(200);
45
  tokens--;
 
46
  const r = await fetch(url, opts);
 
47
  if (r.status === 429) {
48
  const retry = parseInt(r.headers.get("Retry-After") || "5", 10) * 1000;
 
49
  await delay(retry);
50
+ return limitedFetch(url, opts);
51
  }
 
52
  return r;
53
  }
54
 
55
+ /* ---------- In-memory verse & meta cache ---------- */
56
+ const verseCache = new Map(); // key → {text, exp}
57
+ const metaCache = new Map(); // book → {chapters:[n], exp}
58
+ function cacheGet(map, key) {
59
+ const v = map.get(key);
60
+ return v && v.exp > Date.now() ? v.data : undefined;
61
  }
62
+ function cacheSet(map, key, data, ttl = 300_000) {
63
+ map.set(key, { data, exp: Date.now() + ttl });
64
  }
65
 
66
  /* ---------- Fetch English KJV verse ---------- */
67
  async function fetchEnglishVerse(book, ch, v) {
68
  const ref = `${book} ${ch}:${v}`;
69
+ const cached = cacheGet(verseCache, ref);
70
  if (cached) return cached;
71
+ const url = `https://bible-api.com/${encodeURIComponent(ref)}?translation=kjv`;
 
 
 
72
  const r = await limitedFetch(url);
73
+ if (!r.ok) throw new Error(`bible-api verse error ${r.status}`);
 
 
 
74
  const j = await r.json();
75
  const text = j.text.trim();
76
+ cacheSet(verseCache, ref, text);
77
  return text;
78
  }
79
 
80
+ /* ---------- Robust /api/meta ---------- */
81
+ async function fetchMetaChapters(book) {
82
+ const cached = cacheGet(metaCache, book);
83
+ if (cached) return cached;
84
+
85
+ const bookId = toBookId(book);
86
+ const url = `https://bible-api.com/data/kjv/${bookId}`;
87
+ let chapters = [];
88
+ try {
89
+ const r = await limitedFetch(url);
90
+ if (r.ok) {
91
+ const j = await r.json();
92
+ if (Array.isArray(j.chapters)) {
93
+ chapters = j.chapters.map(c => parseInt(c.verses_count ?? c.verse_count ?? (c.verse_ids?.length ?? 0), 10));
94
+ } else if (j.chapters && typeof j.chapters === "object") {
95
+ chapters = Object.keys(j.chapters).sort((a, b) => a - b).map(k => {
96
+ const obj = j.chapters[k];
97
+ return parseInt(obj.verses_count ?? obj.verse_count ?? (obj.verse_ids?.length ?? 0), 10);
98
+ });
99
+ } else if (Array.isArray(j.chapter_ids)) {
100
+ // we know count of chapters but not verses, will fetch first+last verse per chapter below
101
+ chapters = await Promise.all(
102
+ j.chapter_ids.map(async id => {
103
+ const parts = id.split(".");
104
+ const chNum = parts[1] || id; // fallback
105
+ return await fetchChapterVerseCount(bookId, chNum);
106
+ })
107
+ );
108
+ }
109
+ }
110
+ } catch (e) {
111
+ console.error("/data API parse fail, falling back", e);
112
+ }
113
+
114
+ // fallback if still empty → step through chapters until 404
115
+ if (!chapters.length) {
116
+ chapters = await bruteForceChapters(book);
117
+ }
118
+
119
+ if (!chapters.length) throw new Error("meta fetch failed");
120
+ cacheSet(metaCache, book, chapters, 3600_000); // 1 óra cache
121
+ return chapters;
122
+ }
123
+
124
+ async function fetchChapterVerseCount(bookId, ch) {
125
+ const url = `https://bible-api.com/data/kjv/${bookId}/${ch}`;
126
+ const r = await limitedFetch(url);
127
+ if (!r.ok) return 0;
128
+ const j = await r.json();
129
+ if (Array.isArray(j.verses)) return j.verses.length;
130
+ if (Array.isArray(j.verse_ids)) return j.verse_ids.length;
131
+ if (j.verses_count || j.verse_count) return parseInt(j.verses_count ?? j.verse_count, 10);
132
+ return 0;
133
+ }
134
+
135
+ async function bruteForceChapters(book) {
136
+ const chapters = [];
137
+ for (let ch = 1; ch <= 200; ch++) {
138
+ const url = `https://bible-api.com/${encodeURIComponent(book + " " + ch)}?translation=kjv`;
139
+ const r = await limitedFetch(url);
140
+ if (!r.ok) break; // 404 signals end
141
+ const j = await r.json();
142
+ const versesInChapter = j.verses[j.verses.length - 1].verse;
143
+ chapters.push(versesInChapter);
144
+ }
145
+ return chapters;
146
+ }
147
+
148
+ /* ---------- Routes ---------- */
149
  app.post("/api/analyze", async (req, res) => {
150
  const { book, chapter, verse } = req.body || {};
 
 
151
  try {
152
  const engVerse = await fetchEnglishVerse(book, chapter, verse);
 
 
153
  const aiRes = await fetch("https://api.openai.com/v1/chat/completions", {
154
  method: "POST",
155
  headers: { "Content-Type": "application/json", Authorization: "Bearer " + process.env.OPENAI_KEY },
156
  body: JSON.stringify({
157
  model: "gpt-4o-mini",
158
  messages: [
159
+ { role: "system", content: "Fordítsd magyarra a megadott Biblia-verset, majd első szám első személyben, legfeljebb két rövid bekezdésben magyarázd el, hogyan kapcsolódik a mai mesterséges intelligencia dilemmáihoz." },
160
+ { role: "user", content: `Verse (KJV): \"${engVerse}\" — ${book} ${chapter}:${verse}` }
 
 
 
161
  ],
162
  temperature: 0.9,
163
  max_tokens: 300
164
  })
165
  });
 
166
  if (!aiRes.ok) throw new Error("OpenAI API hiba");
167
  const data = await aiRes.json();
168
  res.json({ output: data.choices[0].message.content.trim() });
 
169
  } catch (err) {
170
  console.error(err);
171
+ res.status(500).json({ error: err.message });
172
  }
173
  });
174
 
 
175
  app.get("/api/meta", async (req, res) => {
176
+ const { book } = req.query;
177
  if (!book) return res.status(400).json({ error: "Missing book" });
 
178
  try {
179
+ const chapters = await fetchMetaChapters(book);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  res.json({ chapters });
181
  } catch (err) {
182
  console.error(err);