From 7b1d023a9465de8916d43d374b4108c2daa215c9 Mon Sep 17 00:00:00 2001 From: chatenium Date: Fri, 3 Apr 2026 07:38:53 +0200 Subject: [PATCH] Started implementing PictureService (Testing+JSDoc TODO) --- src/core/environment.ts | 4 +- src/core/http.ts | 4 +- src/domain/pictureService.schema.ts | 98 ++++++++++++ src/services/authService.ts | 28 ++-- src/services/networkService.test.ts | 2 +- src/services/networkService.ts | 2 +- src/services/pictureService.ts | 240 ++++++++++++++++++++++++++++ 7 files changed, 359 insertions(+), 19 deletions(-) create mode 100644 src/domain/pictureService.schema.ts create mode 100644 src/services/pictureService.ts diff --git a/src/core/environment.ts b/src/core/environment.ts index 2b83dc5..90edb42 100644 --- a/src/core/environment.ts +++ b/src/core/environment.ts @@ -1,9 +1,11 @@ export interface SDKConfig { apiUrl: string; + cdnUrl: string; } const DefaultEnvironment: SDKConfig = { - apiUrl: "https://api.chatenium.hu" + apiUrl: "https://api.chatenium.hu", + cdnUrl: "https://cdn.chatenium.hu", } let currentConfig = {...DefaultEnvironment} diff --git a/src/core/http.ts b/src/core/http.ts index e7a12eb..5e4a5ae 100644 --- a/src/core/http.ts +++ b/src/core/http.ts @@ -1,11 +1,11 @@ import axios, {AxiosInstance} from 'axios'; import {environment} from "./environment"; -export const getClient = () => { +export const getClient = (cdn: boolean) => { const env = environment.get(); return axios.create({ - baseURL: env.apiUrl, + baseURL: cdn ? env.cdnUrl : env.apiUrl, timeout: 5000, headers: {'Content-Type': 'application/json'} }); diff --git a/src/domain/pictureService.schema.ts b/src/domain/pictureService.schema.ts new file mode 100644 index 0000000..612acfa --- /dev/null +++ b/src/domain/pictureService.schema.ts @@ -0,0 +1,98 @@ +import {PublicUserData, TimeStamp} from "./common.schema"; + +// Request schemas +export interface GetReq { + userid: string + target: string +} + +export interface CreateAlbumReq { + name: string + userid: string +} + +export interface FinalizeUploadReq { + userid: string + uploadId: string +} + +export interface DeleteImageReq { + userid: string + imageId: string +} + +export interface ChangePictureVisibilityReq { + visibility: string + userid: string + imageId: string +} + +export interface EditPictureTitleReq { + title: string + userid: string + imageId: string +} + +export interface TogglePictureLikeReq { + userid: string + imageId: string +} + +export interface PostCommentReq { + comment: string + userid: string + imageId: string +} + +export interface ToggleFollowReq { + uploaderId: string + userid: string +} + +export interface GetAlbumReq { + userid: string +} + +export interface GetCommentsReq { + imageId: string +} + +export interface UploadImageReq { + userid: string + picture: string + title: string + visibility: string + album: string + uploadId: string + name: string +} + +// Response schemas +export interface GetResp { + userData: PublicUserData + pictures: Album[] +} + +export interface DiscoveryResp { + recent: Image[] + mostLiked: Image[] +} + +// Types +export interface Album { + created_at: TimeStamp + name: string + albumId: string + images: Image[] +} + +export interface Image { + path: string + visibility: string + title: string + imageId: string + uploaded_at: TimeStamp + liked: boolean + likes: number + userData: PublicUserData | null +} \ No newline at end of file diff --git a/src/services/authService.ts b/src/services/authService.ts index ac55ccc..e93af0b 100644 --- a/src/services/authService.ts +++ b/src/services/authService.ts @@ -17,7 +17,7 @@ export class AuthService { */ async getAuthMethods(unameMailPhone: string): Promise { try { - const resp = await getClient().get(`user/authOptions?unameMailPhone=${unameMailPhone}`); + const resp = await getClient(false).get(`user/authOptions?unameMailPhone=${unameMailPhone}`); return resp.data } catch (e) { if (isAxiosError(e)) { @@ -35,7 +35,7 @@ export class AuthService { */ async otpSendCode(unameMailPhone: string, type: number): Promise { try { - const resp = await getClient().post("v2/user/otpSendCode", { + const resp = await getClient(false).post("v2/user/otpSendCode", { usernamePhoneMail: unameMailPhone, type: type }); @@ -60,7 +60,7 @@ export class AuthService { */ async otpVerifyCode(unameMailPhone: string, type: number, code: number): Promise { try { - const resp = await getClient().post("v2/user/otpVerifyCode", { + const resp = await getClient(false).post("v2/user/otpVerifyCode", { usernamePhoneMail: unameMailPhone, type: type, code: code @@ -81,7 +81,7 @@ export class AuthService { */ async loginPasswordAuth(usernamePhoneMail: string, password: string): Promise { try { - const resp = await getClient().post("v2/user/loginPasswordAuth", { + const resp = await getClient(false).post("v2/user/loginPasswordAuth", { unameMailPhone: usernamePhoneMail, password: password }); @@ -99,7 +99,7 @@ export class AuthService { */ async isUsernameUsed(username: string): Promise { try { - const resp = await getClient().get(`v2/user/unameUsage?username=${username}`); + const resp = await getClient(false).get(`v2/user/unameUsage?username=${username}`); return resp.data.used } catch (e) { if (isAxiosError(e)) { @@ -114,7 +114,7 @@ export class AuthService { */ async isEmailUsed(email: string): Promise { try { - const resp = await getClient().get(`v2/user/emailUsage?email=${email}`); + const resp = await getClient(false).get(`v2/user/emailUsage?email=${email}`); return resp.data.used } catch (e) { if (isAxiosError(e)) { @@ -132,7 +132,7 @@ export class AuthService { */ async pleSendVCode(phoneMail: string, type: number): Promise { try { - const resp = await getClient().post("v2/user/pleSendVCode", { + const resp = await getClient(false).post("v2/user/pleSendVCode", { phoneMail: phoneMail, type: type, }); @@ -157,7 +157,7 @@ export class AuthService { */ async pleVerifyCode(phoneMail: string, type: number, code: number): Promise { try { - const resp = await getClient().post("v2/user/pleVerifyCode", { + const resp = await getClient(false).post("v2/user/pleVerifyCode", { phoneMail: phoneMail, type: type, code: code @@ -181,7 +181,7 @@ export class AuthService { */ async finishPLEAccount(phoneMail: string, authCode: string, username: string, displayName: string|null): Promise { try { - const resp = await getClient().post("v2/user/finishPLEAccount", { + const resp = await getClient(false).post("v2/user/finishPLEAccount", { phoneMail: phoneMail, authCode: authCode, displayName: displayName, @@ -202,7 +202,7 @@ export class AuthService { */ async loginWithGoogle(code: string): Promise { try { - const resp = await getClient().post("user/loginWithGoogle", { + const resp = await getClient(false).post("user/loginWithGoogle", { code: code }); return resp.data @@ -220,7 +220,7 @@ export class AuthService { */ async loginWithApple(code: string): Promise { try { - const resp = await getClient().post("user/loginWithGoogle", { + const resp = await getClient(false).post("user/loginWithGoogle", { code: code, isApple: false, }); @@ -241,7 +241,7 @@ export class AuthService { */ async register(username: string, password: string, displayName: string|null): Promise { try { - const resp = await getClient().post("v2/user/register", { + const resp = await getClient(false).post("v2/user/register", { username: username, displayName: displayName, password: password, @@ -263,7 +263,7 @@ export class AuthService { */ async resetPassword(unameMailPhone: string): Promise { try { - const resp = await getClient().post("user/resetPassword", { + const resp = await getClient(false).post("user/resetPassword", { unameMailPhone: unameMailPhone, }); if (resp.data.code != null) { @@ -287,7 +287,7 @@ export class AuthService { */ async verifyPasswordReset(unameMailPhone: string, code: number, newPassword: string): Promise { try { - await getClient().post("user/verifyResetCode", { + await getClient(false).post("user/verifyResetCode", { unameMailPhone: unameMailPhone, vCode: code, newPassword: newPassword, diff --git a/src/services/networkService.test.ts b/src/services/networkService.test.ts index be5a747..61e7764 100644 --- a/src/services/networkService.test.ts +++ b/src/services/networkService.test.ts @@ -6,7 +6,7 @@ import {getClient} from "../core/http"; import {environment, SDKConfig} from "../core/environment"; describe("NetworkService", () => { - const service = new NetworkService("", "", "", new DatabaseMock(), getClient()) + const service = new NetworkService("", "", "", new DatabaseMock(), getClient(false)) it('should get invites', async () => { const invites = await service.getInvites(); diff --git a/src/services/networkService.ts b/src/services/networkService.ts index 1bb6cf7..4d72d3d 100644 --- a/src/services/networkService.ts +++ b/src/services/networkService.ts @@ -30,7 +30,7 @@ export class NetworkService { this.userid = userid; this.networkId = networkId; this.database = database; - this.client = (httpClientOverwrite ?? getClient()).create({ + this.client = (httpClientOverwrite ?? getClient(false)).create({ headers: { "Authorization": token } diff --git a/src/services/pictureService.ts b/src/services/pictureService.ts new file mode 100644 index 0000000..93d2c3c --- /dev/null +++ b/src/services/pictureService.ts @@ -0,0 +1,240 @@ +import {DatabaseAPI} from "../storage/database"; +import {AxiosInstance, isAxiosError} from "axios"; +import {getClient} from "../core/http"; +import {NetworkInvite} from "../domain/networkService.schema"; +import {GenericErrorBody} from "../domain/http.schema"; +import { + Album, ChangePictureVisibilityReq, + CreateAlbumReq, + DeleteImageReq, + DiscoveryResp, EditPictureTitleReq, + FinalizeUploadReq, + GetResp, PostCommentReq, ToggleFollowReq, TogglePictureLikeReq, UploadImageReq +} from "../domain/pictureService.schema"; +import {environment} from "../core/environment"; + +export class PictureService { + userid: string; + uploaderId: string; + database: DatabaseAPI; + client: AxiosInstance + cdnClient: AxiosInstance + + constructor(token: string, uploaderId: string, userid: string, database: DatabaseAPI, httpClientOverwrite: AxiosInstance | null, cdnClientOverwrite: AxiosInstance | null) { + this.userid = userid; + this.uploaderId = uploaderId; + this.database = database; + this.client = (httpClientOverwrite ?? getClient(false)).create({ + headers: { + "Authorization": token + } + }) + this.cdnClient = (cdnClientOverwrite ?? getClient(true)).create({ + headers: { + "Authorization": token + } + }) + } + + async get(): Promise { + try { + const resp = await this.client.get(`picture/pictures?userid=${this.userid}&target=${this.uploaderId}`); + return resp.data + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async discovery(): Promise { + try { + const resp = await this.client.get("picture/discovery"); + return resp.data + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async createAlbum(name: string): Promise { + try { + const resp = await this.client.post("picture/createAlbum", { + userid: this.userid, + name: name + }); + return resp.data + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async finalizeUpload(uploadId: string): Promise { + try { + await this.client.post("picture/finalizeUpload", { + userid: this.userid, + uploadId: uploadId, + }); + return + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async deletePicture(imageId: string): Promise { + try { + await this.client.post("picture/delete", { + userid: this.userid, + imageId: imageId + }); + return + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async changeVisibility(imageId: string, newVisibility: string): Promise { + try { + await this.client.post("picture/changeVisibility", { + userid: this.userid, + visibility: newVisibility + }); + return + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async editTitle(imageId: string, newTitle: string): Promise { + try { + await this.client.post("picture/editTitle", { + userid: this.userid, + title: newTitle + }); + return + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async toggleLike(imageId: string): Promise { + try { + await this.client.post("picture/toggleLike", { + userid: this.userid, + imageId: imageId + }); + return + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async postComment(imageId: string, comment: string): Promise { + try { + await this.client.post("picture/comment", { + userid: this.userid, + imageId: imageId, + comment: comment + }); + return + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async toggleFollow(): Promise { + try { + await this.client.post("picture/toggleFollow", { + userid: this.userid, + uploaderId: this.uploaderId, + }); + return + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async getAlbums(): Promise { + try { + const resp = await this.client.get(`picture/getAlbums?userid=${this.userid}`); + return resp.data + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async getComments(imageId: string): Promise { + try { + const resp = await this.client.get(`picture/comment?userid=${this.userid}&imageId=${imageId}`); + return resp.data + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } + + async uploadPicture(picture: string, title: string, visibility: string, album: string, uploadId: string, name: string): Promise { + try { + await this.cdnClient.post("picture/upload", { + album: album, + uploadId: uploadId, + title: title, + picture: picture, + name: name, + visibility: visibility, + userid: this.userid, + }); + return + } catch (e) { + console.log(e) + if (isAxiosError(e)) { + throw e; + } + throw new Error("Unexpected error") + } + } +} \ No newline at end of file