Merge pull request #68 from jcreek/55-enable-account-closure

55 enable account closure
This commit is contained in:
Olly
2024-11-06 20:51:30 +00:00
committed by GitHub
12 changed files with 553 additions and 13 deletions
+7 -1
View File
@@ -5,4 +5,10 @@ STRIPE_SECRET_KEY=""
STRIPE_ENDPOINT_SECRET=""
VITE_PRODUCT_ID_EXAMPLEPRODUCT=""
VITE_AXIOM_DATASET=""
VITE_AXIOM_TOKEN=""
VITE_AXIOM_TOKEN=""
# Brevo API configuration for transactional emails (account closure notifications)
VITE_BREVO_API_KEY=""
# Default sender email for system notifications
VITE_BREVO_SENDER_EMAIL="noreply@example.com"
# Display name for the sender email
VITE_BREVO_SENDER_NAME="No Reply"
+9 -1
View File
@@ -79,7 +79,15 @@ There are two options to create a migration file:
## Applying migrations
Reset database in cloud
To do so without resetting the database, you can use the following command:
`npx supabase migration up`
> Use this command when you want to preserve existing data whilst applying new schema changes. For a fresh start with test data, use `npx supabase db reset` instead.
And for the database in the cloud:
`npx supabase db push`
> Prerequisite login using `npx supabase login`
+1
View File
@@ -48,6 +48,7 @@
"type": "module",
"dependencies": {
"@axiomhq/winston": "^1.2.0",
"@getbrevo/brevo": "^2.2.0",
"@supabase/ssr": "^0.4.0",
"@supabase/supabase-js": "^2.44.2",
"stripe": "^15.4.0",
+309
View File
@@ -11,6 +11,9 @@ importers:
'@axiomhq/winston':
specifier: ^1.2.0
version: 1.2.0
'@getbrevo/brevo':
specifier: ^2.2.0
version: 2.2.0
'@supabase/ssr':
specifier: ^0.4.0
version: 0.4.1(@supabase/supabase-js@2.45.4)
@@ -886,6 +889,9 @@ packages:
resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
'@getbrevo/brevo@2.2.0':
resolution: {integrity: sha512-mNCkXtgqn6jqLglAx1JzZcTj53kBZ5dK9Yd6zVuEyXAvhz68f5Ps6dSJar9HvkHH0Lfr3NrqW76xurjcoxqhIg==}
'@humanwhocodes/config-array@0.13.0':
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
engines: {node: '>=10.10.0'}
@@ -1367,9 +1373,19 @@ packages:
resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
engines: {node: '>= 0.4'}
asn1@0.2.6:
resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
assert-plus@1.0.0:
resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
engines: {node: '>=0.8'}
async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
at-least-node@1.0.0:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
engines: {node: '>= 4.0.0'}
@@ -1385,6 +1401,12 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
aws-sign2@0.7.0:
resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
aws4@1.13.2:
resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==}
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
engines: {node: '>= 0.4'}
@@ -1428,6 +1450,9 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
bcrypt-pbkdf@1.0.2:
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
bin-links@4.0.4:
resolution: {integrity: sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -1439,6 +1464,9 @@ packages:
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
@@ -1490,6 +1518,9 @@ packages:
caniuse-lite@1.0.30001660:
resolution: {integrity: sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==}
caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@@ -1549,6 +1580,10 @@ packages:
colorspace@1.1.4:
resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
commander@12.1.0:
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
engines: {node: '>=18'}
@@ -1584,6 +1619,9 @@ packages:
core-js-compat@3.38.1:
resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==}
core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -1612,6 +1650,10 @@ packages:
resolution: {integrity: sha512-jp1RAuzbHhGdXmn957Z2XsTZStXGHzFfF0FgIOZj3Wv9sH7OZgLfXTRZNfKVYxltGUOBsG1kbWAdF5SrqjebvA==}
engines: {node: '>=16.9.0'}
dashdash@1.14.1:
resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
engines: {node: '>=0.10'}
data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
@@ -1671,6 +1713,10 @@ packages:
defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
detect-indent@6.1.0:
resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
engines: {node: '>=8'}
@@ -1706,6 +1752,9 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
ecc-jsbn@0.1.2:
resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
ejs@3.1.10:
resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
engines: {node: '>=0.10.0'}
@@ -1853,6 +1902,13 @@ packages:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
extsprintf@1.3.0:
resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==}
engines: {'0': node >=0.6.0}
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -1928,6 +1984,13 @@ packages:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'}
forever-agent@0.6.1:
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
form-data@2.3.3:
resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
engines: {node: '>= 0.12'}
formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
@@ -1975,6 +2038,9 @@ packages:
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
engines: {node: '>= 0.4'}
getpass@0.1.7:
resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==}
github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
@@ -2030,6 +2096,15 @@ packages:
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
har-schema@2.0.0:
resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==}
engines: {node: '>=4'}
har-validator@5.1.5:
resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==}
engines: {node: '>=6'}
deprecated: this library is no longer supported
has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@@ -2060,6 +2135,10 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
http-signature@1.2.0:
resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
engines: {node: '>=0.8', npm: '>=1.3.7'}
https-proxy-agent@7.0.5:
resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==}
engines: {node: '>= 14'}
@@ -2209,6 +2288,9 @@ packages:
resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
engines: {node: '>= 0.4'}
is-typedarray@1.0.0:
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
is-weakref@1.0.2:
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
@@ -2218,6 +2300,9 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
isstream@0.1.2:
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
@@ -2237,6 +2322,9 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
jsbn@0.1.1:
resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
jsesc@0.5.0:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
hasBin: true
@@ -2261,6 +2349,9 @@ packages:
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
json-stringify-safe@5.0.1:
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
@@ -2273,6 +2364,10 @@ packages:
resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
engines: {node: '>=0.10.0'}
jsprim@1.4.2:
resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
engines: {node: '>=0.6.0'}
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@@ -2360,6 +2455,14 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
@@ -2461,6 +2564,9 @@ packages:
resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
oauth-sign@0.9.0:
resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -2535,6 +2641,9 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
periscopic@3.1.0:
resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
@@ -2677,6 +2786,9 @@ packages:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
engines: {node: '>= 0.6.0'}
psl@1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
pump@3.0.2:
resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==}
@@ -2695,6 +2807,10 @@ packages:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'}
qs@6.5.3:
resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
engines: {node: '>=0.6'}
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -2752,6 +2868,11 @@ packages:
resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
hasBin: true
request@2.88.2:
resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
engines: {node: '>= 6'}
deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
@@ -2768,6 +2889,9 @@ packages:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
rewire@7.0.0:
resolution: {integrity: sha512-DyyNyzwMtGYgu0Zl/ya0PR/oaunM+VuCuBxCuhYJHHaV0V+YvYa3bBGxb5OZ71vndgmp1pYY8F4YOwQo1siRGw==}
rimraf@2.7.1:
resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
deprecated: Rimraf versions prior to v4 are no longer supported
@@ -2814,6 +2938,9 @@ packages:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
sander@0.5.1:
resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==}
@@ -2906,6 +3033,11 @@ packages:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
deprecated: Please use @jridgewell/sourcemap-codec instead
sshpk@1.18.0:
resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==}
engines: {node: '>=0.10.0'}
hasBin: true
stack-trace@0.0.10:
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
@@ -3127,6 +3259,10 @@ packages:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
tough-cookie@2.5.0:
resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
engines: {node: '>=0.8'}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
@@ -3155,6 +3291,9 @@ packages:
tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
tweetnacl@0.14.5:
resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@@ -3237,10 +3376,19 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
uuid@3.4.0:
resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
hasBin: true
uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
verror@1.10.0:
resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
engines: {'0': node >=0.6.0}
vite-plugin-pwa@0.20.5:
resolution: {integrity: sha512-aweuI/6G6n4C5Inn0vwHumElU/UEpNuO+9iZzwPZGTCH87TeZ6YFMrEY6ZUBQdIHHlhTsbMDryFARcSuOdsz9Q==}
engines: {node: '>=16.0.0'}
@@ -4325,6 +4473,14 @@ snapshots:
'@eslint/js@8.57.1': {}
'@getbrevo/brevo@2.2.0':
dependencies:
bluebird: 3.7.2
request: 2.88.2
rewire: 7.0.0
transitivePeerDependencies:
- supports-color
'@humanwhocodes/config-array@0.13.0':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
@@ -4864,8 +5020,16 @@ snapshots:
is-array-buffer: 3.0.4
is-shared-array-buffer: 1.0.3
asn1@0.2.6:
dependencies:
safer-buffer: 2.1.2
assert-plus@1.0.0: {}
async@3.2.6: {}
asynckit@0.4.0: {}
at-least-node@1.0.0: {}
autoprefixer@10.4.20(postcss@8.4.47):
@@ -4882,6 +5046,10 @@ snapshots:
dependencies:
possible-typed-array-names: 1.0.0
aws-sign2@0.7.0: {}
aws4@1.13.2: {}
axobject-query@4.1.0: {}
b4a@1.6.6: {}
@@ -4938,6 +5106,10 @@ snapshots:
base64-js@1.5.1: {}
bcrypt-pbkdf@1.0.2:
dependencies:
tweetnacl: 0.14.5
bin-links@4.0.4:
dependencies:
cmd-shim: 6.0.3
@@ -4953,6 +5125,8 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
bluebird@3.7.2: {}
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
@@ -5005,6 +5179,8 @@ snapshots:
caniuse-lite@1.0.30001660: {}
caseless@0.12.0: {}
chalk@2.4.2:
dependencies:
ansi-styles: 3.2.1
@@ -5078,6 +5254,10 @@ snapshots:
color: 3.2.1
text-hex: 1.0.0
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
commander@12.1.0: {}
commander@2.20.3: {}
@@ -5100,6 +5280,8 @@ snapshots:
dependencies:
browserslist: 4.23.3
core-util-is@1.0.2: {}
cross-spawn@7.0.3:
dependencies:
path-key: 3.1.1
@@ -5131,6 +5313,10 @@ snapshots:
transitivePeerDependencies:
- postcss
dashdash@1.14.1:
dependencies:
assert-plus: 1.0.0
data-uri-to-buffer@4.0.1: {}
data-view-buffer@1.0.1:
@@ -5190,6 +5376,8 @@ snapshots:
defu@6.1.4: {}
delayed-stream@1.0.0: {}
detect-indent@6.1.0: {}
detect-libc@2.0.3: {}
@@ -5216,6 +5404,11 @@ snapshots:
eastasianwidth@0.2.0: {}
ecc-jsbn@0.1.2:
dependencies:
jsbn: 0.1.1
safer-buffer: 2.1.2
ejs@3.1.10:
dependencies:
jake: 10.9.2
@@ -5451,6 +5644,10 @@ snapshots:
expand-template@2.0.3: {}
extend@3.0.2: {}
extsprintf@1.3.0: {}
fast-deep-equal@3.1.3: {}
fast-fifo@1.3.2: {}
@@ -5524,6 +5721,14 @@ snapshots:
cross-spawn: 7.0.3
signal-exit: 4.1.0
forever-agent@0.6.1: {}
form-data@2.3.3:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
formdata-polyfill@4.0.10:
dependencies:
fetch-blob: 3.2.0
@@ -5573,6 +5778,10 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.2.4
getpass@0.1.7:
dependencies:
assert-plus: 1.0.0
github-from-package@0.0.0: {}
glob-parent@5.1.2:
@@ -5641,6 +5850,13 @@ snapshots:
graphemer@1.4.0: {}
har-schema@2.0.0: {}
har-validator@5.1.5:
dependencies:
ajv: 6.12.6
har-schema: 2.0.0
has-bigints@1.0.2: {}
has-flag@3.0.0: {}
@@ -5663,6 +5879,12 @@ snapshots:
dependencies:
function-bind: 1.1.2
http-signature@1.2.0:
dependencies:
assert-plus: 1.0.0
jsprim: 1.4.2
sshpk: 1.18.0
https-proxy-agent@7.0.5:
dependencies:
agent-base: 7.1.1
@@ -5795,6 +6017,8 @@ snapshots:
dependencies:
which-typed-array: 1.1.15
is-typedarray@1.0.0: {}
is-weakref@1.0.2:
dependencies:
call-bind: 1.0.7
@@ -5803,6 +6027,8 @@ snapshots:
isexe@2.0.0: {}
isstream@0.1.2: {}
jackspeak@3.4.3:
dependencies:
'@isaacs/cliui': 8.0.2
@@ -5824,6 +6050,8 @@ snapshots:
dependencies:
argparse: 2.0.1
jsbn@0.1.1: {}
jsesc@0.5.0: {}
jsesc@2.5.2: {}
@@ -5838,6 +6066,8 @@ snapshots:
json-stable-stringify-without-jsonify@1.0.1: {}
json-stringify-safe@5.0.1: {}
json5@2.2.3: {}
jsonfile@6.1.0:
@@ -5848,6 +6078,13 @@ snapshots:
jsonpointer@5.0.1: {}
jsprim@1.4.2:
dependencies:
assert-plus: 1.0.0
extsprintf: 1.3.0
json-schema: 0.4.0
verror: 1.10.0
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
@@ -5923,6 +6160,12 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.1
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
mimic-response@3.1.0: {}
min-indent@1.0.1: {}
@@ -5998,6 +6241,8 @@ snapshots:
npm-normalize-package-bin@3.0.1: {}
oauth-sign@0.9.0: {}
object-assign@4.1.1: {}
object-hash@3.0.0: {}
@@ -6068,6 +6313,8 @@ snapshots:
path-type@4.0.0: {}
performance-now@2.1.0: {}
periscopic@3.1.0:
dependencies:
'@types/estree': 1.0.5
@@ -6181,6 +6428,8 @@ snapshots:
process@0.11.10: {}
psl@1.9.0: {}
pump@3.0.2:
dependencies:
end-of-stream: 1.4.4
@@ -6204,6 +6453,8 @@ snapshots:
dependencies:
side-channel: 1.0.6
qs@6.5.3: {}
queue-microtask@1.2.3: {}
queue-tick@1.0.1: {}
@@ -6275,6 +6526,29 @@ snapshots:
dependencies:
jsesc: 0.5.0
request@2.88.2:
dependencies:
aws-sign2: 0.7.0
aws4: 1.13.2
caseless: 0.12.0
combined-stream: 1.0.8
extend: 3.0.2
forever-agent: 0.6.1
form-data: 2.3.3
har-validator: 5.1.5
http-signature: 1.2.0
is-typedarray: 1.0.0
isstream: 0.1.2
json-stringify-safe: 5.0.1
mime-types: 2.1.35
oauth-sign: 0.9.0
performance-now: 2.1.0
qs: 6.5.3
safe-buffer: 5.2.1
tough-cookie: 2.5.0
tunnel-agent: 0.6.0
uuid: 3.4.0
require-from-string@2.0.2: {}
resolve-from@4.0.0: {}
@@ -6287,6 +6561,12 @@ snapshots:
reusify@1.0.4: {}
rewire@7.0.0:
dependencies:
eslint: 8.57.1
transitivePeerDependencies:
- supports-color
rimraf@2.7.1:
dependencies:
glob: 7.2.3
@@ -6350,6 +6630,8 @@ snapshots:
safe-stable-stringify@2.5.0: {}
safer-buffer@2.1.2: {}
sander@0.5.1:
dependencies:
es6-promise: 3.3.1
@@ -6459,6 +6741,18 @@ snapshots:
sourcemap-codec@1.4.8: {}
sshpk@1.18.0:
dependencies:
asn1: 0.2.6
assert-plus: 1.0.0
bcrypt-pbkdf: 1.0.2
dashdash: 1.14.1
ecc-jsbn: 0.1.2
getpass: 0.1.7
jsbn: 0.1.1
safer-buffer: 2.1.2
tweetnacl: 0.14.5
stack-trace@0.0.10: {}
streamx@2.20.1:
@@ -6759,6 +7053,11 @@ snapshots:
totalist@3.0.1: {}
tough-cookie@2.5.0:
dependencies:
psl: 1.9.0
punycode: 2.3.1
tr46@0.0.3: {}
tr46@1.0.1:
@@ -6781,6 +7080,8 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
tweetnacl@0.14.5: {}
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
@@ -6869,8 +7170,16 @@ snapshots:
util-deprecate@1.0.2: {}
uuid@3.4.0: {}
uuid@8.3.2: {}
verror@1.10.0:
dependencies:
assert-plus: 1.0.0
core-util-is: 1.0.2
extsprintf: 1.3.0
vite-plugin-pwa@0.20.5(@vite-pwa/assets-generator@0.2.6)(vite@5.4.6(@types/node@22.5.5)(terser@5.33.0))(workbox-build@7.1.1)(workbox-window@7.1.0):
dependencies:
debug: 4.3.7
+26
View File
@@ -34,6 +34,32 @@ export type Database = {
}
public: {
Tables: {
account_deletion_requests: {
Row: {
requested_at: string
token: string
user_id: string
}
Insert: {
requested_at?: string
token: string
user_id: string
}
Update: {
requested_at?: string
token?: string
user_id?: string
}
Relationships: [
{
foreignKeyName: "account_deletion_requests_user_id_fkey"
columns: ["user_id"]
isOneToOne: true
referencedRelation: "users"
referencedColumns: ["id"]
},
]
}
credit_transactions: {
Row: {
created_at: string
+34
View File
@@ -0,0 +1,34 @@
import brevo from '@getbrevo/brevo';
import {
VITE_BREVO_API_KEY,
VITE_BREVO_SENDER_EMAIL,
VITE_BREVO_SENDER_NAME
} from '$env/static/private';
const apiInstance = new brevo.TransactionalEmailsApi();
const apiKey = apiInstance.authentications['apiKey'];
if (!VITE_BREVO_API_KEY) {
throw new Error('Brevo API key is not configured');
}
apiKey.apiKey = VITE_BREVO_API_KEY;
const sendEmail = async (subject: string, htmlContentString: string, toAddress: string) => {
const sendSmtpEmail = new brevo.SendSmtpEmail();
sendSmtpEmail.subject = subject;
sendSmtpEmail.htmlContent = htmlContentString;
sendSmtpEmail.sender = { name: VITE_BREVO_SENDER_NAME, email: VITE_BREVO_SENDER_EMAIL };
sendSmtpEmail.to = [{ email: toAddress, name: toAddress }];
sendSmtpEmail.replyTo = { email: VITE_BREVO_SENDER_EMAIL, name: VITE_BREVO_SENDER_NAME };
apiInstance.sendTransacEmail(sendSmtpEmail).then(
function (data) {
console.log('API called successfully. Returned data: ' + JSON.stringify(data));
},
function (error) {
console.error(error);
}
);
};
export { sendEmail };
+52 -1
View File
@@ -5,6 +5,7 @@ import { PUBLIC_SUPABASE_URL } from '$env/static/public';
import { SUPABASE_SERVICE_ROLE_KEY } from '$env/static/private';
import { stripe as stripeClient } from '$lib/utils/stripe';
import logger from '$lib/utils/logger/logger';
import { sendEmail } from '../brevo/email';
const toDateTime = (secs: number) => {
const t = new Date(+0); // Unix epoch start.
@@ -496,6 +497,54 @@ const getProductById = async (productId: string) => {
return product as Product;
};
const requestAccountDeletion = async (userId: string, userEmail: string, baseUrl: string) => {
const token = crypto.randomUUID();
const { error: insertError } = await supabaseAdmin.from('account_deletion_requests').upsert({
user_id: userId,
token,
requested_at: new Date().toISOString()
});
if (insertError) throw new Error(`Account deletion request failed: ${insertError.message}`);
const deletionLink = `${baseUrl}/api/delete-account?token=${token}`;
// Send deletion email using Brevo
try {
await sendEmail(
'Confirm Account Deletion',
`<p>Click <a href="${deletionLink}">here</a> to confirm your account deletion.</p>`,
userEmail
);
console.log('Deletion email sent successfully.');
} catch (emailError) {
console.error('Failed to send deletion email:', emailError);
throw new Error('Failed to send confirmation email.');
}
};
const deleteAccount = async (token: string) => {
// Verify the token
const { data: deletionRequest, error } = await supabaseAdmin
.from('account_deletion_requests')
.select('user_id')
.eq('token', token)
.single();
if (error || !deletionRequest) {
return new Response('Invalid or expired token', { status: 400 });
}
// N.B. The SQL here will either cascade delete or nullify the foreign key constraints depending on the table, to enable anonymisation
const { error: deletionError } = await supabaseAdmin.auth.admin.deleteUser(
deletionRequest.user_id
);
if (deletionError) {
throw new Error(`Failed to delete user: ${deletionError.message}`);
}
};
const getUserSubscriptions = async (userId: string) => {
try {
// Step 1: Retrieve subscription data
@@ -601,5 +650,7 @@ export {
upsertCustomerToSupabase,
getStripeCustomerId,
getUserSubscriptions,
getUserTransactions
getUserTransactions,
requestAccountDeletion,
deleteAccount
};
+22
View File
@@ -2,6 +2,7 @@ import { fail, redirect } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types';
import logger from '$lib/utils/logger/logger';
import { getUserSubscriptions, getUserTransactions } from '$lib/utils/supabase/admin';
import { requestAccountDeletion } from '$lib/utils/supabase/admin';
export const load: PageServerLoad = async ({ locals: { supabase, safeGetSession } }) => {
const { session } = await safeGetSession();
@@ -24,6 +25,27 @@ export const load: PageServerLoad = async ({ locals: { supabase, safeGetSession
};
export const actions: Actions = {
delete: async ({ url, locals: { safeGetSession } }) => {
const { session } = await safeGetSession();
const baseUrl = url.origin;
if (session) {
if (!session.user.email) {
return fail(400, { message: 'Email is required for account deletion' });
}
try {
await requestAccountDeletion(session.user.id, session.user.email, baseUrl);
return {
success: true,
message: 'Account deletion request submitted successfully'
};
} catch (error) {
console.error('Failed to request account deletion:', error);
return fail(500, { message: 'Failed to process deletion request' });
}
} else {
redirect(303, '/');
}
},
update: async ({ request, locals: { supabase, safeGetSession } }) => {
const formData = await request.formData();
const name = formData.get('name') as string;
+29 -1
View File
@@ -18,6 +18,27 @@
loading = false;
};
};
const handleSignOut: SubmitFunction = () => {
loading = true;
return async ({ update }) => {
loading = false;
update();
};
};
const handleAccountDeletion: SubmitFunction = () => {
loading = true;
if (session.user && session.user.email) {
alert('Check your email to confirm account deletion.');
} else {
alert('Please sign in to delete your account.');
}
return async () => {
loading = false;
};
};
</script>
<div
@@ -163,5 +184,12 @@
<a href="/contact" class="btn btn-outline w-full">Provide Feedback</a>
</section>
</div>
</div>
</form>
<form method="post" action="?/delete" use:enhance={handleAccountDeletion}>
<div>
<button class="button block" disabled={loading}>Delete My Account</button>
</div>
</form>
</div>
+18
View File
@@ -0,0 +1,18 @@
import type { RequestHandler } from '@sveltejs/kit';
import { deleteAccount } from '$lib/utils/supabase/admin';
export const GET: RequestHandler = async ({ url }) => {
const token = url.searchParams.get('token');
if (!token) {
return new Response('Invalid token', { status: 400 });
}
try {
await deleteAccount(token);
} catch (error) {
console.error(error);
return new Response('Failed to delete account', { status: 500 });
}
return new Response('Account deleted successfully', { status: 200 });
};
@@ -1,9 +0,0 @@
import { json } from '@sveltejs/kit';
export const GET = async () => {
return json(await getSomeData());
};
async function getSomeData() {
// Do your async fetching here
}
@@ -0,0 +1,46 @@
create table account_deletion_requests (
user_id uuid references auth.users not null primary key,
token text not null,
requested_at timestamp with time zone default now() not null
);
alter table account_deletion_requests enable row level security;
create policy "Can view own deletion request data" on account_deletion_requests
for select using (auth.uid() = user_id);
create policy "Can insert own deletion request data" on account_deletion_requests
for insert with check (auth.uid() = user_id);
ALTER TABLE customers
DROP CONSTRAINT IF EXISTS customers_id_fkey;
ALTER TABLE customers
ADD CONSTRAINT customers_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE;
ALTER TABLE subscriptions
DROP CONSTRAINT IF EXISTS subscriptions_user_id_fkey;
ALTER TABLE subscriptions
ADD CONSTRAINT subscriptions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL;
ALTER TABLE purchases
DROP CONSTRAINT IF EXISTS purchases_user_id_fkey;
ALTER TABLE purchases
ADD CONSTRAINT purchases_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL;
ALTER TABLE user_credits
DROP CONSTRAINT IF EXISTS user_credits_user_id_fkey;
ALTER TABLE user_credits
ADD CONSTRAINT user_credits_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE;
ALTER TABLE credit_transactions
DROP CONSTRAINT IF EXISTS credit_transactions_user_id_fkey;
ALTER TABLE credit_transactions
ADD CONSTRAINT credit_transactions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL;
ALTER TABLE account_deletion_requests
DROP CONSTRAINT IF EXISTS account_deletion_requests_user_id_fkey;
ALTER TABLE account_deletion_requests
ADD CONSTRAINT account_deletion_requests_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE;
ALTER TABLE users
DROP CONSTRAINT IF EXISTS users_id_fkey;
ALTER TABLE users
ADD CONSTRAINT users_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE;