✨ Avatar
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,2 @@ | ||||
| /dist | ||||
| /uploads | ||||
| @@ -2,6 +2,8 @@ package models | ||||
|  | ||||
| import ( | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
|  | ||||
| 	"gorm.io/datatypes" | ||||
| @@ -19,6 +21,7 @@ type Account struct { | ||||
|  | ||||
| 	Name         string                       `json:"name" gorm:"uniqueIndex"` | ||||
| 	Nick         string                       `json:"nick"` | ||||
| 	Avatar       string                       `json:"avatar"` | ||||
| 	State        AccountState                 `json:"state"` | ||||
| 	Profile      AccountProfile               `json:"profile"` | ||||
| 	Sessions     []AuthSession                `json:"sessions"` | ||||
| @@ -39,6 +42,11 @@ func (v Account) GetPrimaryEmail() AccountContact { | ||||
| 	return val | ||||
| } | ||||
|  | ||||
| func (v Account) GetAvatarPath() string { | ||||
| 	basepath := viper.GetString("content") | ||||
| 	return filepath.Join(basepath, v.Avatar) | ||||
| } | ||||
|  | ||||
| type AccountContactType = int8 | ||||
|  | ||||
| const ( | ||||
|   | ||||
							
								
								
									
										35
									
								
								pkg/server/avatar_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								pkg/server/avatar_api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/hydrogen/passport/pkg/database" | ||||
| 	"code.smartsheep.studio/hydrogen/passport/pkg/models" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"path/filepath" | ||||
| ) | ||||
|  | ||||
| func getAvatar(c *fiber.Ctx) error { | ||||
| 	id := c.Params("avatarId") | ||||
| 	basepath := viper.GetString("content") | ||||
|  | ||||
| 	return c.SendFile(filepath.Join(basepath, id)) | ||||
| } | ||||
|  | ||||
| func setAvatar(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	file, err := c.FormFile("avatar") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	user.Avatar = uuid.NewString() | ||||
|  | ||||
| 	if err := c.SaveFile(file, user.GetAvatarPath()); err != nil { | ||||
| 		return err | ||||
| 	} else { | ||||
| 		database.C.Save(&user) | ||||
| 	} | ||||
|  | ||||
| 	return c.SendStatus(fiber.StatusOK) | ||||
| } | ||||
| @@ -57,6 +57,9 @@ func NewServer() { | ||||
|  | ||||
| 	api := A.Group("/api").Name("API") | ||||
| 	{ | ||||
| 		api.Get("/avatar/:avatarId", getAvatar) | ||||
| 		api.Put("/avatar", auth, setAvatar) | ||||
|  | ||||
| 		api.Get("/users/me", auth, getUserinfo) | ||||
| 		api.Put("/users/me", auth, editUserinfo) | ||||
| 		api.Get("/users/me/events", auth, getEvents) | ||||
|   | ||||
| @@ -8,7 +8,7 @@ export default function PersonalPage() { | ||||
|   const [success, setSuccess] = createSignal<null | string>(null); | ||||
|   const [loading, setLoading] = createSignal(false); | ||||
|  | ||||
|   async function update(evt: SubmitEvent) { | ||||
|   async function updateBasis(evt: SubmitEvent) { | ||||
|     evt.preventDefault(); | ||||
|  | ||||
|     const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement)); | ||||
| @@ -33,6 +33,27 @@ export default function PersonalPage() { | ||||
|     setLoading(false); | ||||
|   } | ||||
|  | ||||
|   async function updateAvatar(evt: SubmitEvent) { | ||||
|     evt.preventDefault(); | ||||
|  | ||||
|     setLoading(true); | ||||
|     const data = new FormData(evt.target as HTMLFormElement); | ||||
|     const res = await fetch("/api/avatar", { | ||||
|       method: "PUT", | ||||
|       headers: { "Authorization": `Bearer ${getAtk()}` }, | ||||
|       body: data | ||||
|     }); | ||||
|     if (res.status !== 200) { | ||||
|       setSuccess(null); | ||||
|       setError(await res.text()); | ||||
|     } else { | ||||
|       await readProfiles(); | ||||
|       setSuccess("Your avatar has been update."); | ||||
|       setError(null); | ||||
|     } | ||||
|     setLoading(false); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div class="max-w-[720px] mx-auto px-5 pt-12"> | ||||
|       <div class="px-5"> | ||||
| @@ -40,12 +61,9 @@ export default function PersonalPage() { | ||||
|         <p>Joined at {new Date(userinfo?.meta?.created_at).toLocaleString()}</p> | ||||
|       </div> | ||||
|  | ||||
|       <div class="card shadow mt-5"> | ||||
|         <div class="card-body"> | ||||
|  | ||||
|           <Show when={error()}> | ||||
|       <div id="alerts"> | ||||
|               <div role="alert" class="alert alert-error"> | ||||
|         <Show when={error()}> | ||||
|           <div role="alert" class="alert alert-error mt-3"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" | ||||
|                  viewBox="0 0 24 24"> | ||||
|               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | ||||
| @@ -53,12 +71,10 @@ export default function PersonalPage() { | ||||
|             </svg> | ||||
|             <span class="capitalize">{error()}</span> | ||||
|           </div> | ||||
|             </div> | ||||
|         </Show> | ||||
|  | ||||
|         <Show when={success()}> | ||||
|             <div id="alerts"> | ||||
|               <div role="alert" class="alert alert-success"> | ||||
|           <div role="alert" class="alert alert-success mt-3"> | ||||
|             <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" | ||||
|                  viewBox="0 0 24 24"> | ||||
|               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | ||||
| @@ -66,10 +82,13 @@ export default function PersonalPage() { | ||||
|             </svg> | ||||
|             <span class="capitalize">{success()}</span> | ||||
|           </div> | ||||
|             </div> | ||||
|         </Show> | ||||
|       </div> | ||||
|  | ||||
|           <form class="grid grid-cols-1 gap-2" onSubmit={update}> | ||||
|       <div class="card shadow-xl mt-5"> | ||||
|  | ||||
|         <div class="card-body border-b border-base-200"> | ||||
|           <form class="grid grid-cols-1 gap-2" onSubmit={updateBasis}> | ||||
|             <label class="form-control w-full"> | ||||
|               <div class="label"> | ||||
|                 <span class="label-text">Username</span> | ||||
| @@ -107,7 +126,30 @@ export default function PersonalPage() { | ||||
|                        placeholder="Type here" class="input input-bordered w-full" /> | ||||
|               </label> | ||||
|             </div> | ||||
|             <button type="submit" class="btn btn-primary btn-block mt-5" disabled={loading()}> | ||||
|  | ||||
|             <div> | ||||
|               <button type="submit" class="btn btn-primary mt-5" disabled={loading()}> | ||||
|                 <Show when={loading()} fallback={"Save changes"}> | ||||
|                   <span class="loading loading-spinner"></span> | ||||
|                 </Show> | ||||
|               </button> | ||||
|             </div> | ||||
|           </form> | ||||
|         </div> | ||||
|  | ||||
|         <div class="card-body"> | ||||
|           <form onSubmit={updateAvatar}> | ||||
|             <label class="form-control w-full"> | ||||
|               <div class="label"> | ||||
|                 <span class="label-text">Pick an avatar</span> | ||||
|               </div> | ||||
|               <input type="file" name="avatar" accept="image/*" class="file-input file-input-bordered w-full" /> | ||||
|               <div class="label"> | ||||
|                 <span class="label-text-alt">Will took some time to apply to entire site</span> | ||||
|               </div> | ||||
|             </label> | ||||
|  | ||||
|             <button type="submit" class="btn btn-primary mt-5" disabled={loading()}> | ||||
|               <Show when={loading()} fallback={"Save changes"}> | ||||
|                 <span class="loading loading-spinner"></span> | ||||
|               </Show> | ||||
|   | ||||
| @@ -7,6 +7,8 @@ bind = "0.0.0.0:8444" | ||||
| domain = "id.smartsheep.studio" | ||||
| secret = "LtTjzAGFLshwXhN4ZD4nG5KlMv1MWcsvfv03TSZYnT1VhiAnLIZFTnHUwR0XhGgi" | ||||
|  | ||||
| content = "uploads" | ||||
|  | ||||
| use_registration_magic_token = true | ||||
|  | ||||
| [mailer] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user