// Same-origin API wrapper — обычный скрипт, без ES modules (Safari iOS) // Используем только credentials:'include' + кэш отключен const DEFAULT_INIT = { credentials: 'include', cache: 'no-store', redirect: 'follow', }; async function api(path, options) { options = options || {}; const json = options.json; const extraHeaders = options.headers; const rest = options; delete rest.json; delete rest.headers; const headers = new Headers(extraHeaders || {}); let body = rest.body; if (json !== undefined) { headers.set('Content-Type', 'application/json'); body = JSON.stringify(json); } if (!headers.has('Accept')) { headers.set('Accept', 'application/json'); } const res = await fetch(path, { ...DEFAULT_INIT, ...rest, headers, body, }); const ct = res.headers.get('content-type') || ''; const isJson = ct.indexOf('application/json') >= 0; const data = isJson ? await res.json().catch(function () { return null; }) : await res.text(); if (!res.ok) { const err = new Error((data && (data.message || data.error)) || ('HTTP ' + res.status)); err.status = res.status; err.data = data; throw err; } return data; } const apiGet = function (path, opts) { return api(path, Object.assign({}, opts, { method: 'GET' })); }; const apiPost = function (path, json, opts) { return api(path, Object.assign({}, opts, { method: 'POST', json: json })); }; const apiPut = function (path, json, opts) { return api(path, Object.assign({}, opts, { method: 'PUT', json: json })); }; const apiDelete = function (path, opts) { return api(path, Object.assign({}, opts, { method: 'DELETE' })); }; const Store = { access: null, refresh: null, user: null, save: function (tokens) { if (!tokens) return; this.access = tokens.Access || tokens.access; this.refresh = tokens.Refresh || tokens.refresh; }, load: function () { return this.access; }, clear: function () { this.access = null; this.refresh = null; this.user = null; }, }; const Auth = { loginEmail: function (email, password) { return apiPost('/api/v1/auth/login', { email: email, password: password }); }, registerEmail: function (data) { return apiPost('/api/v1/auth/register', data); }, me: function () { return apiGet('/api/v1/me'); }, updateMe: function (p) { return apiPut('/api/v1/me', p); }, getPrefs: function () { return apiGet('/api/v1/me/prefs'); }, updatePrefs: function (p) { return apiPut('/api/v1/me/prefs', p); }, updateLocation: function (lat, lng, visible) { return apiPut('/api/v1/me/location', { lat: lat, lng: lng, visible: visible }); }, setVisibility: function (v) { return apiPut('/api/v1/me/visibility', { visible: v }); }, searchNearby: function (lat, lng, radius) { return apiGet('/api/v1/search/nearby?lat=' + lat + '&lng=' + lng + '&radius=' + radius); }, getUser: function (id) { return apiGet('/api/v1/users/' + id); }, }; const ChatApi = { ensureChat: function (otherId) { return apiPost('/api/v1/chats', { other_id: otherId }); }, listChats: function () { return apiGet('/api/v1/chats'); }, listMessages: function (chatId) { return apiGet('/api/v1/chats/' + chatId + '/messages'); }, sendMessage: function (chatId, body, photoUrl) { return apiPost('/api/v1/chats/' + chatId + '/messages', { body: body, photo_url: photoUrl }); }, editMessage: function (msgId, body) { return apiPut('/api/v1/messages/' + msgId, { body: body }); }, deleteMessage: function (msgId) { return apiDelete('/api/v1/messages/' + msgId); }, markRead: function (chatId) { return apiPut('/api/v1/chats/' + chatId + '/read', {}); }, block: function (userId) { return apiPost('/api/v1/blocks', { user_id: userId }); }, report: function (type, id, reason) { return apiPost('/api/v1/reports', { target_type: type, target_id: id, reason: reason }); }, }; const ReviewApi = { create: function (chatId, rating, body, anonymous) { return apiPost('/api/v1/reviews', { chat_id: chatId, rating: rating, body: body, anonymous: anonymous }); }, listForUser: function (userId) { return apiGet('/api/v1/users/' + userId + '/reviews'); }, stats: function (userId) { return apiGet('/api/v1/users/' + userId + '/stats'); }, }; const ConsentApi = { status: function () { return apiGet('/api/v1/auth/consents').catch(function () { return { all_accepted: false, accepted: {} }; }); }, accept: function (payload) { return apiPost('/api/v1/auth/consents', payload); }, }; const PlacesApi = { nearby: function (lat, lng, radius, limit) { limit = limit || 30; return apiGet('/api/v1/places/nearby?lat=' + lat + '&lng=' + lng + '&radius=' + (radius || 5) + '&limit=' + limit) .catch(function (e) { return { places: [], count: 0, error: e.message }; }); }, }; window.API = { api: api, apiGet: apiGet, apiPost: apiPost, apiPut: apiPut, apiDelete: apiDelete, Auth: Auth, ChatApi: ChatApi, ReviewApi: ReviewApi, ConsentApi: ConsentApi, PlacesApi: PlacesApi, Store: Store, };