Migrate profile/account deletion

This commit is contained in:
Daniel J. Summers 2023-01-15 15:50:23 -05:00
parent 415cbbf650
commit 3593d84fe6
6 changed files with 67 additions and 102 deletions

View File

@ -74,19 +74,6 @@ const routes: Array<RouteRecordRaw> = [
component: () => import(/* webpackChunkName: "joblist" */ "../views/listing/MyListings.vue"),
meta: { auth: true, title: "My Job Listings" }
},
// "So Long" URLs
{
path: "/so-long/options",
name: "DeletionOptions",
component: () => import(/* webpackChunkName: "so-long" */ "../views/so-long/DeletionOptions.vue"),
meta: { auth: true, title: "Account Deletion Options" }
},
{
path: "/so-long/success",
name: "DeletionSuccess",
component: () => import(/* webpackChunkName: "so-long" */ "../views/so-long/DeletionSuccess.vue"),
meta: { auth: false, title: "Account Deletion Success" }
},
// Success Story URLs
{
path: "/success-story/list",

View File

@ -1,61 +0,0 @@
<template>
<article>
<h3 class="pb-3">Account Deletion Options</h3>
<h4 class="pb-3">Option 1 &ndash; Delete Your Profile</h4>
<p>
Utilizing this option will remove your current employment profile and skills. This will preserve any job listings
you may have posted, or any success stories you may have written, and preserves this application&rsquo;s knowledge
of you. This is what you want to use if you want to clear out your profile and start again (and remove the current
one from others&rsquo; view).
</p>
<p class="text-center">
<button class="btn btn-danger" @click.prevent="deleteProfile">Delete Your Profile</button>
</p>
<hr>
<h4 class="pb-3">Option 2 &ndash; Delete Your Account</h4>
<p>
This option will make it like you never visited this site. It will delete your profile, skills, job listings,
success stories, and account. This is what you want to use if you want to disappear from this application.
</p>
<p class="text-center">
<button class="btn btn-danger" @click.prevent="deleteAccount">Delete Your Entire Account</button>
</p>
</article>
</template>
<script setup lang="ts">
import { useRouter } from "vue-router"
import api, { LogOnSuccess } from "@/api"
import { toastError, toastSuccess } from "@/components/layout/AppToaster.vue"
import { useStore, Mutations } from "@/store"
const store = useStore()
const router = useRouter()
/** The currently logged-on user */
const user = store.state.user as LogOnSuccess
/** Delete the profile only; redirect to home page on success */
const deleteProfile = async () => {
const resp = await api.profile.delete(user)
if (typeof resp === "string") {
toastError(resp, "Deleting Profile")
} else {
toastSuccess("Profile Deleted Successfully")
await router.push("/citizen/dashboard")
}
}
/** Delete everything pertaining to the user's account */
const deleteAccount = async () => {
const resp = await api.citizen.delete(user)
if (typeof resp === "string") {
toastError(resp, "Deleting Account")
} else {
store.commit(Mutations.ClearUser)
toastSuccess("Account Deleted Successfully")
await router.push("/so-long/success")
}
}
</script>

View File

@ -1,9 +0,0 @@
<template>
<article>
<h3 class="pb-3">Account Deletion Success</h3>
<p>&nbsp;</p>
<p>Your account has been successfully deleted.</p>
<p>&nbsp;</p>
<p>Thank you for participating, and thank you for your courage. #GitmoNation</p>
</article>
</template>

View File

@ -289,6 +289,13 @@ module Citizen =
return! Citizen.dashboard citizen.Value profile prfCount |> render "Dashboard" next ctx
}
// POST: /citizen/delete
let delete : HttpHandler = requireUser >=> validateCsrf >=> fun next ctx -> task {
do! Citizens.deleteById (currentCitizenId ctx)
do! ctx.SignOutAsync ()
return! render "Account Deleted Successfully" next ctx Citizen.deleted
}
// GET: /citizen/deny/[token]
let deny token next ctx = task {
let! wasDeleted = Citizens.denyAccount token
@ -402,6 +409,10 @@ module Citizen =
return! refreshPage () next ctx
}
// GET: /citizen/so-long
let soLong : HttpHandler = requireUser >=> fun next ctx ->
Citizen.deletionOptions (csrf ctx) |> render "Account Deletion Options" next ctx
/// Handlers for /api/citizen routes
[<RequireQualifiedAccess>]
@ -442,12 +453,6 @@ module CitizenApi =
| None -> return! Error.notFound next ctx
}
// DELETE: /api/citizen
let delete : HttpHandler = authorize >=> fun next ctx -> task {
do! Citizens.deleteById (currentCitizenId ctx)
return! ok next ctx
}
/// Handlers for /api/continent routes
[<RequireQualifiedAccess>]
@ -590,6 +595,13 @@ module Listing =
[<RequireQualifiedAccess>]
module Profile =
// POST: /profile/delete
let delete : HttpHandler = requireUser >=> validateCsrf >=> fun next ctx -> task {
do! Profiles.deleteById (currentCitizenId ctx)
do! addSuccess "Profile deleted successfully" ctx
return! redirectToGet "/citizen/dashboard" next ctx
}
// GET: /profile/edit
let edit : HttpHandler = requireUser >=> fun next ctx -> task {
let citizenId = currentCitizenId ctx
@ -743,12 +755,6 @@ module ProfileApi =
| None -> return! Error.notFound next ctx
}
// DELETE: /api/profile
let delete : HttpHandler = authorize >=> fun next ctx -> task {
do! Profiles.deleteById (currentCitizenId ctx)
return! ok next ctx
}
/// Handlers for /api/success routes
[<RequireQualifiedAccess>]
@ -811,8 +817,10 @@ let allEndpoints = [
route "/log-off" Citizen.logOff
route "/log-on" Citizen.logOn
route "/register" Citizen.register
route "/so-long" Citizen.soLong
]
POST [
route "/delete" Citizen.delete
route "/log-on" Citizen.doLogOn
route "/register" Citizen.doRegistration
]
@ -826,7 +834,10 @@ let allEndpoints = [
route "/search" Profile.search
route "/seeking" Profile.seeking
]
POST [ route "/save" Profile.save ]
POST [
route "/delete" Profile.delete
route "/save" Profile.save
]
]
GET_HEAD [ route "/terms-of-service" Home.termsOfService ]
@ -836,9 +847,6 @@ let allEndpoints = [
PATCH [
route "/account" CitizenApi.account
]
DELETE [
route "" CitizenApi.delete
]
]
GET_HEAD [ route "/continents" Continent.all ]
subRoute "/listing" [

View File

@ -94,6 +94,46 @@ let dashboard (citizen : Citizen) (profile : Profile option) profileCount =
]
/// The account deletion success page
let deleted =
article [] [
h3 [ _class "pb-3" ] [ rawText "Account Deletion Success" ]
p [] [ rawText "&nbsp;" ]
p [] [ rawText "Your account has been successfully deleted." ]
p [] [ rawText "&nbsp;" ]
p [] [ rawText "Thank you for participating, and thank you for your courage. #GitmoNation" ]
]
/// The profile or account deletion page
let deletionOptions csrf =
article [] [
h3 [ _class "pb-3" ] [ rawText "Account Deletion Options" ]
h4 [ _class "pb-3" ] [ rawText "Option 1 &ndash; Delete Your Profile" ]
p [] [
rawText "Utilizing this option will remove your current employment profile and skills. This will preserve "
rawText "any job listings you may have posted, or any success stories you may have written, and preserves "
rawText "this application&rsquo;s knowledge of you. This is what you want to use if you want to clear out "
rawText "your profile and start again (and remove the current one from others&rsquo; view)."
]
form [ _class "text-center"; _method "POST"; _action "/profile/delete" ] [
antiForgery csrf
button [ _type "submit"; _class "btn btn-danger" ] [ rawText "Delete Your Profile" ]
]
hr []
h4 [ _class "pb-3" ] [ rawText "Option 2 &ndash; Delete Your Account" ]
p [] [
rawText "This option will make it like you never visited this site. It will delete your profile, skills, "
rawText "job listings, success stories, and account. This is what you want to use if you want to disappear "
rawText "from this application."
]
form [ _class "text-center"; _method "POST"; _action "/citizen/delete" ] [
antiForgery csrf
button [ _type "submit"; _class "btn btn-danger" ] [ rawText "Delete Your Entire Account" ]
]
]
/// The account denial page
let denyAccount wasDeleted =
article [] [
@ -179,11 +219,11 @@ let register q1 q2 (m : RegisterViewModel) csrf =
]
]
div [ _class "col-12 col-xl-6" ] [
textBox [ _type "text"; _maxlength "30" ] (nameof m.Question1Answer) m.Question1Answer "Question 1" true
textBox [ _type "text"; _maxlength "30" ] (nameof m.Question1Answer) m.Question1Answer q1 true
input [ _type "hidden"; _name (nameof m.Question1Index); _value (string m.Question1Index ) ]
]
div [ _class "col-12 col-xl-6" ] [
textBox [ _type "text"; _maxlength "30" ] (nameof m.Question2Answer) m.Question2Answer "Question 2" true
textBox [ _type "text"; _maxlength "30" ] (nameof m.Question2Answer) m.Question2Answer q2 true
input [ _type "hidden"; _name (nameof m.Question2Index); _value (string m.Question2Index ) ]
]
div [ _class "col-12" ] [

View File

@ -117,7 +117,7 @@ let edit (m : EditProfileViewModel) continents isNew citizenId csrf =
hr []
p [ _class "text-muted fst-italic" ] [
rawText "(If you want to delete your profile, or your entire account, "
a [ _href "/so-long/options" ] [ rawText "see your deletion options here" ]; rawText ".)"
a [ _href "/citizen/so-long" ] [ rawText "see your deletion options here" ]; rawText ".)"
]
script [] [
rawText """addEventListener("DOMContentLoaded", function () {"""