// 宇森游戏 · 前端与 Odoo 通信层 // 本地调试:前端 8888 -> Odoo 8069(跨域,带调试 CORS 头) // 生产部署:前端与 Odoo 同域,由 nginx 代理 /game/*。 // 库名说明:本地 Odoo 用 odoo.conf 钉死 yt_game;线上游戏库名为 game(dbfilter 不含 yt_game)。 // game/* 走统一 API_CONFIG.db;yt_world/* 单独带 ?db=,按 WORLD_DB 区分本地/线上,避免 git 同步互相覆盖。 const API_CONFIG = { base: (location.hostname === '127.0.0.1' && location.port === '8888') ? 'http://127.0.0.1:8069' : (location.hostname === 'localhost' && location.port === '8888') ? 'http://127.0.0.1:8069' : '', db: (location.hostname === '127.0.0.1' && location.port === '8888') ? 'study' : 'game', }; // 世界(yt_world)所在库:本地 Odoo 钉 yt_game;线上游戏库名为 game。按域名自适应,git 同步不冲突。 const WORLD_DB = (location.hostname === '127.0.0.1' || location.hostname === 'localhost') ? 'yt_game' : 'game'; const _cred = API_CONFIG.base ? 'include' : 'same-origin'; function _url(path) { return `${API_CONFIG.base}${path}?db=${API_CONFIG.db}`; } const API = { async me() { return (await fetch(_url('/game/api/me'), { credentials: _cred })).json(); }, async login(login, password) { return (await fetch(_url('/game/api/login'), { method: 'POST', credentials: _cred, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ login, password }), })).json(); }, async register(payload) { return (await fetch(_url('/game/api/register'), { method: 'POST', credentials: _cred, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), })).json(); }, async logout() { return (await fetch(_url('/game/api/logout'), { method: 'POST', credentials: _cred, })).json(); }, async codex() { return (await fetch(_url('/game/api/codex'), { credentials: _cred })).json(); }, async rules() { return (await fetch(_url('/game/api/rules'), { credentials: _cred })).json(); }, async currencies() { return (await fetch(_url('/game/api/currencies'), { credentials: _cred })).json(); }, async pages() { return (await fetch(_url('/game/api/pages'), { credentials: _cred })).json(); }, async threads() { return (await fetch(_url('/game/api/threads'), { credentials: _cred })).json(); }, async announcements() { return (await fetch(_url('/game/api/announcements'), { credentials: _cred })).json(); }, async story() { return (await fetch(_url('/game/api/story'), { credentials: _cred })).json(); }, async storyChapter(cid) { return (await fetch(_url('/game/api/story/chapter/' + cid), { credentials: _cred })).json(); }, // ---------- 沙盘记忆(yt_world 模块) ---------- async world() { return (await fetch('/yt_world/api/world?db=' + WORLD_DB, { credentials: 'same-origin' })).json(); }, async saveTiles(tiles) { return (await fetch('/yt_world/api/tiles/save?db=' + WORLD_DB, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tiles }), })).json(); }, async saveSettings(settings) { return (await fetch('/yt_world/api/settings?db=' + WORLD_DB, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ settings }), })).json(); }, // ---------- 世界数据 / 包裹(后端接口预留:yt_world 暂未实现路由,前端 mock 兜底) ---------- async worldData() { try { const r = await fetch('/yt_world/api/data?db=' + WORLD_DB, { credentials: 'same-origin' }); if (!r.ok) return null; return await r.json(); } catch (e) { return null; } }, async inventory() { try { const r = await fetch('/yt_world/api/inventory?db=' + WORLD_DB, { credentials: 'same-origin' }); if (!r.ok) return null; return await r.json(); } catch (e) { return null; } }, };