import { POST_LIMIT, PROFILE_POST_LIMIT } from '../constanst'; import { PostAuth, PostDelete, PostListAuth, PostListNonAuth, PostNew, PostUpdate } from '../types/Post'; import { User, UserImageUpdate, UserUpdate } from '../types/User'; const BASE = 'https://khofmann.userpage.fu-berlin.de/phpCourse/exam/api/'; let instance: ApiImpl; class ApiImpl { private token?: string; private refreshToken?: string; private self?: User; private userListeners: ((user?: User) => void)[] = []; constructor() { if (instance) { throw new Error('New instance cannot be created!!'); } //eslint-disable-next-line @typescript-eslint/no-this-alias instance = this; } public hasAuth = () => this.token !== undefined; //FIXME: TESTING public isAdmin = () => this.hasAuth() && this.self?.isAdmin; public getAuthenticatedUser = () => this.self; public getCurrentSession = () => [this.token, this.refreshToken]; public subscribeToAuthenticatedUser = (callback: (user?: User) => void) => { this.userListeners.push(callback); return () => { this.userListeners = this.userListeners.filter((item) => item !== callback); }; }; public logIn = async (email: string, password: string): Promise => { const { user, token } = await (await this.post('login', { email, password })).json(); this.self = user; this.token = token; this.userListeners.forEach((listener) => listener(user)); }; public logOut = async (): Promise => { try { return await (await this.postAuth('logout')).json(); } catch { return false; } finally { this.self = undefined; this.token = undefined; this.userListeners.forEach((listener) => listener()); } }; public posts = async (page?: number): Promise => { const url = `posts?p=${page ?? 0}&l=${POST_LIMIT}`; if (this.token) return await (await this.getAuth(url)).json(); return await (await this.get(url)).json(); }; public deletePost = async (id: number): Promise => { return await (await this.delete(`posts/${id}?l=${POST_LIMIT}`)).json(); }; public user = async (id?: number): Promise => { return await (await this.getAuth(`users/${id ?? this.self?.id}`)).json(); }; public updateUser = async (data: UserUpdate, id?: number): Promise => { const user = await (await this.patch(`users/${id ?? 'self'}`, data as Record)).json(); this.self = user; this.userListeners.forEach((listener) => listener(user)); return user; }; public updateUserImage = async (data: UserImageUpdate, id?: number): Promise => { const formData = new FormData(); if (data.image) formData.append('image', data.image); if (!data.image && data.predefined) formData.append('predefined', data.predefined); const user = await (await this.postAuthRaw(`users/${id ?? 'self'}/image`, formData)).json(); this.self = user; this.userListeners.forEach((listener) => listener(user)); return user; }; public newPost = async (data: PostUpdate): Promise => { return await (await this.postAuth(`posts?l=${POST_LIMIT}`, { ...data } as Record)).json(); }; public updatePost = async (data: PostUpdate, id: number): Promise => { return await (await this.patch(`posts/${id}`, data as Record)).json(); }; public userPosts = async (id?: number): Promise => { return await (await this.getAuth(`users/${id}/posts?l=${PROFILE_POST_LIMIT}&s=desc`)).json(); }; /* Internal */ private post = async (endpoint: string, body?: Record, headers?: HeadersInit) => { const response = await fetch(`${BASE}${endpoint}`, { mode: 'cors', method: 'post', headers, body: JSON.stringify(body), }); if (response.ok) return response; throw await response.json(); }; private postAuth = async (endpoint: string, body?: Record, headers?: HeadersInit) => { const response = await fetch(`${BASE}${endpoint}`, { mode: 'cors', method: 'post', headers: { token: this.token ?? '', ...headers }, body: JSON.stringify(body), }); if (response.ok) return response; throw await response.json(); }; private postAuthRaw = async (endpoint: string, body?: FormData, headers?: HeadersInit) => { const response = await fetch(`${BASE}${endpoint}`, { mode: 'cors', method: 'post', headers: { token: this.token ?? '', ...headers }, body, }); if (response.ok) return response; throw await response.json(); }; private get = async (endpoint: string, headers?: HeadersInit) => { const response = await fetch(`${BASE}${endpoint}`, { mode: 'cors', method: 'get', headers, }); if (response.ok) return response; throw await response.json(); }; private getAuth = async (endpoint: string, headers?: HeadersInit) => { const response = await fetch(`${BASE}${endpoint}`, { mode: 'cors', method: 'get', headers: { token: this.token ?? '', ...headers }, }); if (response.ok) return response; throw await response.json(); }; private delete = async (endpoint: string, headers?: HeadersInit) => { const response = await fetch(`${BASE}${endpoint}`, { mode: 'cors', method: 'delete', headers: { token: this.token ?? '', ...headers }, }); if (response.ok) return response; throw await response.json(); }; private patch = async (endpoint: string, body?: Record, headers?: HeadersInit) => { const response = await fetch(`${BASE}${endpoint}`, { mode: 'cors', method: 'patch', headers: { token: this.token ?? '', ...headers }, body: JSON.stringify(body), }); if (response.ok) return response; throw await response.json(); }; } const Api = new ApiImpl(); export default Api;