feat: add mod action notice to login page
This commit is contained in:
		
							parent
							
								
									f02e64fca7
								
							
						
					
					
						commit
						ab77fab0ea
					
				
					 10 changed files with 110 additions and 54 deletions
				
			
		|  | @ -43,8 +43,10 @@ type discordCallbackResponse struct { | |||
| 	Ticket        string `json:"ticket,omitempty"` | ||||
| 	RequireInvite bool   `json:"require_invite"` // require an invite for signing up | ||||
| 
 | ||||
| 	IsDeleted bool       `json:"is_deleted"` | ||||
| 	DeletedAt *time.Time `json:"deleted_at,omitempty"` | ||||
| 	IsDeleted    bool       `json:"is_deleted"` | ||||
| 	DeletedAt    *time.Time `json:"deleted_at,omitempty"` | ||||
| 	SelfDelete   *bool      `json:"self_delete,omitempty"` | ||||
| 	DeleteReason *string    `json:"delete_reason,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (s *Server) discordCallback(w http.ResponseWriter, r *http.Request) error { | ||||
|  | @ -81,7 +83,7 @@ func (s *Server) discordCallback(w http.ResponseWriter, r *http.Request) error { | |||
| 
 | ||||
| 	u, err := s.DB.DiscordUser(ctx, du.ID) | ||||
| 	if err == nil { | ||||
| 		if u.DeletedAt != nil && *u.SelfDelete { | ||||
| 		if u.DeletedAt != nil { | ||||
| 			// store cancel delete token | ||||
| 			token := undeleteToken() | ||||
| 			err = s.saveUndeleteToken(ctx, u.ID, token) | ||||
|  | @ -91,11 +93,13 @@ func (s *Server) discordCallback(w http.ResponseWriter, r *http.Request) error { | |||
| 			} | ||||
| 
 | ||||
| 			render.JSON(w, r, discordCallbackResponse{ | ||||
| 				HasAccount: true, | ||||
| 				Token:      token, | ||||
| 				User:       dbUserToUserResponse(u, []db.Field{}), | ||||
| 				IsDeleted:  true, | ||||
| 				DeletedAt:  u.DeletedAt, | ||||
| 				HasAccount:   true, | ||||
| 				Token:        token, | ||||
| 				User:         dbUserToUserResponse(u, []db.Field{}), | ||||
| 				IsDeleted:    true, | ||||
| 				DeletedAt:    u.DeletedAt, | ||||
| 				SelfDelete:   u.SelfDelete, | ||||
| 				DeleteReason: u.DeleteReason, | ||||
| 			}) | ||||
| 			return nil | ||||
| 		} | ||||
|  |  | |||
|  | @ -31,8 +31,10 @@ type fediCallbackResponse struct { | |||
| 	Ticket        string `json:"ticket,omitempty"` | ||||
| 	RequireInvite bool   `json:"require_invite"` // require an invite for signing up | ||||
| 
 | ||||
| 	IsDeleted bool       `json:"is_deleted"` | ||||
| 	DeletedAt *time.Time `json:"deleted_at,omitempty"` | ||||
| 	IsDeleted    bool       `json:"is_deleted"` | ||||
| 	DeletedAt    *time.Time `json:"deleted_at,omitempty"` | ||||
| 	SelfDelete   *bool      `json:"self_delete,omitempty"` | ||||
| 	DeleteReason *string    `json:"delete_reason,omitempty"` | ||||
| } | ||||
| 
 | ||||
| type partialMastodonAccount struct { | ||||
|  | @ -102,7 +104,7 @@ func (s *Server) mastodonCallback(w http.ResponseWriter, r *http.Request) error | |||
| 
 | ||||
| 	u, err := s.DB.FediverseUser(ctx, mu.ID, app.ID) | ||||
| 	if err == nil { | ||||
| 		if u.DeletedAt != nil && *u.SelfDelete { | ||||
| 		if u.DeletedAt != nil { | ||||
| 			// store cancel delete token | ||||
| 			token := undeleteToken() | ||||
| 			err = s.saveUndeleteToken(ctx, u.ID, token) | ||||
|  | @ -112,11 +114,13 @@ func (s *Server) mastodonCallback(w http.ResponseWriter, r *http.Request) error | |||
| 			} | ||||
| 
 | ||||
| 			render.JSON(w, r, fediCallbackResponse{ | ||||
| 				HasAccount: true, | ||||
| 				Token:      token, | ||||
| 				User:       dbUserToUserResponse(u, []db.Field{}), | ||||
| 				IsDeleted:  true, | ||||
| 				DeletedAt:  u.DeletedAt, | ||||
| 				HasAccount:   true, | ||||
| 				Token:        token, | ||||
| 				User:         dbUserToUserResponse(u, []db.Field{}), | ||||
| 				IsDeleted:    true, | ||||
| 				DeletedAt:    u.DeletedAt, | ||||
| 				SelfDelete:   u.SelfDelete, | ||||
| 				DeleteReason: u.DeleteReason, | ||||
| 			}) | ||||
| 			return nil | ||||
| 		} | ||||
|  |  | |||
|  | @ -27,6 +27,16 @@ func (s *Server) cancelDelete(w http.ResponseWriter, r *http.Request) error { | |||
| 		return server.APIError{Code: server.ErrNotFound} // assume invalid token | ||||
| 	} | ||||
| 
 | ||||
| 	// only self deleted users can undelete themselves | ||||
| 	u, err := s.DB.User(ctx, id) | ||||
| 	if err != nil { | ||||
| 		log.Errorf("getting user: %v", err) | ||||
| 		return errors.Wrap(err, "getting user") | ||||
| 	} | ||||
| 	if !*u.SelfDelete { | ||||
| 		return server.APIError{Code: server.ErrForbidden} | ||||
| 	} | ||||
| 
 | ||||
| 	err = s.DB.UndoDeleteUser(ctx, id) | ||||
| 	if err != nil { | ||||
| 		log.Errorf("executing undelete query: %v", err) | ||||
|  |  | |||
|  | @ -77,6 +77,11 @@ func (s *Server) resolveReport(w http.ResponseWriter, r *http.Request) error { | |||
| 		} | ||||
| 
 | ||||
| 		if req.Delete { | ||||
| 			err = s.DB.InvalidateAllTokens(ctx, tx, report.UserID) | ||||
| 			if err != nil { | ||||
| 				return errors.Wrap(err, "invalidating tokens") | ||||
| 			} | ||||
| 
 | ||||
| 			err = s.DB.CleanUser(ctx, report.UserID) | ||||
| 			if err != nil { | ||||
| 				log.Errorf("cleaning user data: %v", err) | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
|   import ErrorAlert from "$lib/components/ErrorAlert.svelte"; | ||||
|   import { userStore } from "$lib/store"; | ||||
|   import { addToast } from "$lib/toast"; | ||||
|   import { DateTime } from "luxon"; | ||||
|   import { onMount } from "svelte"; | ||||
|   import { | ||||
|     Alert, | ||||
|  | @ -26,6 +27,8 @@ | |||
|   export let token: string | undefined; | ||||
|   export let user: MeUser | undefined; | ||||
|   export let deletedAt: string | undefined; | ||||
|   export let selfDelete: boolean | undefined; | ||||
|   export let deleteReason: string | undefined; | ||||
| 
 | ||||
|   onMount(() => { | ||||
|     if (!isDeleted && token && user) { | ||||
|  | @ -136,8 +139,12 @@ | |||
|     </div> | ||||
|     <Button type="submit" color="primary">Sign up</Button> | ||||
|   </form> | ||||
| {:else if isDeleted && token} | ||||
|   <p>Your account is pending deletion since {deletedAt}.</p> | ||||
| {:else if isDeleted && token && selfDelete && deletedAt} | ||||
|   <p> | ||||
|     Your account is pending deletion since {DateTime.fromISO(deletedAt) | ||||
|       .toLocal() | ||||
|       .toLocaleString(DateTime.DATETIME_MED)}. | ||||
|   </p> | ||||
|   <p>If you wish to cancel deletion, press the button below.</p> | ||||
|   <p> | ||||
|     <Button color="primary" on:click={cancelDelete} disabled={deleteCancelled} | ||||
|  | @ -152,34 +159,6 @@ | |||
|   <p> | ||||
|     <Button color="link" on:click={toggleForceDeleteModal}>Force delete account</Button> | ||||
|   </p> | ||||
|   <Modal | ||||
|     header="Force delete account" | ||||
|     isOpen={forceDeleteModalOpen} | ||||
|     toggle={toggleForceDeleteModal} | ||||
|   > | ||||
|     <ModalBody> | ||||
|       <p> | ||||
|         If you want to delete your account, type your username below: | ||||
|         <br /> | ||||
|         <b> | ||||
|           This is irreversible! Your account <i>cannot</i> be recovered after you press "Force delete | ||||
|           account". | ||||
|         </b> | ||||
|       </p> | ||||
|       <p> | ||||
|         <input type="text" class="form-control" bind:value={forceDeleteName} /> | ||||
|       </p> | ||||
|       {#if deleteError} | ||||
|         <ErrorAlert error={deleteError} /> | ||||
|       {/if} | ||||
|     </ModalBody> | ||||
|     <ModalFooter> | ||||
|       <Button color="danger" on:click={forceDeleteAccount} disabled={forceDeleteName !== user?.name} | ||||
|         >Force delete account</Button | ||||
|       > | ||||
|       <Button color="secondary" on:click={toggleForceDeleteModal}>Cancel delete</Button> | ||||
|     </ModalFooter> | ||||
|   </Modal> | ||||
|   {#if deleteCancelled} | ||||
|     <Alert color="secondary" fade={false}> | ||||
|       Account deletion cancelled! You can now <a href="/auth/login">log in</a> again. | ||||
|  | @ -188,6 +167,50 @@ | |||
|   {#if deleteError} | ||||
|     <ErrorAlert error={deleteError} /> | ||||
|   {/if} | ||||
| {:else if isDeleted && token && !selfDelete && deletedAt} | ||||
|   <p> | ||||
|     Your account is pending deletion since {DateTime.fromISO(deletedAt) | ||||
|       .toLocal() | ||||
|       .toLocaleString(DateTime.DATETIME_MED)}. | ||||
|   </p> | ||||
|   <p> | ||||
|     <strong>Your account was deactivated by a moderator.</strong> You cannot cancel deletion. The moderator | ||||
|     gave the following reason: | ||||
|   </p> | ||||
|   <blockquote class="blockquote"> | ||||
|     {deleteReason} | ||||
|   </blockquote> | ||||
|   <p> | ||||
|     Your account will be fully deleted 180 days after being deactivated. If you want your data wiped | ||||
|     immediately instead, press the force delete link below. | ||||
|   </p> | ||||
|   <p> | ||||
|     <Button color="link" on:click={toggleForceDeleteModal}>Force delete account</Button> | ||||
|   </p> | ||||
| {:else} | ||||
|   Loading... | ||||
| {/if} | ||||
| 
 | ||||
| <Modal header="Force delete account" isOpen={forceDeleteModalOpen} toggle={toggleForceDeleteModal}> | ||||
|   <ModalBody> | ||||
|     <p> | ||||
|       If you want to delete your account, type your username (<code>{user.name}</code>) below: | ||||
|       <br /> | ||||
|       <b> | ||||
|         This is irreversible! Your account <i>cannot</i> be recovered after you press "Force delete account". | ||||
|       </b> | ||||
|     </p> | ||||
|     <p> | ||||
|       <input type="text" class="form-control" bind:value={forceDeleteName} /> | ||||
|     </p> | ||||
|     {#if deleteError} | ||||
|       <ErrorAlert error={deleteError} /> | ||||
|     {/if} | ||||
|   </ModalBody> | ||||
|   <ModalFooter> | ||||
|     <Button color="danger" on:click={forceDeleteAccount} disabled={forceDeleteName !== user?.name} | ||||
|       >Force delete account</Button | ||||
|     > | ||||
|     <Button color="secondary" on:click={toggleForceDeleteModal}>Cancel delete</Button> | ||||
|   </ModalFooter> | ||||
| </Modal> | ||||
|  |  | |||
|  | @ -33,4 +33,6 @@ interface CallbackResponse { | |||
| 
 | ||||
|   is_deleted: boolean; | ||||
|   deleted_at?: string; | ||||
|   self_delete?: boolean; | ||||
|   delete_reason?: string; | ||||
| } | ||||
|  |  | |||
|  | @ -57,6 +57,8 @@ | |||
|   token={data.token} | ||||
|   user={data.user} | ||||
|   deletedAt={data.deleted_at} | ||||
|   selfDelete={data.self_delete} | ||||
|   deleteReason={data.delete_reason} | ||||
|   {linkAccount} | ||||
|   {signupForm} | ||||
| /> | ||||
|  |  | |||
|  | @ -33,4 +33,6 @@ interface CallbackResponse { | |||
| 
 | ||||
|   is_deleted: boolean; | ||||
|   deleted_at?: string; | ||||
|   self_delete?: boolean; | ||||
|   delete_reason?: string; | ||||
| } | ||||
|  |  | |||
|  | @ -59,6 +59,8 @@ | |||
|   token={data.token} | ||||
|   user={data.user} | ||||
|   deletedAt={data.deleted_at} | ||||
|   selfDelete={data.self_delete} | ||||
|   deleteReason={data.delete_reason} | ||||
|   {linkAccount} | ||||
|   {signupForm} | ||||
| /> | ||||
|  |  | |||
|  | @ -48,14 +48,16 @@ | |||
| 
 | ||||
|   <div> | ||||
|     {#each data.reports as report, index} | ||||
|       <ReportCard {report}> | ||||
|         • | ||||
|         <Button outline color="warning" size="sm" on:click={() => openWarnModalFor(index)} | ||||
|           >Warn user</Button | ||||
|         > | ||||
|         <Button outline color="danger" size="sm">Deactivate user</Button> | ||||
|         <Button outline color="secondary" size="sm">Ignore report</Button> | ||||
|       </ReportCard> | ||||
|       <div class="my-2"> | ||||
|         <ReportCard {report}> | ||||
|           • | ||||
|           <Button outline color="warning" size="sm" on:click={() => openWarnModalFor(index)} | ||||
|             >Warn user</Button | ||||
|           > | ||||
|           <Button outline color="danger" size="sm">Deactivate user</Button> | ||||
|           <Button outline color="secondary" size="sm">Ignore report</Button> | ||||
|         </ReportCard> | ||||
|       </div> | ||||
|     {/each} | ||||
|   </div> | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue