Fix: api.js без ES modules — Safari iOS не видит export
api.js был написан как ES module ("export async function") но подключён
как обычный <script>. Safari WebKit не поднимает export'ы на window.
Переписан на обычный const/async function с window.API в конце.
This commit is contained in:
parent
058649013d
commit
ab6320dff8
136
public/js/api.js
136
public/js/api.js
@ -1,5 +1,5 @@
|
||||
// Same-origin API wrapper (api проксируется через nginx на тот же домен)
|
||||
// Никаких retry/timeout на login — только UI feedback
|
||||
// Same-origin API wrapper — обычный скрипт, без ES modules (Safari iOS)
|
||||
// Используем только credentials:'include' + кэш отключен
|
||||
|
||||
const DEFAULT_INIT = {
|
||||
credentials: 'include',
|
||||
@ -7,8 +7,14 @@ const DEFAULT_INIT = {
|
||||
redirect: 'follow',
|
||||
};
|
||||
|
||||
export async function api(path, options = {}) {
|
||||
const { json, headers: extraHeaders, ...rest } = options;
|
||||
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;
|
||||
|
||||
@ -28,11 +34,11 @@ export async function api(path, options = {}) {
|
||||
});
|
||||
|
||||
const ct = res.headers.get('content-type') || '';
|
||||
const isJson = ct.includes('application/json');
|
||||
const data = isJson ? await res.json().catch(() => null) : await res.text();
|
||||
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}`);
|
||||
const err = new Error((data && (data.message || data.error)) || ('HTTP ' + res.status));
|
||||
err.status = res.status;
|
||||
err.data = data;
|
||||
throw err;
|
||||
@ -40,60 +46,70 @@ export async function api(path, options = {}) {
|
||||
return data;
|
||||
}
|
||||
|
||||
export const apiGet = (path, opts) => api(path, { ...opts, method: 'GET' });
|
||||
export const apiPost = (path, json, opts) => api(path, { ...opts, method: 'POST', json });
|
||||
export const apiPut = (path, json, opts) => api(path, { ...opts, method: 'PUT', json });
|
||||
export const apiDelete = (path, opts) => api(path, { ...opts, method: 'DELETE' });
|
||||
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' })); };
|
||||
|
||||
// Совместимость со старым window.API.Auth
|
||||
window.API = {
|
||||
api, apiGet, apiPost, apiPut, apiDelete,
|
||||
// обёртки для совместимости со старыми view
|
||||
Auth: {
|
||||
loginEmail: (email, password) => apiPost('/api/v1/auth/login', { email, password }),
|
||||
registerEmail: (data) => apiPost('/api/v1/auth/register', data),
|
||||
me: () => apiGet('/api/v1/me'),
|
||||
updateMe: (p) => apiPut('/api/v1/me', p),
|
||||
getPrefs: () => apiGet('/api/v1/me/prefs'),
|
||||
updatePrefs: (p) => apiPut('/api/v1/me/prefs', p),
|
||||
updateLocation: (lat, lng, visible) => apiPut('/api/v1/me/location', { lat, lng, visible }),
|
||||
setVisibility: (v) => apiPut('/api/v1/me/visibility', { visible: v }),
|
||||
searchNearby: (lat, lng, radius) => apiGet(`/api/v1/search/nearby?lat=${lat}&lng=${lng}&radius=${radius}`),
|
||||
getUser: (id) => apiGet('/api/v1/users/' + id),
|
||||
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;
|
||||
},
|
||||
ChatApi: {
|
||||
ensureChat: (otherId) => apiPost('/api/v1/chats', { other_id: otherId }),
|
||||
listChats: () => apiGet('/api/v1/chats'),
|
||||
listMessages: (chatId) => apiGet('/api/v1/chats/' + chatId + '/messages'),
|
||||
sendMessage: (chatId, body, photoUrl) => apiPost('/api/v1/chats/' + chatId + '/messages', { body, photo_url: photoUrl }),
|
||||
editMessage: (msgId, body) => apiPut('/api/v1/messages/' + msgId, { body }),
|
||||
deleteMessage: (msgId) => apiDelete('/api/v1/messages/' + msgId),
|
||||
markRead: (chatId) => apiPut('/api/v1/chats/' + chatId + '/read', {}),
|
||||
block: (userId) => apiPost('/api/v1/blocks', { user_id: userId }),
|
||||
report: (type, id, reason) => apiPost('/api/v1/reports', { target_type: type, target_id: id, reason }),
|
||||
},
|
||||
ReviewApi: {
|
||||
create: (chatId, rating, body, anonymous) => apiPost('/api/v1/reviews', { chat_id: chatId, rating, body, anonymous }),
|
||||
listForUser: (userId) => apiGet('/api/v1/users/' + userId + '/reviews'),
|
||||
stats: (userId) => apiGet('/api/v1/users/' + userId + '/stats'),
|
||||
},
|
||||
ConsentApi: {
|
||||
status: () => apiGet('/api/v1/auth/consents').catch(() => ({ all_accepted: false, accepted: {} })),
|
||||
accept: (payload) => apiPost('/api/v1/auth/consents', payload),
|
||||
},
|
||||
PlacesApi: {
|
||||
nearby: (lat, lng, radius, limit = 30) => apiGet(`/api/v1/places/nearby?lat=${lat}&lng=${lng}&radius=${radius || 5}&limit=${limit}`).catch((e) => ({ places: [], count: 0, error: e.message })),
|
||||
},
|
||||
Store: {
|
||||
access: null,
|
||||
refresh: null,
|
||||
user: null,
|
||||
save(tokens) {
|
||||
if (!tokens) return;
|
||||
this.access = tokens.Access || tokens.access;
|
||||
this.refresh = tokens.Refresh || tokens.refresh;
|
||||
},
|
||||
load() { return this.access; },
|
||||
clear() { this.access = null; this.refresh = null; this.user = null; },
|
||||
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,
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user