feat: add mod action notice to login page
This commit is contained in:
parent
f02e64fca7
commit
ab77fab0ea
|
@ -43,8 +43,10 @@ type discordCallbackResponse struct {
|
||||||
Ticket string `json:"ticket,omitempty"`
|
Ticket string `json:"ticket,omitempty"`
|
||||||
RequireInvite bool `json:"require_invite"` // require an invite for signing up
|
RequireInvite bool `json:"require_invite"` // require an invite for signing up
|
||||||
|
|
||||||
IsDeleted bool `json:"is_deleted"`
|
IsDeleted bool `json:"is_deleted"`
|
||||||
DeletedAt *time.Time `json:"deleted_at,omitempty"`
|
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 {
|
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)
|
u, err := s.DB.DiscordUser(ctx, du.ID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if u.DeletedAt != nil && *u.SelfDelete {
|
if u.DeletedAt != nil {
|
||||||
// store cancel delete token
|
// store cancel delete token
|
||||||
token := undeleteToken()
|
token := undeleteToken()
|
||||||
err = s.saveUndeleteToken(ctx, u.ID, token)
|
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{
|
render.JSON(w, r, discordCallbackResponse{
|
||||||
HasAccount: true,
|
HasAccount: true,
|
||||||
Token: token,
|
Token: token,
|
||||||
User: dbUserToUserResponse(u, []db.Field{}),
|
User: dbUserToUserResponse(u, []db.Field{}),
|
||||||
IsDeleted: true,
|
IsDeleted: true,
|
||||||
DeletedAt: u.DeletedAt,
|
DeletedAt: u.DeletedAt,
|
||||||
|
SelfDelete: u.SelfDelete,
|
||||||
|
DeleteReason: u.DeleteReason,
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,10 @@ type fediCallbackResponse struct {
|
||||||
Ticket string `json:"ticket,omitempty"`
|
Ticket string `json:"ticket,omitempty"`
|
||||||
RequireInvite bool `json:"require_invite"` // require an invite for signing up
|
RequireInvite bool `json:"require_invite"` // require an invite for signing up
|
||||||
|
|
||||||
IsDeleted bool `json:"is_deleted"`
|
IsDeleted bool `json:"is_deleted"`
|
||||||
DeletedAt *time.Time `json:"deleted_at,omitempty"`
|
DeletedAt *time.Time `json:"deleted_at,omitempty"`
|
||||||
|
SelfDelete *bool `json:"self_delete,omitempty"`
|
||||||
|
DeleteReason *string `json:"delete_reason,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type partialMastodonAccount struct {
|
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)
|
u, err := s.DB.FediverseUser(ctx, mu.ID, app.ID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if u.DeletedAt != nil && *u.SelfDelete {
|
if u.DeletedAt != nil {
|
||||||
// store cancel delete token
|
// store cancel delete token
|
||||||
token := undeleteToken()
|
token := undeleteToken()
|
||||||
err = s.saveUndeleteToken(ctx, u.ID, token)
|
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{
|
render.JSON(w, r, fediCallbackResponse{
|
||||||
HasAccount: true,
|
HasAccount: true,
|
||||||
Token: token,
|
Token: token,
|
||||||
User: dbUserToUserResponse(u, []db.Field{}),
|
User: dbUserToUserResponse(u, []db.Field{}),
|
||||||
IsDeleted: true,
|
IsDeleted: true,
|
||||||
DeletedAt: u.DeletedAt,
|
DeletedAt: u.DeletedAt,
|
||||||
|
SelfDelete: u.SelfDelete,
|
||||||
|
DeleteReason: u.DeleteReason,
|
||||||
})
|
})
|
||||||
return nil
|
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
|
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)
|
err = s.DB.UndoDeleteUser(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("executing undelete query: %v", err)
|
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 {
|
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)
|
err = s.DB.CleanUser(ctx, report.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("cleaning user data: %v", err)
|
log.Errorf("cleaning user data: %v", err)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import ErrorAlert from "$lib/components/ErrorAlert.svelte";
|
import ErrorAlert from "$lib/components/ErrorAlert.svelte";
|
||||||
import { userStore } from "$lib/store";
|
import { userStore } from "$lib/store";
|
||||||
import { addToast } from "$lib/toast";
|
import { addToast } from "$lib/toast";
|
||||||
|
import { DateTime } from "luxon";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
|
@ -26,6 +27,8 @@
|
||||||
export let token: string | undefined;
|
export let token: string | undefined;
|
||||||
export let user: MeUser | undefined;
|
export let user: MeUser | undefined;
|
||||||
export let deletedAt: string | undefined;
|
export let deletedAt: string | undefined;
|
||||||
|
export let selfDelete: boolean | undefined;
|
||||||
|
export let deleteReason: string | undefined;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (!isDeleted && token && user) {
|
if (!isDeleted && token && user) {
|
||||||
|
@ -136,8 +139,12 @@
|
||||||
</div>
|
</div>
|
||||||
<Button type="submit" color="primary">Sign up</Button>
|
<Button type="submit" color="primary">Sign up</Button>
|
||||||
</form>
|
</form>
|
||||||
{:else if isDeleted && token}
|
{:else if isDeleted && token && selfDelete && deletedAt}
|
||||||
<p>Your account is pending deletion since {deletedAt}.</p>
|
<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>If you wish to cancel deletion, press the button below.</p>
|
||||||
<p>
|
<p>
|
||||||
<Button color="primary" on:click={cancelDelete} disabled={deleteCancelled}
|
<Button color="primary" on:click={cancelDelete} disabled={deleteCancelled}
|
||||||
|
@ -152,34 +159,6 @@
|
||||||
<p>
|
<p>
|
||||||
<Button color="link" on:click={toggleForceDeleteModal}>Force delete account</Button>
|
<Button color="link" on:click={toggleForceDeleteModal}>Force delete account</Button>
|
||||||
</p>
|
</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}
|
{#if deleteCancelled}
|
||||||
<Alert color="secondary" fade={false}>
|
<Alert color="secondary" fade={false}>
|
||||||
Account deletion cancelled! You can now <a href="/auth/login">log in</a> again.
|
Account deletion cancelled! You can now <a href="/auth/login">log in</a> again.
|
||||||
|
@ -188,6 +167,50 @@
|
||||||
{#if deleteError}
|
{#if deleteError}
|
||||||
<ErrorAlert error={deleteError} />
|
<ErrorAlert error={deleteError} />
|
||||||
{/if}
|
{/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}
|
{:else}
|
||||||
Loading...
|
Loading...
|
||||||
{/if}
|
{/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;
|
is_deleted: boolean;
|
||||||
deleted_at?: string;
|
deleted_at?: string;
|
||||||
|
self_delete?: boolean;
|
||||||
|
delete_reason?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,8 @@
|
||||||
token={data.token}
|
token={data.token}
|
||||||
user={data.user}
|
user={data.user}
|
||||||
deletedAt={data.deleted_at}
|
deletedAt={data.deleted_at}
|
||||||
|
selfDelete={data.self_delete}
|
||||||
|
deleteReason={data.delete_reason}
|
||||||
{linkAccount}
|
{linkAccount}
|
||||||
{signupForm}
|
{signupForm}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -33,4 +33,6 @@ interface CallbackResponse {
|
||||||
|
|
||||||
is_deleted: boolean;
|
is_deleted: boolean;
|
||||||
deleted_at?: string;
|
deleted_at?: string;
|
||||||
|
self_delete?: boolean;
|
||||||
|
delete_reason?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,8 @@
|
||||||
token={data.token}
|
token={data.token}
|
||||||
user={data.user}
|
user={data.user}
|
||||||
deletedAt={data.deleted_at}
|
deletedAt={data.deleted_at}
|
||||||
|
selfDelete={data.self_delete}
|
||||||
|
deleteReason={data.delete_reason}
|
||||||
{linkAccount}
|
{linkAccount}
|
||||||
{signupForm}
|
{signupForm}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -48,14 +48,16 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{#each data.reports as report, index}
|
{#each data.reports as report, index}
|
||||||
<ReportCard {report}>
|
<div class="my-2">
|
||||||
•
|
<ReportCard {report}>
|
||||||
<Button outline color="warning" size="sm" on:click={() => openWarnModalFor(index)}
|
•
|
||||||
>Warn user</Button
|
<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>
|
<Button outline color="danger" size="sm">Deactivate user</Button>
|
||||||
</ReportCard>
|
<Button outline color="secondary" size="sm">Ignore report</Button>
|
||||||
|
</ReportCard>
|
||||||
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue