Compare commits

...
8 Commits
Author SHA1 Message Date
Jonas Lochmann e14237be7d Update dependencies 2023-05-29 02:00:00 +02:00
Jonas Lochmann 4183ea615a Update dependencies 2023-05-08 02:00:00 +02:00
Jonas Lochmann e46f5bea3f Add account deletion API 2023-04-09 14:30:26 +02:00
Jonas Lochmann 05fac79849 Refactor signInByMailCode 2023-04-03 02:00:00 +02:00
Jonas Lochmann 1e5da1b95e Improve logging of mails during development mode 2023-04-03 02:00:00 +02:00
Jonas Lochmann dc5e2baebd Add deleting session durations when deleting families
For the automatic deletion, they were always already deleted.
However, for manual deletion, this is required.
2023-04-03 02:00:00 +02:00
Jonas Lochmann 73465ebe6e Update sequelize 2023-02-27 01:00:00 +01:00
Jonas Lochmann 98ee0fe94e Fix ReferenceError instead of ParseYesNoException 2022-12-12 01:00:00 +01:00
23 changed files with 803 additions and 190 deletions
+24
View File
@@ -194,3 +194,27 @@ If the ``secondPasswordHash`` is invalid: HTTP status code 401 Unauthorized
If the server does not support this request: HTTP status code 404
On success: ``{"token": "some string"}``; you should not make any assumptions about the token string
## POST /parent/delete-account
Use this to delete an account. This includes the complete family registration
with users and devices. Due to that, all parents with a linked mail address
have to authenticate this action.
## request
see [this JSON schema](../schema/deleteaccountpayload.md)
## response
On success: HTTP status code 200
On a invalid request body: HTTP status code 400 Bad Request
On unknown device auth token: HTTP status code 401 Unauthorized
On missing parent authentication: HTTP status code 401 Unauthorized
On unrelated parent authentication: HTTP status code 401 Unauthorized
If a newer endpoint must be used/the client is too old: HTTP status code 410 Gone
@@ -0,0 +1,23 @@
{
"type": "object",
"properties": {
"deviceAuthToken": {
"type": "string"
},
"mailAuthTokens": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"deviceAuthToken",
"mailAuthTokens"
],
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DeleteAccountPayload",
"$id": "https://timelimit.io/DeleteAccountPayload"
}
+4
View File
@@ -14,6 +14,8 @@
* [CreateRegisterDeviceTokenRequest](./createregisterdevicetokenrequest.md) `https://timelimit.io/CreateRegisterDeviceTokenRequest`
* [DeleteAccountPayload](./deleteaccountpayload.md) `https://timelimit.io/DeleteAccountPayload`
* [FinishPurchaseByGooglePlayRequest](./finishpurchasebygoogleplayrequest.md) `https://timelimit.io/FinishPurchaseByGooglePlayRequest`
* [IdentityTokenPayload](./identitytokenpayload.md) `https://timelimit.io/IdentityTokenPayload`
@@ -282,6 +284,8 @@
* [Untitled array in ClientPushChangesRequest](./clientpushchangesrequest-properties-actions.md) `https://timelimit.io/ClientPushChangesRequest#/properties/actions`
* [Untitled array in DeleteAccountPayload](./deleteaccountpayload-properties-mailauthtokens.md) `https://timelimit.io/DeleteAccountPayload#/properties/mailAuthTokens`
* [Untitled array in SerializedAppLogicAction](./serializedapplogicaction-definitions-serializedaddinstalledappsaction-properties-apps.md) `https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedAddInstalledAppsAction/properties/apps`
* [Untitled array in SerializedAppLogicAction](./serializedapplogicaction-definitions-serializedaddusedtimeactionversion2-properties-i.md) `https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedAddUsedTimeActionVersion2/properties/i`
@@ -0,0 +1,15 @@
# Untitled undefined type in DeleteAccountPayload Schema
```txt
https://timelimit.io/DeleteAccountPayload#/definitions
```
| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :-------------------------------------------------------------------------------------------- |
| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [DeleteAccountPayload.schema.json\*](DeleteAccountPayload.schema.json "open original schema") |
## definitions Type
unknown
@@ -0,0 +1,15 @@
# Untitled string in DeleteAccountPayload Schema
```txt
https://timelimit.io/DeleteAccountPayload#/properties/deviceAuthToken
```
| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :-------------------------------------------------------------------------------------------- |
| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [DeleteAccountPayload.schema.json\*](DeleteAccountPayload.schema.json "open original schema") |
## deviceAuthToken Type
`string`
@@ -0,0 +1,15 @@
# Untitled string in DeleteAccountPayload Schema
```txt
https://timelimit.io/DeleteAccountPayload#/properties/mailAuthTokens/items
```
| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :-------------------------------------------------------------------------------------------- |
| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [DeleteAccountPayload.schema.json\*](DeleteAccountPayload.schema.json "open original schema") |
## items Type
`string`
@@ -0,0 +1,15 @@
# Untitled array in DeleteAccountPayload Schema
```txt
https://timelimit.io/DeleteAccountPayload#/properties/mailAuthTokens
```
| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :-------------------------------------------------------------------------------------------- |
| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [DeleteAccountPayload.schema.json\*](DeleteAccountPayload.schema.json "open original schema") |
## mailAuthTokens Type
`string[]`
+60
View File
@@ -0,0 +1,60 @@
# DeleteAccountPayload Schema
```txt
https://timelimit.io/DeleteAccountPayload
```
| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
| :------------------ | :--------- | :------------- | :----------- | :---------------- | :-------------------- | :------------------ | :------------------------------------------------------------------------------------------ |
| Can be instantiated | Yes | Unknown status | No | Forbidden | Forbidden | none | [DeleteAccountPayload.schema.json](DeleteAccountPayload.schema.json "open original schema") |
## DeleteAccountPayload Type
`object` ([DeleteAccountPayload](deleteaccountpayload.md))
# DeleteAccountPayload Properties
| Property | Type | Required | Nullable | Defined by |
| :---------------------------------- | :------- | :------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
| [deviceAuthToken](#deviceauthtoken) | `string` | Required | cannot be null | [DeleteAccountPayload](deleteaccountpayload-properties-deviceauthtoken.md "https://timelimit.io/DeleteAccountPayload#/properties/deviceAuthToken") |
| [mailAuthTokens](#mailauthtokens) | `array` | Required | cannot be null | [DeleteAccountPayload](deleteaccountpayload-properties-mailauthtokens.md "https://timelimit.io/DeleteAccountPayload#/properties/mailAuthTokens") |
## deviceAuthToken
`deviceAuthToken`
* is required
* Type: `string`
* cannot be null
* defined in: [DeleteAccountPayload](deleteaccountpayload-properties-deviceauthtoken.md "https://timelimit.io/DeleteAccountPayload#/properties/deviceAuthToken")
### deviceAuthToken Type
`string`
## mailAuthTokens
`mailAuthTokens`
* is required
* Type: `string[]`
* cannot be null
* defined in: [DeleteAccountPayload](deleteaccountpayload-properties-mailauthtokens.md "https://timelimit.io/DeleteAccountPayload#/properties/mailAuthTokens")
### mailAuthTokens Type
`string[]`
# DeleteAccountPayload Definitions
+192
View File
@@ -0,0 +1,192 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<title>
</title>
<!--[if !mso]><!-->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!--<![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
#outlook a {
padding: 0;
}
body {
margin: 0;
padding: 0;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
table,
td {
border-collapse: collapse;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
}
img {
border: 0;
height: auto;
line-height: 100%;
outline: none;
text-decoration: none;
-ms-interpolation-mode: bicubic;
}
p {
display: block;
margin: 13px 0;
}
</style>
<!--[if mso]>
<noscript>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
</noscript>
<![endif]-->
<!--[if lte mso 11]>
<style type="text/css">
.mj-outlook-group-fix { width:100% !important; }
</style>
<![endif]-->
<!--[if !mso]><!-->
<!--<![endif]-->
<style type="text/css">
@media only screen and (min-width:480px) {
.mj-column-per-100 {
width: 100% !important;
max-width: 100%;
}
}
</style>
<style media="screen and (min-width:480px)">
.moz-text-html .mj-column-per-100 {
width: 100% !important;
max-width: 100%;
}
</style>
<style type="text/css">
</style>
</head>
<body style="word-spacing:normal;">
<div style="">
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" bgcolor="#009688" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="background:#009688;background-color:#009688;margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#009688;background-color:#009688;width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tbody>
<tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:20px;line-height:1;text-align:left;color:#ffffff;">TimeLimit</div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tbody>
<tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">
<p> Sie haben eine Löschung Ihres Benutzerkontos angefordert. Diese wurde soeben durchgeführt. </p>
<p> You requested the deletion of your account. This deletion is finished now. </p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tbody>
<tr>
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<p style="border-top:dashed 1px lightgrey;font-size:1px;margin:0px auto;width:100%;">
</p>
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" style="border-top:dashed 1px lightgrey;font-size:1px;margin:0px auto;width:550px;" role="presentation" width="550px" ><tr><td style="height:0;line-height:0;"> &nbsp;
</td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="margin:0px auto;max-width:600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
<tbody>
<tr>
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
<tbody>
<tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;line-height:1;text-align:left;color:#000000;">
<p> Sie erhalten diese Nachricht aufgrund Ihrer Anfrage. Falls Sie Fragen haben können Sie einfach auf diese E-Mail antworten. </p>
<p> You got this mail due to your request. If you have got any questions, then you can reply to this message. </p>
<p> &copy; <%= mailimprint %> </p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</div>
</body>
</html>
@@ -0,0 +1,44 @@
<mjml>
<mj-body>
<mj-section background-color="#009688">
<mj-column>
<mj-text font-size="20px" color="#ffffff">TimeLimit</mj-text>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text>
<p>
Sie haben eine Löschung Ihres Benutzerkontos angefordert. Diese wurde soeben durchgeführt.
</p>
<p>
You requested the deletion of your account. This deletion is finished now.
</p>
</mj-text>
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-divider border-width="1px" border-style="dashed" border-color="lightgrey" />
</mj-column>
</mj-section>
<mj-section>
<mj-column>
<mj-text>
<p>
Sie erhalten diese Nachricht aufgrund Ihrer Anfrage.
Falls Sie Fragen haben können Sie einfach auf diese E-Mail antworten.
</p>
<p>
You got this mail due to your request.
If you have got any questions, then you can reply to this message.
</p>
<p>
&copy; <%= mailimprint %>
</p>
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
+1
View File
@@ -0,0 +1 @@
Konto gelöscht/Account deleted
+13
View File
@@ -0,0 +1,13 @@
Sie haben eine Löschung Ihres Benutzerkontos angefordert. Diese wurde soeben durchgeführt.
You requested the deletion of your account. This deletion is finished now.
----------------------
Sie erhalten diese Nachricht aufgrund Ihrer Anfrage.
Falls Sie Fragen haben können Sie einfach auf diese E-Mail antworten.
You got this mail due to your request.
If you have got any questions, then you can reply to this message.
<C> <%- mailimprint %>
+82 -70
View File
@@ -315,9 +315,12 @@
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"node_modules/@types/cors": {
"version": "2.8.12",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
"version": "2.8.13",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
"integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/debug": {
"version": "4.1.7",
@@ -1187,9 +1190,9 @@
}
},
"node_modules/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
"engines": {
"node": ">= 0.6"
}
@@ -1416,9 +1419,9 @@
}
},
"node_modules/engine.io": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz",
"integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==",
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz",
"integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==",
"dependencies": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
@@ -1429,16 +1432,16 @@
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.3",
"ws": "~8.2.3"
"ws": "~8.11.0"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/engine.io-parser": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
"integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
"engines": {
"node": ">=10.0.0"
}
@@ -4290,9 +4293,9 @@
}
},
"node_modules/retry-as-promised": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-6.1.0.tgz",
"integrity": "sha512-Hj/jY+wFC+SB9SDlIIFWiGOHnNG0swYbGYsOj2BJ8u2HKUaobNKab0OIC0zOLYzDy0mb7A4xA5BMo4LMz5YtEA=="
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz",
"integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA=="
},
"node_modules/reusify": {
"version": "1.0.4",
@@ -4484,9 +4487,9 @@
}
},
"node_modules/sequelize": {
"version": "6.25.5",
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.25.5.tgz",
"integrity": "sha512-QwA2kL4lSNy4mYTv1Zm4DjvloZLaYajuYF5QMOv0O7FameMUEl8Q5zOWRD+FNKGCLzyOJ28gQCo3HX0N9mcssA==",
"version": "6.29.0",
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.29.0.tgz",
"integrity": "sha512-m8Wi90rs3NZP9coXE52c7PL4Q078nwYZXqt1IxPvgki7nOFn0p/F0eKsYDBXCPw9G8/BCEa6zZNk0DQUAT4ypA==",
"funding": [
{
"type": "opencollective",
@@ -4501,9 +4504,9 @@
"inflection": "^1.13.2",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"moment-timezone": "^0.5.34",
"moment-timezone": "^0.5.35",
"pg-connection-string": "^2.5.0",
"retry-as-promised": "^6.1.0",
"retry-as-promised": "^7.0.3",
"semver": "^7.3.5",
"sequelize-pool": "^7.1.0",
"toposort-class": "^1.0.1",
@@ -4637,15 +4640,15 @@
}
},
"node_modules/socket.io": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz",
"integrity": "sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==",
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz",
"integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==",
"dependencies": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.2",
"engine.io": "~6.2.1",
"socket.io-adapter": "~2.4.0",
"engine.io": "~6.4.1",
"socket.io-adapter": "~2.5.2",
"socket.io-parser": "~4.2.1"
},
"engines": {
@@ -4653,14 +4656,17 @@
}
},
"node_modules/socket.io-adapter": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
"integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg=="
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
"integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
"dependencies": {
"ws": "~8.11.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
"integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
@@ -5418,9 +5424,9 @@
"devOptional": true
},
"node_modules/ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
"engines": {
"node": ">=10.0.0"
},
@@ -5769,9 +5775,12 @@
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
},
"@types/cors": {
"version": "2.8.12",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
"version": "2.8.13",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
"integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
"requires": {
"@types/node": "*"
}
},
"@types/debug": {
"version": "4.1.7",
@@ -6409,9 +6418,9 @@
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
},
"cookie-signature": {
"version": "1.0.6",
@@ -6577,9 +6586,9 @@
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
},
"engine.io": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz",
"integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==",
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz",
"integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==",
"requires": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
@@ -6590,13 +6599,13 @@
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.3",
"ws": "~8.2.3"
"ws": "~8.11.0"
}
},
"engine.io-parser": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg=="
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
"integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw=="
},
"es2015-i18n-tag": {
"version": "1.6.1",
@@ -8673,9 +8682,9 @@
"dev": true
},
"retry-as-promised": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-6.1.0.tgz",
"integrity": "sha512-Hj/jY+wFC+SB9SDlIIFWiGOHnNG0swYbGYsOj2BJ8u2HKUaobNKab0OIC0zOLYzDy0mb7A4xA5BMo4LMz5YtEA=="
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz",
"integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA=="
},
"reusify": {
"version": "1.0.4",
@@ -8820,9 +8829,9 @@
}
},
"sequelize": {
"version": "6.25.5",
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.25.5.tgz",
"integrity": "sha512-QwA2kL4lSNy4mYTv1Zm4DjvloZLaYajuYF5QMOv0O7FameMUEl8Q5zOWRD+FNKGCLzyOJ28gQCo3HX0N9mcssA==",
"version": "6.29.0",
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.29.0.tgz",
"integrity": "sha512-m8Wi90rs3NZP9coXE52c7PL4Q078nwYZXqt1IxPvgki7nOFn0p/F0eKsYDBXCPw9G8/BCEa6zZNk0DQUAT4ypA==",
"requires": {
"@types/debug": "^4.1.7",
"@types/validator": "^13.7.1",
@@ -8831,9 +8840,9 @@
"inflection": "^1.13.2",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"moment-timezone": "^0.5.34",
"moment-timezone": "^0.5.35",
"pg-connection-string": "^2.5.0",
"retry-as-promised": "^6.1.0",
"retry-as-promised": "^7.0.3",
"semver": "^7.3.5",
"sequelize-pool": "^7.1.0",
"toposort-class": "^1.0.1",
@@ -8913,27 +8922,30 @@
"dev": true
},
"socket.io": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz",
"integrity": "sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==",
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz",
"integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==",
"requires": {
"accepts": "~1.3.4",
"base64id": "~2.0.0",
"debug": "~4.3.2",
"engine.io": "~6.2.1",
"socket.io-adapter": "~2.4.0",
"engine.io": "~6.4.1",
"socket.io-adapter": "~2.5.2",
"socket.io-parser": "~4.2.1"
}
},
"socket.io-adapter": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
"integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg=="
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
"integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
"requires": {
"ws": "~8.11.0"
}
},
"socket.io-parser": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz",
"integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==",
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
"requires": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
@@ -9484,9 +9496,9 @@
"devOptional": true
},
"ws": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
"requires": {}
},
"xtend": {
+3 -2
View File
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2022 Jonas Lochmann
* Copyright (C) 2019 - 2023 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -43,7 +43,8 @@ const types = [
'RequestWithAuthToken',
'SendMailLoginCodeRequest',
'SignInByMailCodeRequest',
'IdentityTokenPayload'
'IdentityTokenPayload',
'DeleteAccountPayload',
]
const docOnlyTypes = [
+18 -2
View File
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2022 Jonas Lochmann
* Copyright (C) 2019 - 2023 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -21,6 +21,7 @@ import { Router } from 'express'
import { BadRequest, Forbidden, Unauthorized } from 'http-errors'
import { config } from '../config'
import { Database, Transaction } from '../database'
import { deleteAccount } from '../function/cleanup/account-deletion'
import { removeDevice } from '../function/device/remove-device'
import { createAddDeviceToken } from '../function/parent/create-add-device-token'
import { createFamily } from '../function/parent/create-family'
@@ -36,7 +37,8 @@ import {
isCreateFamilyByMailTokenRequest,
isCreateRegisterDeviceTokenRequest, isLinkParentMailAddressRequest,
isMailAuthTokenRequestBody, isRecoverParentPasswordRequest,
isRemoveDeviceRequest, isSignIntoFamilyRequest, isRequestIdentityTokenRequest
isRemoveDeviceRequest, isSignIntoFamilyRequest, isRequestIdentityTokenRequest,
isDeleteAccountPayload
} from './validator'
export const createParentRouter = ({
@@ -356,5 +358,19 @@ export const createParentRouter = ({
}
})
router.post('/delete-account', json(), async (req, res, next) => {
try {
if (!isDeleteAccountPayload(req.body)) {
throw new BadRequest()
}
await deleteAccount({ database, request: req.body, websocket })
res.sendStatus(200)
} catch (ex) {
next(ex)
}
})
return router
}
+6 -1
View File
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2022 Jonas Lochmann
* Copyright (C) 2019 - 2023 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -176,5 +176,10 @@ export type IdentityTokenPayload = IdentityTokenCreatePayload & {
exp: number
}
export interface DeleteAccountPayload {
deviceAuthToken: string
mailAuthTokens: Array<string>
}
export { SerializedParentAction, SerializedChildAction, SerializedAppLogicAction } from '../action/serialization'
export { ServerDataStatus } from '../object/serverdatastatus'
+22 -1
View File
@@ -1,5 +1,5 @@
// tslint:disable
import { ClientPushChangesRequest, ClientPullChangesRequest, MailAuthTokenRequestBody, CreateFamilyByMailTokenRequest, SignIntoFamilyRequest, RecoverParentPasswordRequest, RegisterChildDeviceRequest, SerializedParentAction, SerializedAppLogicAction, SerializedChildAction, CreateRegisterDeviceTokenRequest, CanDoPurchaseRequest, FinishPurchaseByGooglePlayRequest, LinkParentMailAddressRequest, UpdatePrimaryDeviceRequest, RemoveDeviceRequest, RequestIdentityTokenRequest, RequestWithAuthToken, SendMailLoginCodeRequest, SignInByMailCodeRequest, IdentityTokenPayload } from './schema'
import { ClientPushChangesRequest, ClientPullChangesRequest, MailAuthTokenRequestBody, CreateFamilyByMailTokenRequest, SignIntoFamilyRequest, RecoverParentPasswordRequest, RegisterChildDeviceRequest, SerializedParentAction, SerializedAppLogicAction, SerializedChildAction, CreateRegisterDeviceTokenRequest, CanDoPurchaseRequest, FinishPurchaseByGooglePlayRequest, LinkParentMailAddressRequest, UpdatePrimaryDeviceRequest, RemoveDeviceRequest, RequestIdentityTokenRequest, RequestWithAuthToken, SendMailLoginCodeRequest, SignInByMailCodeRequest, IdentityTokenPayload, DeleteAccountPayload } from './schema'
import Ajv from 'ajv'
const ajv = new Ajv()
@@ -3493,3 +3493,24 @@ export const isIdentityTokenPayload: (value: unknown) => value is IdentityTokenP
"definitions": definitions,
"$schema": "http://json-schema.org/draft-07/schema#"
})
export const isDeleteAccountPayload: (value: unknown) => value is DeleteAccountPayload = ajv.compile({
"type": "object",
"properties": {
"deviceAuthToken": {
"type": "string"
},
"mailAuthTokens": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
"required": [
"deviceAuthToken",
"mailAuthTokens"
],
"definitions": definitions,
"$schema": "http://json-schema.org/draft-07/schema#"
})
+2 -2
View File
@@ -34,6 +34,8 @@ function parseYesNo (value: string) {
}
}
class ParseYesNoException extends Error {}
export const config: Config = {
mailWhitelist: (process.env.MAIL_WHITELIST || '').split(',').map((item) => item.trim()).filter((item) => item.length > 0),
disableSignup: parseYesNo(process.env.DISABLE_SIGNUP || 'no'),
@@ -41,5 +43,3 @@ export const config: Config = {
alwaysPro: process.env.ALWAYS_PRO ? parseYesNo(process.env.ALWAYS_PRO) : false,
signSecret: process.env.SIGN_SECRET || ''
}
class ParseYesNoException extends Error {}
+7 -5
View File
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2021 Jonas Lochmann
* Copyright (C) 2019 - 2023 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -109,7 +109,7 @@ export const signInByMailCode = async ({ mailLoginToken, receivedCode, database
database: Database
// no transaction here because this is directly called from an API endpoint
}): Promise<{ mailAuthToken: string }> => {
return database.transaction(async (transaction) => {
const result = await database.transaction(async (transaction) => {
const entry = await database.mailLoginToken.findOne({
where: {
mailLoginToken
@@ -127,9 +127,9 @@ export const signInByMailCode = async ({ mailLoginToken, receivedCode, database
await entry.save({ transaction })
if (entry.remainingAttempts === 0) {
throw new Gone()
return () => { throw new Gone() }
} else {
throw new Forbidden()
return () => { throw new Forbidden() }
}
}
@@ -151,6 +151,8 @@ export const signInByMailCode = async ({ mailLoginToken, receivedCode, database
transaction
})
return { mailAuthToken }
return () => ({ mailAuthToken })
})
return result()
}
+107
View File
@@ -0,0 +1,107 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2023 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Unauthorized } from 'http-errors'
import { DeleteAccountPayload } from '../../api/schema'
import { Database } from '../../database'
import { sendAccountDeletedMail } from '../../util/mail'
import { WebsocketApi } from '../../websocket'
import { requireMailAndLocaleByAuthToken } from '../authentication'
import { deleteFamilies } from './delete-families'
export async function deleteAccount({ request, database, websocket }: {
request: DeleteAccountPayload
database: Database
websocket: WebsocketApi
}) {
await database.transaction(async (transaction) => {
const deviceEntryUnsafe = await database.device.findOne({
where: { deviceAuthToken: request.deviceAuthToken },
attributes: ['familyId'],
transaction
})
if (!deviceEntryUnsafe) {
throw new Unauthorized()
}
const deviceEntry = {
familyId: deviceEntryUnsafe.familyId
}
const userEntries = (await database.user.findAll({
where: {
familyId: deviceEntry.familyId,
type: 'parent'
},
attributes: ['mail'],
transaction
})).map((item) => ({ mail: item.mail }))
const registeredMailAddresses = new Set<string>()
userEntries.forEach((item) => {
if (item.mail !== '') registeredMailAddresses.add(item.mail)
})
const authenticatedMailAddresses = new Set<string>()
for (const mailAuthToken of request.mailAuthTokens) {
const info = await requireMailAndLocaleByAuthToken({
mailAuthToken,
database,
transaction,
invalidate: true
})
if (!registeredMailAddresses.has(info.mail)) throw new Unauthorized()
authenticatedMailAddresses.add(info.mail)
}
if (registeredMailAddresses.size !== authenticatedMailAddresses.size) throw new Unauthorized()
registeredMailAddresses.forEach((mail) => {
if (!authenticatedMailAddresses.has(mail)) throw new Unauthorized()
})
const deviceEntries = (await database.device.findAll({
where: {
familyId: deviceEntry.familyId
},
transaction,
attributes: ['deviceAuthToken']
})).map((item) => ({ deviceAuthToken: item.deviceAuthToken }))
await deleteFamilies({ database, transaction, familiyIds: [deviceEntry.familyId] })
transaction.afterCommit(() => {
for (const device of deviceEntries) {
websocket.triggerSyncByDeviceAuthToken({
deviceAuthToken: device.deviceAuthToken,
isImportant: true
})
}
registeredMailAddresses.forEach((receiver) => {
sendAccountDeletedMail({ receiver }).catch((ex) => {
console.warn('failure while sending account deletion confirmation', ex)
})
})
})
})
}
+110 -101
View File
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2022 Jonas Lochmann
* Copyright (C) 2019 - 2023 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -17,10 +17,11 @@
import { difference } from 'lodash'
import * as Sequelize from 'sequelize'
import { Database } from '../../database'
import { Database, Transaction } from '../../database'
export async function deleteFamilies ({ database, familiyIds }: {
export async function deleteFamilies ({ database, transaction, familiyIds }: {
database: Database
transaction: Transaction
familiyIds: Array<string>
// no transaction here because this should run isolated
}) {
@@ -28,118 +29,126 @@ export async function deleteFamilies ({ database, familiyIds }: {
return
}
await database.transaction(async (transaction) => {
// category
await database.category.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// category
await database.category.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// categoryapp
await database.categoryApp.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// categoryapp
await database.categoryApp.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// purchase
await database.purchase.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// purchase
await database.purchase.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// timelimitrule
await database.timelimitRule.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// timelimitrule
await database.timelimitRule.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// usedtime
await database.usedTime.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// usedtime
await database.usedTime.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// user
await database.user.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// session durations
await database.sessionDuration.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// device
const oldDeviceAuthTokens = (await database.device.findAll({
// user
await database.user.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// device
const oldDeviceAuthTokens = (await database.device.findAll({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
attributes: ['deviceAuthToken'],
transaction
})).map((item) => item.deviceAuthToken)
await database.device.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// olddevice
if (oldDeviceAuthTokens.length > 0) {
const knownOldDeviceAuthTokens = (await database.oldDevice.findAll({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
deviceAuthToken: {
[Sequelize.Op.in]: oldDeviceAuthTokens
}
},
attributes: ['deviceAuthToken'],
transaction
})).map((item) => item.deviceAuthToken)
await database.device.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
const oldDeviceAuthTokensToAdd = difference(oldDeviceAuthTokens, knownOldDeviceAuthTokens)
// olddevice
if (oldDeviceAuthTokens.length > 0) {
const knownOldDeviceAuthTokens = (await database.oldDevice.findAll({
where: {
deviceAuthToken: {
[Sequelize.Op.in]: oldDeviceAuthTokens
}
},
transaction
})).map((item) => item.deviceAuthToken)
const oldDeviceAuthTokensToAdd = difference(oldDeviceAuthTokens, knownOldDeviceAuthTokens)
if (oldDeviceAuthTokensToAdd.length > 0) {
await database.oldDevice.bulkCreate(
oldDeviceAuthTokensToAdd.map((item) => ({
deviceAuthToken: item
})),
{ transaction }
)
}
if (oldDeviceAuthTokensToAdd.length > 0) {
await database.oldDevice.bulkCreate(
oldDeviceAuthTokensToAdd.map((item) => ({
deviceAuthToken: item
})),
{ transaction }
)
}
}
// family
await database.family.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
// family
await database.family.destroy({
where: {
familyId: {
[Sequelize.Op.in]: familiyIds
}
},
transaction
})
}
+7 -4
View File
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2020 Jonas Lochmann
* Copyright (C) 2019 - 2023 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -26,9 +26,12 @@ export async function deleteOldFamilies (database: Database) {
if (oldFamilyIds.length > 0) {
const familyIdsToDelete = oldFamilyIds.slice(0, 256) /* limit to 256 families per execution */
await deleteFamilies({
database,
familiyIds: familyIdsToDelete
await database.transaction(async (transaction) => {
await deleteFamilies({
database,
transaction,
familiyIds: familyIdsToDelete
})
})
}
}
+18 -2
View File
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2022 Jonas Lochmann
* Copyright (C) 2019 - 2023 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -72,7 +72,10 @@ function createMailTemplateSender (templateName: string) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data = (info as any).message
console.log(JSON.stringify(JSON.parse(data), null, 2))
console.log(JSON.stringify({
...JSON.parse(data),
params
}, null, 2))
}
resolve()
@@ -190,6 +193,19 @@ export const sendPasswordRecoveryUsedMail = async ({ receiver, locale }: {
})
}
const accountDeletedMailSender = createMailTemplateSender('account-deleted')
export const sendAccountDeletedMail = async ({ receiver }: {
receiver: string
}) => {
await accountDeletedMailSender.sendMail({
receiver,
params: {
mailimprint
}
})
}
function getMailSecurityText (locale: string) {
if (locale === 'de') {
return 'Achten Sie darauf, dass Ihr Kind/Ihre Kinder keinen Zugang zu der E-Mail-Adresse hat/haben, die Sie bei TimeLimit angegeben haben.'