Spaces:
Running
Running
| /* ============================================================ | |
| GLASSGRID — API LAYER | |
| All data operations go through this layer. | |
| Zero UI modification. Returns structured data objects. | |
| Uses Schema for validation. | |
| ============================================================ */ | |
| ; | |
| import { validate, applyDefaults } from '../data/schema.js'; | |
| const BASE_URL = '/api'; // Replace with your backend URL | |
| async function request(method, endpoint, body = null, headers = {}) { | |
| const token = localStorage.getItem('gg_auth_token'); | |
| const opts = { | |
| method, | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| ...(token ? { Authorization: `Bearer ${token}` } : {}), | |
| ...headers, | |
| }, | |
| ...(body ? { body: JSON.stringify(body) } : {}), | |
| }; | |
| const res = await fetch(`${BASE_URL}${endpoint}`, opts); | |
| if (!res.ok) { | |
| const err = await res.json().catch(() => ({ message: `HTTP ${res.status}` })); | |
| throw new Error(err.message || `Request failed: ${res.status}`); | |
| } | |
| return res.json(); | |
| } | |
| export const api = { | |
| // ── Auth ────────────────────────────── | |
| async login(identifier, password) { | |
| const data = await request('POST', '/auth/login', { identifier, password }); | |
| if (data.token) localStorage.setItem('gg_auth_token', data.token); | |
| return data; | |
| }, | |
| async logout() { | |
| localStorage.removeItem('gg_auth_token'); | |
| await request('POST', '/auth/logout').catch(() => {}); | |
| }, | |
| async register(username, email, password) { | |
| return request('POST', '/auth/register', { username, email, password }); | |
| }, | |
| async getCurrentUser() { | |
| return request('GET', '/auth/me'); | |
| }, | |
| // ── Posts ───────────────────────────── | |
| async getFeed(cursor = null, limit = 20) { | |
| const q = new URLSearchParams({ limit }); | |
| if (cursor) q.set('cursor', cursor); | |
| return request('GET', `/posts/feed?${q}`); | |
| }, | |
| async getExplorePosts(cursor = null, limit = 30) { | |
| const q = new URLSearchParams({ limit }); | |
| if (cursor) q.set('cursor', cursor); | |
| return request('GET', `/posts/explore?${q}`); | |
| }, | |
| async getPost(postId) { | |
| return request('GET', `/posts/${postId}`); | |
| }, | |
| async createPost(formData) { | |
| const token = localStorage.getItem('gg_auth_token'); | |
| const res = await fetch(`${BASE_URL}/posts`, { | |
| method: 'POST', | |
| headers: { ...(token ? { Authorization: `Bearer ${token}` } : {}) }, | |
| body: formData, // multipart/form-data for image upload | |
| }); | |
| if (!res.ok) throw new Error(`Upload failed: ${res.status}`); | |
| return res.json(); | |
| }, | |
| async deletePost(postId) { | |
| return request('DELETE', `/posts/${postId}`); | |
| }, | |
| // ── Likes ───────────────────────────── | |
| async likePost(postId) { | |
| return request('POST', `/posts/${postId}/like`); | |
| }, | |
| async unlikePost(postId) { | |
| return request('DELETE', `/posts/${postId}/like`); | |
| }, | |
| async likeComment(commentId) { | |
| return request('POST', `/comments/${commentId}/like`); | |
| }, | |
| // ── Comments ────────────────────────── | |
| async getComments(postId, cursor = null) { | |
| const q = new URLSearchParams({ limit: 20 }); | |
| if (cursor) q.set('cursor', cursor); | |
| return request('GET', `/posts/${postId}/comments?${q}`); | |
| }, | |
| async addComment(postId, text, parentId = null) { | |
| return request('POST', `/posts/${postId}/comments`, { text, parentId }); | |
| }, | |
| async deleteComment(commentId) { | |
| return request('DELETE', `/comments/${commentId}`); | |
| }, | |
| // ── Users ───────────────────────────── | |
| async getUser(username) { | |
| return request('GET', `/users/${username}`); | |
| }, | |
| async getUserPosts(userId, cursor = null) { | |
| const q = new URLSearchParams({ limit: 21 }); | |
| if (cursor) q.set('cursor', cursor); | |
| return request('GET', `/users/${userId}/posts?${q}`); | |
| }, | |
| async followUser(userId) { | |
| return request('POST', `/users/${userId}/follow`); | |
| }, | |
| async unfollowUser(userId) { | |
| return request('DELETE', `/users/${userId}/follow`); | |
| }, | |
| async updateProfile(data) { | |
| return request('PATCH', '/users/me', data); | |
| }, | |
| // ── Stories ─────────────────────────── | |
| async getStories() { | |
| return request('GET', '/stories'); | |
| }, | |
| async viewStory(storyId) { | |
| return request('POST', `/stories/${storyId}/view`); | |
| }, | |
| // ── Config (Admin) ──────────────────── | |
| async getConfig() { | |
| return request('GET', '/admin/config'); | |
| }, | |
| async updateConfig(config) { | |
| return request('PATCH', '/admin/config', config); | |
| }, | |
| async updateFeatureFlag(featureKey, enabled) { | |
| return request('PATCH', `/admin/features/${featureKey}`, { enabled }); | |
| }, | |
| // ── Moderation (Admin) ──────────────── | |
| async getModerationQueue() { | |
| return request('GET', '/admin/moderation'); | |
| }, | |
| async approvePost(postId) { | |
| return request('POST', `/admin/posts/${postId}/approve`); | |
| }, | |
| async removePost(postId, reason) { | |
| return request('DELETE', `/admin/posts/${postId}`, { reason }); | |
| }, | |
| async banUser(userId, reason) { | |
| return request('POST', `/admin/users/${userId}/ban`, { reason }); | |
| }, | |
| async unbanUser(userId) { | |
| return request('DELETE', `/admin/users/${userId}/ban`); | |
| }, | |
| }; | |