From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server-vie001.gnuweeb.org X-Spam-Level: X-Spam-Status: No, score=-1.2 required=5.0 tests=ALL_TRUSTED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,T_FILL_THIS_FORM_SHORT, URIBL_DBL_BLOCKED_OPENDNS,URIBL_ZEN_BLOCKED_OPENDNS autolearn=ham autolearn_force=no version=3.4.6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gnuweeb.org; s=default; t=1741185666; bh=Nz+3QQncMqLb7LViGw9Kwy5A/FIU334ai6q3rrYh6R4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Transfer-Encoding:Message-ID:Date:From: Reply-To:Subject:To:Cc:In-Reply-To:References:Resent-Date: Resent-From:Resent-To:Resent-Cc:User-Agent:Content-Type: Content-Transfer-Encoding; b=gLd5XYnZuCUn1i/Y1ora/gtbSzzs6ZGBd+6tKnLbRdAXZEwvtvywhcHa0zBTeQvSB 246ehhCAI/iC3UsQgSp74xrTQ+JirdlB4h786LbCFXcmaNmmXLxnLznAoTypdwY5vz /SbNToX46VI7JJuBPt6qFpyo/LdupAmTF73scJ5NhCQswjOR5EbGaJoL1sko4geC6u ewlLUD23Er/C8Sr4TeoKoRiZBovK5bpc3i7g5PDbjvJ22oQr19nSuNVMHa9cR+IMzI hUVi7vs9/1reZYWSBmZ5Chf7GzZU0pZ9wgq79GCIeD1tXFsYvjdqbbNTKS7pNSQjEm M9ycUXrdq2Qww== Received: from localhost.localdomain (unknown [203.217.140.198]) by server-vie001.gnuweeb.org (Postfix) with ESMTPSA id 4C07220B4831; Wed, 5 Mar 2025 14:41:05 +0000 (UTC) From: Muhammad Rizki To: Ammar Faizi Cc: Muhammad Rizki , Alviro Iskandar Setiawan , GNU/Weeb Mailing List Subject: [PATCH v1 13/17] refactor!:feat: update API response structure, update profile page Date: Wed, 5 Mar 2025 21:40:12 +0700 Message-ID: <20250305144018.267-14-kiizuha@gnuweeb.org> X-Mailer: git-send-email 2.46.0.windows.1 In-Reply-To: <20250305144018.267-1-kiizuha@gnuweeb.org> References: <20250305144018.267-1-kiizuha@gnuweeb.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: - Update API response structure to match the new API response structure. - Update profile page to follow the user profile schema and its types. - Update login page to use the new API response structure. - Update the profile page to use the new API response structure. Signed-off-by: Muhammad Rizki --- src/lib/hooks/auth.svelte.ts | 12 +- src/lib/schemas/profile-schema.ts | 20 +- src/lib/typings/credential.d.ts | 11 +- src/lib/typings/http.d.ts | 11 +- src/routes/(protected)/+layout.ts | 7 +- .../(protected)/settings/profile/+page.svelte | 577 +++++++++++++----- .../(protected)/settings/profile/+page.ts | 7 +- src/routes/+page.svelte | 17 +- 8 files changed, 498 insertions(+), 164 deletions(-) diff --git a/src/lib/hooks/auth.svelte.ts b/src/lib/hooks/auth.svelte.ts index ba357f7..7bd2d3b 100644 --- a/src/lib/hooks/auth.svelte.ts +++ b/src/lib/hooks/auth.svelte.ts @@ -19,7 +19,7 @@ export function useAuth() { return data.user_info; }, - set token(newValue: string) { + set token(newValue) { data.token = newValue; }, @@ -28,11 +28,11 @@ export function useAuth() { data.user_info = JSON.parse(user!) as User; }, - save(newData: LoginResponse) { - data = newData; - localStorage.setItem("gwm_token", newData.token); - localStorage.setItem("gwm_token_exp_at", newData.token_exp_at.toString()); - localStorage.setItem("gwm_uinfo", JSON.stringify(newData.user_info)); + save({ user_info, token, token_exp_at }: LoginResponse) { + data = { user_info, token, token_exp_at }; + localStorage.setItem("gwm_token", token || "null"); + localStorage.setItem("gwm_token_exp_at", token_exp_at!.toString() ?? "0"); + localStorage.setItem("gwm_uinfo", JSON.stringify(user_info)); }, clear() { diff --git a/src/lib/schemas/profile-schema.ts b/src/lib/schemas/profile-schema.ts index fa67c17..c4c7228 100644 --- a/src/lib/schemas/profile-schema.ts +++ b/src/lib/schemas/profile-schema.ts @@ -1,8 +1,22 @@ import { z } from "zod"; +const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB + +const photoSchema = z.instanceof(File).refine((file) => file.size <= MAX_FILE_SIZE, { + message: "File size must be less than 10MB" +}); + export const profileSchema = z.object({ - avatar: z.instanceof(File).optional(), + photo: photoSchema.nullable(), username: z.string().optional(), - full_name: z.string().optional(), - gender: z.string().optional() + full_name: z.string().min(1, "Full name is required"), + ext_email: z.string().email("Invalid email format"), + gender: z.enum(["m", "f"]), + socials: z.object({ + github_username: z.string().optional(), + telegram_username: z.string().optional(), + twitter_username: z.string().optional(), + discord_username: z.string().optional() + }), + password: z.string() }); diff --git a/src/lib/typings/credential.d.ts b/src/lib/typings/credential.d.ts index df02c86..beabe5e 100644 --- a/src/lib/typings/credential.d.ts +++ b/src/lib/typings/credential.d.ts @@ -1,7 +1,16 @@ export interface User { user_id: number; full_name: string; - gender: string; + gender: Gender; username: string; + ext_email: string; role: string; + is_active: IsActive | boolean; + socials: { + github_username: string; + telegram_username: string; + twitter_username: string; + discord_username: string; + }; + photo: string; } diff --git a/src/lib/typings/http.d.ts b/src/lib/typings/http.d.ts index 98d64f5..1d390a3 100644 --- a/src/lib/typings/http.d.ts +++ b/src/lib/typings/http.d.ts @@ -2,7 +2,10 @@ import type { User } from "./credential"; export interface ResponseAPI { code: number; - res?: Data & { renew_token?: RenewTokenResponse }; + res?: Data & { + msg: string; + renew_token?: RenewTokenResponse; + }; } export interface RenewTokenResponse { @@ -11,7 +14,7 @@ export interface RenewTokenResponse { } export interface LoginResponse { - token: string; - token_exp_at: number; - user_info?: User | null; + token?: string; + token_exp_at?: number; + user_info?: User; } diff --git a/src/routes/(protected)/+layout.ts b/src/routes/(protected)/+layout.ts index 91dbfc7..06ff2db 100644 --- a/src/routes/(protected)/+layout.ts +++ b/src/routes/(protected)/+layout.ts @@ -16,6 +16,9 @@ export const load: LayoutLoad = async () => { params: { action: "get_user_info" } }); - localStorage.setItem("gwm_uinfo", JSON.stringify(data.res?.user_info)); - auth.refresh(); + auth.save({ + token: data.res?.renew_token?.token, + token_exp_at: data.res?.renew_token?.token_exp_at, + user_info: data.res?.user_info + }); }; diff --git a/src/routes/(protected)/settings/profile/+page.svelte b/src/routes/(protected)/settings/profile/+page.svelte index 73edb8a..b91382a 100644 --- a/src/routes/(protected)/settings/profile/+page.svelte +++ b/src/routes/(protected)/settings/profile/+page.svelte @@ -1,170 +1,469 @@ -
-
- - - {#snippet children({ props })} - - - - {getShortName()} - -
- - Edit -
-
- - {/snippet} -
-
-
- -
- - - {#snippet children({ props })} - Username - - This is your GNU/Weeb email username. - {/snippet} - - - - - - {#snippet children({ props })} - Full Name - - {/snippet} - - - - - - {#snippet children({ props })} - Gender - + +
+
+ + + {#snippet children({ props })} + + Profile picture + + + + {#if !Boolean(avatar)} + {getShortName()} + {:else} + + {/if} + + + + + + Edit + + + + + + + + + {/snippet} + + +
+ +
+ + + {#snippet children({ props })} + Username + + This is your GNU/Weeb email username. + {/snippet} + + + + + + {#snippet children({ props })} + External Email + + + + This is your external email address e.g username@gmail.com + + {/snippet} + + + + + + {#snippet children({ props })} + Full Name + + + You full name must be your real name. + {/snippet} + + + + + + {#snippet children({ props })} + Gender + +
+ + +
+
+ + +
+
+ + + Your real gender, NO LGBT. + + {/snippet} +
+
+ +
+ -
- - -
-
- - -
- - {/snippet} - - -
- - - + Social Accounts + +
+ + + + {#snippet children({ props })} + + {/snippet} + + +
+
+ + + + {#snippet children({ props })} + + {/snippet} + + +
+
+ + + + {#snippet children({ props })} + + {/snippet} + + +
+
+ + + + {#snippet children({ props })} + + {/snippet} + + +
+
+
+ + +
+ + + {#snippet child({ props })} + + {/snippet} + + + + + Update Profile Confirmation + Confirm changes to your profile here. + +
+ + + {#snippet children({ props })} + Password + + + Your password is required to make changes to your profile. + + {/snippet} + + +
+ + + +
+ + diff --git a/src/routes/(protected)/settings/profile/+page.ts b/src/routes/(protected)/settings/profile/+page.ts index 9e036c8..7b25a4f 100644 --- a/src/routes/(protected)/settings/profile/+page.ts +++ b/src/routes/(protected)/settings/profile/+page.ts @@ -11,10 +11,11 @@ export const load: PageLoad = async () => { const data = { username: auth.user?.username, full_name: auth.user?.full_name, - gender: auth.user?.gender + ext_email: auth.user?.ext_email, + gender: auth.user?.gender, + socials: auth.user?.socials }; const form = await superValidate(data, zod(profileSchema)); - - return { form }; + return { form, avatar: auth.user?.photo }; }; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index eeadde5..cb4f673 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -22,7 +22,10 @@ validators: zod(loginSchema), async onUpdate({ form }) { - const res = await http({ + const { + status, + data: { res } + } = await http({ params: { action: "login" }, method: "POST", data: { @@ -31,12 +34,12 @@ } }); - if (res.status === 200) { - auth.save(res.data.res!); + if (status === 200) { + auth.save(res as typing.LoginResponse); } else { setError(form, "username_or_email", ""); setError(form, "password", ""); - setMessage(form, res.data.res); + setMessage(form, res?.msg ?? "Invalid credential, please login again."); } } }); @@ -57,8 +60,10 @@
- GNU/Weeb Mail Login - Proceed login to manage your email account + GNU/Weeb Mail Login + + Proceed login to manage your email account + {#if isError() && !isCredentialInvalid()} -- Muhammad Rizki