mcmeszi commited on
Commit
6fcd392
·
verified ·
1 Parent(s): abd0a51

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +100 -43
server.js CHANGED
@@ -20,10 +20,9 @@ app.use(express.static("public"));
20
  /* ------------------------------------------------------------------
21
  1) Helyi KJV betöltése és NORMALIZÁLÁSA
22
  ------------------------------------------------------------------ */
23
- let LOCAL_BIBLE = null; // { Book: [ [v1,v2,…], … ] }
24
 
25
  function toVerseArray(flatArr) {
26
- // flat [{book,chapter,verse,text}] -> {Book:[[v1,v2,…],[…]]}
27
  const out = {};
28
  for (const v of flatArr) {
29
  const b = v.book;
@@ -37,15 +36,13 @@ function toVerseArray(flatArr) {
37
  }
38
 
39
  function normaliseBook(raw) {
40
- // already [[…]]
41
- if (Array.isArray(raw) && Array.isArray(raw[0])) return raw;
42
- // aruljohn type {chapters:[{verses:[…]}]}
43
  if (raw?.chapters) {
44
  return raw.chapters.map(ch => Array.isArray(ch) ? ch : ch.verses?.map(v => (v.text ?? v).trim()));
45
  }
46
- // thiagobodruk type {verses:[…]}
47
  if (raw?.verses) {
48
- return toVerseArray(raw.verses)[Object.keys(toVerseArray(raw.verses))[0]]; // returns array of chapters
 
49
  }
50
  return null;
51
  }
@@ -54,15 +51,12 @@ try {
54
  if (fs.existsSync("./kjv.json")) {
55
  console.log("📖 Betöltöm a helyi kjv.json fájlt…");
56
  let txt = fs.readFileSync("./kjv.json", "utf8");
57
- txt = txt.replace(/^[\uFEFF\u200B]+/, ""); // BOM vagy ZERO WIDTH SPACE eltávolítása
58
  const parsed = JSON.parse(txt);
59
 
60
- let structured;
61
  if (Array.isArray(parsed) && parsed[0]?.book) {
62
- // komplett flat lista
63
  structured = toVerseArray(parsed);
64
- } else {
65
- structured = parsed;
66
  }
67
 
68
  const normalised = {};
@@ -80,10 +74,10 @@ try {
80
  }
81
 
82
  /* ------------------------------------------------------------------
83
- 2) Book név → ID (Bible‑API /data)
84
  ------------------------------------------------------------------ */
85
- const BOOK_IDS = { Genesis:"GEN", Exodus:"EXO", Leviticus:"LEV", Numbers:"NUM", Deuteronomy:"DEU", Joshua:"JOS", Judges:"JDG", Ruth:"RUT", "1 Samuel":"1SA", "2 Samuel":"2SA", "1 Kings":"1KI", "2 Kings":"2KI", "1 Chronicles":"1CH", "2 Chronicles":"2CH", Ezra:"EZR", Nehemiah:"NEH", Esther:"EST", Job:"JOB", Psalms:"PSA", Proverbs:"PRO", Ecclesiastes:"ECC", "Song of Solomon":"SNG", Isaiah:"ISA", Jeremiah:"JER", Lamentations:"LAM", Ezekiel:"EZK", Daniel:"DAN", Hosea:"HOS", Joel:"JOL", Amos:"AMO", Obadiah:"OBA", Jonah:"JON", Micah:"MIC", Nahum:"NAM", Habakkuk:"HAB", Zephaniah:"ZEP", Haggai:"HAG", Zechariah:"ZEC", Malachi:"MAL", Matthew:"MAT", Mark:"MRK", Luke:"LUK", John:"JHN", Acts:"ACT", Romans:"ROM", "1 Corinthians":"1CO", "2 Corinthians":"2CO", Galatians:"GAL", Ephesians:"EPH", Philippians:"PHP", Colossians:"COL", "1 Thessalonians":"1TH", "2 Thessalonians":"2TH", "1 Timothy":"1TI", "2 Timothy":"2TI", Titus:"TIT", Philemon:"PHM", Hebrews:"HEB", James:"JAS", "1 Peter":"1PE", "2 Peter":"2PE", "1 John":"1JN", "2 John":"2JN", "3 John":"3JN", Jude:"JUD", Revelation:"REV" };
86
- const toBookId = n => BOOK_IDS[n] || n.slice(0,3).toUpperCase();
87
 
88
  /* ------------------------------------------------------------------
89
  3) Rate‑limiter Bible‑API‑hoz (token‑bucket 15/30 s)
@@ -103,27 +97,25 @@ async function limitedFetch(url, opts) {
103
  }
104
 
105
  /* ------------------------------------------------------------------
106
- 4) Mem‑cache segédek
107
  ------------------------------------------------------------------ */
108
  const verseCache = new Map();
109
- const metaCache = new Map();
110
  const getCache = (map, k) => { const v = map.get(k); return v && v.exp > Date.now() ? v.data : undefined; };
111
- const setCache = (map, k, d, ttl=3_600_000) => map.set(k, { data:d, exp:Date.now()+ttl });
112
 
113
  /* ------------------------------------------------------------------
114
- 5) Local verse helpers
115
  ------------------------------------------------------------------ */
116
- const getLocalVerse = (b,c,v) => LOCAL_BIBLE?.[b]?.[c-1]?.[v-1] ?? null;
117
- const getLocalChapterCounts = b => LOCAL_BIBLE?.[b]?.map(ch=>ch.length) ?? null;
118
 
119
  async function fetchEnglishVerse(book, ch, v) {
120
  const ref = `${book} ${ch}:${v}`;
121
  const cached = getCache(verseCache, ref);
122
  if (cached) return cached;
123
-
124
  const local = getLocalVerse(book, ch, v);
125
  if (local) { setCache(verseCache, ref, local); return local; }
126
-
127
  const url = `https://bible-api.com/${encodeURIComponent(ref)}?translation=kjv`;
128
  const r = await limitedFetch(url);
129
  if (!r.ok) throw new Error(`Bible-API verse error ${r.status}`);
@@ -137,28 +129,93 @@ async function fetchEnglishVerse(book, ch, v) {
137
  6) Meta fetch
138
  ------------------------------------------------------------------ */
139
  async function bruteForceChapters(book) {
140
- const counts=[];
141
- for(let ch=1; ch<=200; ch++){
142
- const url=`https://bible-api.com/${encodeURIComponent(book+" "+ch)}?translation=kjv`;
143
- const r=await limitedFetch(url);
144
- if(!r.ok) break;
145
- const j=await r.json();
146
  counts.push(j.verses.at(-1).verse);
147
  }
148
  return counts;
149
  }
150
 
151
- async function fetchMetaChapters(book){
152
- const c=getCache(metaCache,book); if(c) return c;
153
- const loc=getLocalChapterCounts(book); if(loc?.length){setCache(metaCache,book,loc);return loc;}
154
- try{
155
- const url=`https://bible-api.com/data/kjv/${toBookId(book)}`;
156
- const r=await limitedFetch(url);
157
- if(r.ok){
158
- const j=await r.json();
159
- let ch=[];
160
- if(Array.isArray(j.chapters)) ch=j.chapters.map(c=>parseInt(c.verses_count??c.verse_count??(Array.isArray(c.verses)?c.verses.length:0)??(c.verse_ids?.length??0),10));
161
- else if(j.chapters){
162
- ch=Object.keys(j.chapters).sort((a,b)=>a-b).map(k=>{
163
- const o=j.chapters[k];
164
- return parseInt(o.verses_count??o.verse
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  /* ------------------------------------------------------------------
21
  1) Helyi KJV betöltése és NORMALIZÁLÁSA
22
  ------------------------------------------------------------------ */
23
+ let LOCAL_BIBLE = null; // { Book: [ [v1,v2,…], … ] }
24
 
25
  function toVerseArray(flatArr) {
 
26
  const out = {};
27
  for (const v of flatArr) {
28
  const b = v.book;
 
36
  }
37
 
38
  function normaliseBook(raw) {
39
+ if (Array.isArray(raw) && Array.isArray(raw[0])) return raw; // already [[…]]
 
 
40
  if (raw?.chapters) {
41
  return raw.chapters.map(ch => Array.isArray(ch) ? ch : ch.verses?.map(v => (v.text ?? v).trim()));
42
  }
 
43
  if (raw?.verses) {
44
+ const tmp = toVerseArray(raw.verses);
45
+ return tmp[Object.keys(tmp)[0]]; // array of chapters for this single book
46
  }
47
  return null;
48
  }
 
51
  if (fs.existsSync("./kjv.json")) {
52
  console.log("📖 Betöltöm a helyi kjv.json fájlt…");
53
  let txt = fs.readFileSync("./kjv.json", "utf8");
54
+ txt = txt.replace(/^[\uFEFF\u200B]+/, ""); // BOM/ZWSP strip
55
  const parsed = JSON.parse(txt);
56
 
57
+ let structured = parsed;
58
  if (Array.isArray(parsed) && parsed[0]?.book) {
 
59
  structured = toVerseArray(parsed);
 
 
60
  }
61
 
62
  const normalised = {};
 
74
  }
75
 
76
  /* ------------------------------------------------------------------
77
+ 2) Book → ID (Bible‑API /data)
78
  ------------------------------------------------------------------ */
79
+ const BOOK_IDS = { Genesis: "GEN", Exodus: "EXO", Leviticus: "LEV", Numbers: "NUM", Deuteronomy: "DEU", Joshua: "JOS", Judges: "JDG", Ruth: "RUT", "1 Samuel": "1SA", "2 Samuel": "2SA", "1 Kings": "1KI", "2 Kings": "2KI", "1 Chronicles": "1CH", "2 Chronicles": "2CH", Ezra: "EZR", Nehemiah: "NEH", Esther: "EST", Job: "JOB", Psalms: "PSA", Proverbs: "PRO", Ecclesiastes: "ECC", "Song of Solomon": "SNG", Isaiah: "ISA", Jeremiah: "JER", Lamentations: "LAM", Ezekiel: "EZK", Daniel: "DAN", Hosea: "HOS", Joel: "JOL", Amos: "AMO", Obadiah: "OBA", Jonah: "JON", Micah: "MIC", Nahum: "NAM", Habakkuk: "HAB", Zephaniah: "ZEP", Haggai: "HAG", Zechariah: "ZEC", Malachi: "MAL", Matthew: "MAT", Mark: "MRK", Luke: "LUK", John: "JHN", Acts: "ACT", Romans: "ROM", "1 Corinthians": "1CO", "2 Corinthians": "2CO", Galatians: "GAL", Ephesians: "EPH", Philippians: "PHP", Colossians: "COL", "1 Thessalonians": "1TH", "2 Thessalonians": "2TH", "1 Timothy": "1TI", "2 Timothy": "2TI", Titus: "TIT", Philemon: "PHM", Hebrews: "HEB", James: "JAS", "1 Peter": "1PE", "2 Peter": "2PE", "1 John": "1JN", "2 John": "2JN", "3 John": "3JN", Jude: "JUD", Revelation: "REV" };
80
+ const toBookId = name => BOOK_IDS[name] || name.slice(0, 3).toUpperCase();
81
 
82
  /* ------------------------------------------------------------------
83
  3) Rate‑limiter Bible‑API‑hoz (token‑bucket 15/30 s)
 
97
  }
98
 
99
  /* ------------------------------------------------------------------
100
+ 4) Cache helpers
101
  ------------------------------------------------------------------ */
102
  const verseCache = new Map();
103
+ const metaCache = new Map();
104
  const getCache = (map, k) => { const v = map.get(k); return v && v.exp > Date.now() ? v.data : undefined; };
105
+ const setCache = (map, k, data, ttl = 3_600_000) => map.set(k, { data, exp: Date.now() + ttl });
106
 
107
  /* ------------------------------------------------------------------
108
+ 5) Local helpers
109
  ------------------------------------------------------------------ */
110
+ const getLocalVerse = (b, c, v) => LOCAL_BIBLE?.[b]?.[c - 1]?.[v - 1] ?? null;
111
+ const getLocalChapterCounts = b => LOCAL_BIBLE?.[b]?.map(ch => ch.length) ?? null;
112
 
113
  async function fetchEnglishVerse(book, ch, v) {
114
  const ref = `${book} ${ch}:${v}`;
115
  const cached = getCache(verseCache, ref);
116
  if (cached) return cached;
 
117
  const local = getLocalVerse(book, ch, v);
118
  if (local) { setCache(verseCache, ref, local); return local; }
 
119
  const url = `https://bible-api.com/${encodeURIComponent(ref)}?translation=kjv`;
120
  const r = await limitedFetch(url);
121
  if (!r.ok) throw new Error(`Bible-API verse error ${r.status}`);
 
129
  6) Meta fetch
130
  ------------------------------------------------------------------ */
131
  async function bruteForceChapters(book) {
132
+ const counts = [];
133
+ for (let ch = 1; ch <= 200; ch++) {
134
+ const url = `https://bible-api.com/${encodeURIComponent(book + " " + ch)}?translation=kjv`;
135
+ const r = await limitedFetch(url);
136
+ if (!r.ok) break;
137
+ const j = await r.json();
138
  counts.push(j.verses.at(-1).verse);
139
  }
140
  return counts;
141
  }
142
 
143
+ async function fetchMetaChapters(book) {
144
+ const cached = getCache(metaCache, book);
145
+ if (cached) return cached;
146
+ const local = getLocalChapterCounts(book);
147
+ if (local?.length) { setCache(metaCache, book, local); return local; }
148
+ // Try /data endpoint
149
+ try {
150
+ const url = `https://bible-api.com/data/kjv/${toBookId(book)}`;
151
+ const r = await limitedFetch(url);
152
+ if (r.ok) {
153
+ const j = await r.json();
154
+ let chapters = [];
155
+ if (Array.isArray(j.chapters)) {
156
+ chapters = j.chapters.map(c => parseInt(
157
+ c.verses_count ?? c.verse_count ?? (Array.isArray(c.verses) ? c.verses.length : 0) ?? (c.verse_ids?.length ?? 0),
158
+ 10));
159
+ } else if (j.chapters && typeof j.chapters === "object") {
160
+ chapters = Object.keys(j.chapters).sort((a, b) => a - b).map(k => {
161
+ const o = j.chapters[k];
162
+ return parseInt(
163
+ o.verses_count ?? o.verse_count ?? (Array.isArray(o.verses) ? o.verses.length : 0) ?? (o.verse_ids?.length ?? 0),
164
+ 10);
165
+ });
166
+ }
167
+ if (chapters.some(n => n > 1)) { setCache(metaCache, book, chapters); return chapters; }
168
+ }
169
+ } catch (e) {
170
+ console.warn("/data endpoint fail", e.message);
171
+ }
172
+ const brute = await bruteForceChapters(book);
173
+ if (brute.length) { setCache(metaCache, book, brute); return brute; }
174
+ throw new Error("Meta fetch failed for " + book);
175
+ }
176
+
177
+ /* ------------------------------------------------------------------
178
+ 7) Routes
179
+ ------------------------------------------------------------------ */
180
+ app.post("/api/analyze", async (req, res) => {
181
+ const { book, chapter, verse } = req.body || {};
182
+ try {
183
+ const engVerse = await fetchEnglishVerse(book, +chapter, +verse);
184
+ const openAIRes = await fetch("https://api.openai.com/v1/chat/completions", {
185
+ method: "POST",
186
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.OPENAI_KEY}` },
187
+ body: JSON.stringify({
188
+ model: "gpt-4o-mini",
189
+ messages: [
190
+ {
191
+ role: "system",
192
+ 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."
193
+ },
194
+ { role: "user", content: `Verse (KJV): \"${engVerse}\" — ${book} ${chapter}:${verse}` }
195
+ ],
196
+ temperature: 0.9,
197
+ max_tokens: 300
198
+ })
199
+ });
200
+ if (!openAIRes.ok) throw new Error("OpenAI API hiba");
201
+ const data = await openAIRes.json();
202
+ res.json({ output: data.choices[0].message.content.trim() });
203
+ } catch (err) {
204
+ console.error(err);
205
+ res.status(500).json({ error: err.message });
206
+ }
207
+ });
208
+
209
+ app.get("/api/meta", async (req, res) => {
210
+ const { book } = req.query;
211
+ if (!book) return res.status(400).json({ error: "Missing book" });
212
+ try {
213
+ const chapters = await fetchMetaChapters(book);
214
+ res.json({ chapters });
215
+ } catch (err) {
216
+ console.error(err);
217
+ res.status(500).json({ error: err.message });
218
+ }
219
+ });
220
+
221
+ app.listen(PORT, () => console.log("🚀 Fut a", PORT, "porton"));