mirror of
https://codeberg.org/timelimit/timelimit-server.git
synced 2025-10-03 17:59:24 +02:00
Compare commits
34 commits
2022-12-19
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6189601459 | ||
![]() |
569e5ce62d | ||
![]() |
6220cc6bb9 | ||
![]() |
33d9fd732f | ||
![]() |
764f240707 | ||
![]() |
b392ca295a | ||
![]() |
f5fc8e6cd6 | ||
![]() |
9c2048af64 | ||
![]() |
b69271f7df | ||
![]() |
97d2730b20 | ||
![]() |
0346197c23 | ||
![]() |
a7ed01af74 | ||
![]() |
f77d91ff56 | ||
![]() |
2d035da0da | ||
![]() |
2d73cba90e | ||
![]() |
1918c74277 | ||
![]() |
e55d1fd1a9 | ||
![]() |
f10b79a023 | ||
![]() |
2c401288a3 | ||
![]() |
89f3325a18 | ||
![]() |
7aaad00881 | ||
![]() |
c7e4cfc9f9 | ||
![]() |
12ed5d73cd | ||
![]() |
4df809a306 | ||
![]() |
8ec0781859 | ||
![]() |
376a2cc624 | ||
![]() |
e14237be7d | ||
![]() |
4183ea615a | ||
![]() |
e46f5bea3f | ||
![]() |
05fac79849 | ||
![]() |
1e5da1b95e | ||
![]() |
dc5e2baebd | ||
![]() |
73465ebe6e | ||
![]() |
98ee0fe94e |
45 changed files with 4290 additions and 2372 deletions
|
@ -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
|
||||
|
|
23
docs/schema/DeleteAccountPayload.schema.json
Normal file
23
docs/schema/DeleteAccountPayload.schema.json
Normal file
|
@ -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"
|
||||
}
|
|
@ -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`
|
||||
|
|
|
@ -545,6 +545,12 @@
|
|||
},
|
||||
"addedManipulationFlags": {
|
||||
"type": "number"
|
||||
},
|
||||
"platformType": {
|
||||
"type": "string"
|
||||
},
|
||||
"platformLevel": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
|
|
@ -212,6 +212,12 @@
|
|||
},
|
||||
"pk": {
|
||||
"type": "string"
|
||||
},
|
||||
"pType": {
|
||||
"type": "string"
|
||||
},
|
||||
"pLevel": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
@ -240,6 +246,7 @@
|
|||
"model",
|
||||
"name",
|
||||
"networkTime",
|
||||
"pLevel",
|
||||
"qOrLater",
|
||||
"reboot",
|
||||
"rebootIsManipulation",
|
||||
|
|
15
docs/schema/deleteaccountpayload-definitions.md
Normal file
15
docs/schema/deleteaccountpayload-definitions.md
Normal file
|
@ -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
docs/schema/deleteaccountpayload.md
Normal file
60
docs/schema/deleteaccountpayload.md
Normal 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
|
|
@ -0,0 +1,15 @@
|
|||
# Untitled number in SerializedAppLogicAction Schema
|
||||
|
||||
```txt
|
||||
https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformLevel
|
||||
```
|
||||
|
||||
|
||||
|
||||
| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
|
||||
| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :---------------------------------------------------------------------------------------------------- |
|
||||
| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [SerializedAppLogicAction.schema.json\*](SerializedAppLogicAction.schema.json "open original schema") |
|
||||
|
||||
## platformLevel Type
|
||||
|
||||
`number`
|
|
@ -0,0 +1,15 @@
|
|||
# Untitled string in SerializedAppLogicAction Schema
|
||||
|
||||
```txt
|
||||
https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformType
|
||||
```
|
||||
|
||||
|
||||
|
||||
| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
|
||||
| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :---------------------------------------------------------------------------------------------------- |
|
||||
| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [SerializedAppLogicAction.schema.json\*](SerializedAppLogicAction.schema.json "open original schema") |
|
||||
|
||||
## platformType Type
|
||||
|
||||
`string`
|
|
@ -28,6 +28,8 @@ https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDevic
|
|||
| [didReboot](#didreboot) | `boolean` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-didreboot.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/didReboot") |
|
||||
| [isQOrLaterNow](#isqorlaternow) | `boolean` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-isqorlaternow.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/isQOrLaterNow") |
|
||||
| [addedManipulationFlags](#addedmanipulationflags) | `number` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-addedmanipulationflags.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/addedManipulationFlags") |
|
||||
| [platformType](#platformtype) | `string` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-platformtype.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformType") |
|
||||
| [platformLevel](#platformlevel) | `number` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-platformlevel.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformLevel") |
|
||||
|
||||
## type
|
||||
|
||||
|
@ -257,3 +259,39 @@ https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDevic
|
|||
### addedManipulationFlags Type
|
||||
|
||||
`number`
|
||||
|
||||
## platformType
|
||||
|
||||
|
||||
|
||||
`platformType`
|
||||
|
||||
* is optional
|
||||
|
||||
* Type: `string`
|
||||
|
||||
* cannot be null
|
||||
|
||||
* defined in: [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-platformtype.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformType")
|
||||
|
||||
### platformType Type
|
||||
|
||||
`string`
|
||||
|
||||
## platformLevel
|
||||
|
||||
|
||||
|
||||
`platformLevel`
|
||||
|
||||
* is optional
|
||||
|
||||
* Type: `number`
|
||||
|
||||
* cannot be null
|
||||
|
||||
* defined in: [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-platformlevel.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformLevel")
|
||||
|
||||
### platformLevel Type
|
||||
|
||||
`number`
|
||||
|
|
|
@ -1240,6 +1240,8 @@ Reference this group by using
|
|||
| [didReboot](#didreboot) | `boolean` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-didreboot.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/didReboot") |
|
||||
| [isQOrLaterNow](#isqorlaternow) | `boolean` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-isqorlaternow.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/isQOrLaterNow") |
|
||||
| [addedManipulationFlags](#addedmanipulationflags) | `number` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-addedmanipulationflags.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/addedManipulationFlags") |
|
||||
| [platformType](#platformtype) | `string` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-platformtype.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformType") |
|
||||
| [platformLevel](#platformlevel) | `number` | Optional | cannot be null | [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-platformlevel.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformLevel") |
|
||||
|
||||
### type
|
||||
|
||||
|
@ -1470,6 +1472,42 @@ Reference this group by using
|
|||
|
||||
`number`
|
||||
|
||||
### platformType
|
||||
|
||||
|
||||
|
||||
`platformType`
|
||||
|
||||
* is optional
|
||||
|
||||
* Type: `string`
|
||||
|
||||
* cannot be null
|
||||
|
||||
* defined in: [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-platformtype.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformType")
|
||||
|
||||
#### platformType Type
|
||||
|
||||
`string`
|
||||
|
||||
### platformLevel
|
||||
|
||||
|
||||
|
||||
`platformLevel`
|
||||
|
||||
* is optional
|
||||
|
||||
* Type: `number`
|
||||
|
||||
* cannot be null
|
||||
|
||||
* defined in: [SerializedAppLogicAction](serializedapplogicaction-definitions-serializedupdatedevicestatusaction-properties-platformlevel.md "https://timelimit.io/SerializedAppLogicAction#/definitions/SerializedUpdateDeviceStatusAction/properties/platformLevel")
|
||||
|
||||
#### platformLevel Type
|
||||
|
||||
`number`
|
||||
|
||||
## Definitions group SerializedUploadDevicePublicKeyAction
|
||||
|
||||
Reference this group by using
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# Untitled number in ServerDataStatus Schema
|
||||
|
||||
```txt
|
||||
https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pLevel
|
||||
```
|
||||
|
||||
|
||||
|
||||
| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
|
||||
| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :------------------------------------------------------------------------------------ |
|
||||
| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [ServerDataStatus.schema.json\*](ServerDataStatus.schema.json "open original schema") |
|
||||
|
||||
## pLevel Type
|
||||
|
||||
`number`
|
|
@ -0,0 +1,15 @@
|
|||
# Untitled string in ServerDataStatus Schema
|
||||
|
||||
```txt
|
||||
https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pType
|
||||
```
|
||||
|
||||
|
||||
|
||||
| Abstract | Extensible | Status | Identifiable | Custom Properties | Additional Properties | Access Restrictions | Defined In |
|
||||
| :------------------ | :--------- | :------------- | :---------------------- | :---------------- | :-------------------- | :------------------ | :------------------------------------------------------------------------------------ |
|
||||
| Can be instantiated | No | Unknown status | Unknown identifiability | Forbidden | Allowed | none | [ServerDataStatus.schema.json\*](ServerDataStatus.schema.json "open original schema") |
|
||||
|
||||
## pType Type
|
||||
|
||||
`string`
|
|
@ -50,6 +50,8 @@ https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData
|
|||
| [qOrLater](#qorlater) | `boolean` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-qorlater.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/qOrLater") |
|
||||
| [mFlags](#mflags) | `number` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-mflags.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/mFlags") |
|
||||
| [pk](#pk) | `string` | Optional | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-pk.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pk") |
|
||||
| [pType](#ptype) | `string` | Optional | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-ptype.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pType") |
|
||||
| [pLevel](#plevel) | `number` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-plevel.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pLevel") |
|
||||
|
||||
## deviceId
|
||||
|
||||
|
@ -718,3 +720,39 @@ https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData
|
|||
### pk Type
|
||||
|
||||
`string`
|
||||
|
||||
## pType
|
||||
|
||||
|
||||
|
||||
`pType`
|
||||
|
||||
* is optional
|
||||
|
||||
* Type: `string`
|
||||
|
||||
* cannot be null
|
||||
|
||||
* defined in: [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-ptype.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pType")
|
||||
|
||||
### pType Type
|
||||
|
||||
`string`
|
||||
|
||||
## pLevel
|
||||
|
||||
|
||||
|
||||
`pLevel`
|
||||
|
||||
* is required
|
||||
|
||||
* Type: `number`
|
||||
|
||||
* cannot be null
|
||||
|
||||
* defined in: [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-plevel.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pLevel")
|
||||
|
||||
### pLevel Type
|
||||
|
||||
`number`
|
||||
|
|
|
@ -435,6 +435,8 @@ Reference this group by using
|
|||
| [qOrLater](#qorlater) | `boolean` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-qorlater.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/qOrLater") |
|
||||
| [mFlags](#mflags) | `number` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-mflags.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/mFlags") |
|
||||
| [pk](#pk) | `string` | Optional | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-pk.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pk") |
|
||||
| [pType](#ptype) | `string` | Optional | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-ptype.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pType") |
|
||||
| [pLevel](#plevel) | `number` | Required | cannot be null | [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-plevel.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pLevel") |
|
||||
|
||||
### deviceId
|
||||
|
||||
|
@ -1104,6 +1106,42 @@ Reference this group by using
|
|||
|
||||
`string`
|
||||
|
||||
### pType
|
||||
|
||||
|
||||
|
||||
`pType`
|
||||
|
||||
* is optional
|
||||
|
||||
* Type: `string`
|
||||
|
||||
* cannot be null
|
||||
|
||||
* defined in: [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-ptype.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pType")
|
||||
|
||||
#### pType Type
|
||||
|
||||
`string`
|
||||
|
||||
### pLevel
|
||||
|
||||
|
||||
|
||||
`pLevel`
|
||||
|
||||
* is required
|
||||
|
||||
* Type: `number`
|
||||
|
||||
* cannot be null
|
||||
|
||||
* defined in: [ServerDataStatus](serverdatastatus-definitions-serverdevicedata-properties-plevel.md "https://timelimit.io/ServerDataStatus#/definitions/ServerDeviceData/properties/pLevel")
|
||||
|
||||
#### pLevel Type
|
||||
|
||||
`number`
|
||||
|
||||
## Definitions group ProtectionLevel
|
||||
|
||||
Reference this group by using
|
||||
|
|
192
other/mail/account-deleted/html.ejs
Normal file
192
other/mail/account-deleted/html.ejs
Normal 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;">
|
||||
</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> © <%= 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>
|
44
other/mail/account-deleted/htmltemplate-src.txt
Normal file
44
other/mail/account-deleted/htmltemplate-src.txt
Normal file
|
@ -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>
|
||||
© <%= mailimprint %>
|
||||
</p>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
</mjml>
|
1
other/mail/account-deleted/subject.ejs
Normal file
1
other/mail/account-deleted/subject.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
Konto gelöscht/Account deleted
|
13
other/mail/account-deleted/text.ejs
Normal file
13
other/mail/account-deleted/text.ejs
Normal 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 %>
|
5296
package-lock.json
generated
5296
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
package.json
12
package.json
|
@ -38,12 +38,11 @@
|
|||
"@types/lodash": "^4.14.166",
|
||||
"@types/node": "^16.11.59",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/umzug": "^2.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||
"@typescript-eslint/parser": "^7.18.0",
|
||||
"eslint": "^8.7.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "^4.4.4",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-json-schema": "^0.52.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -63,10 +62,9 @@
|
|||
"rate-limiter-flexible": "^2.1.15",
|
||||
"sequelize": "^6.25.5",
|
||||
"socket.io": "^4.0.1",
|
||||
"sqlite3": "^4.0.0",
|
||||
"umzug": "^2.3.0"
|
||||
"umzug": "^3.8.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"sqlite3": "^4.0.0"
|
||||
"sqlite3": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* server component for the TimeLimit App
|
||||
* Copyright (C) 2019 - 2022 Jonas Lochmann
|
||||
* Copyright (C) 2019 - 2024 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 = [
|
||||
|
@ -57,7 +58,9 @@ const allTypes = [
|
|||
|
||||
const settings = {
|
||||
required: true,
|
||||
noExtraProps: true
|
||||
noExtraProps: true,
|
||||
// otherwise it finds errors in dependencies that we don't care about
|
||||
ignoreErrors: true
|
||||
};
|
||||
|
||||
const compilerOptions = {
|
||||
|
|
|
@ -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
|
||||
|
@ -15,6 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { minPlatformLevel, maxPlatformLevel, minPlatformTypeLength, maxPlatformTypeLength } from '../database/device'
|
||||
import { NewPermissionStatus } from '../model/newpermissionstatus'
|
||||
import { ProtectionLevel } from '../model/protectionlevel'
|
||||
import { RuntimePermissionStatus } from '../model/runtimepermissionstatus'
|
||||
|
@ -33,6 +34,8 @@ export class UpdateDeviceStatusAction extends AppLogicAction {
|
|||
readonly didReboot: boolean
|
||||
readonly isQOrLaterNow: boolean
|
||||
readonly addedManipulationFlags: number
|
||||
readonly platformType?: string
|
||||
readonly platformLevel?: number
|
||||
|
||||
constructor ({
|
||||
newProtetionLevel,
|
||||
|
@ -43,7 +46,9 @@ export class UpdateDeviceStatusAction extends AppLogicAction {
|
|||
newAppVersion,
|
||||
didReboot,
|
||||
isQOrLaterNow,
|
||||
addedManipulationFlags
|
||||
addedManipulationFlags,
|
||||
platformType,
|
||||
platformLevel
|
||||
}: {
|
||||
newProtetionLevel?: ProtectionLevel
|
||||
newUsageStatsPermissionStatus?: RuntimePermissionStatus
|
||||
|
@ -54,6 +59,8 @@ export class UpdateDeviceStatusAction extends AppLogicAction {
|
|||
didReboot: boolean
|
||||
isQOrLaterNow: boolean
|
||||
addedManipulationFlags: number
|
||||
platformType?: string
|
||||
platformLevel?: number
|
||||
}) {
|
||||
super()
|
||||
|
||||
|
@ -67,6 +74,20 @@ export class UpdateDeviceStatusAction extends AppLogicAction {
|
|||
|
||||
assertSafeInteger({ actionType, field: 'addedManipulationFlags', value: addedManipulationFlags })
|
||||
|
||||
if (platformType !== undefined) {
|
||||
if (platformType.length < minPlatformTypeLength || platformType.length > maxPlatformTypeLength) {
|
||||
throwOutOfRange({ actionType, field: 'platformType.length', value: platformType.length })
|
||||
}
|
||||
}
|
||||
|
||||
if (platformLevel !== undefined) {
|
||||
assertSafeInteger({ actionType, field: 'platformLevel', value: platformLevel })
|
||||
|
||||
if (platformLevel < minPlatformLevel || platformLevel > maxPlatformLevel) {
|
||||
throwOutOfRange({ actionType, field: 'platformLevel', value: platformLevel })
|
||||
}
|
||||
}
|
||||
|
||||
this.newProtetionLevel = newProtetionLevel
|
||||
this.newUsageStatsPermissionStatus = newUsageStatsPermissionStatus
|
||||
this.newNotificationAccessPermission = newNotificationAccessPermission
|
||||
|
@ -76,6 +97,8 @@ export class UpdateDeviceStatusAction extends AppLogicAction {
|
|||
this.didReboot = didReboot
|
||||
this.isQOrLaterNow = isQOrLaterNow
|
||||
this.addedManipulationFlags = addedManipulationFlags
|
||||
this.platformType = platformType
|
||||
this.platformLevel = platformLevel
|
||||
}
|
||||
|
||||
static parse = ({
|
||||
|
@ -87,7 +110,9 @@ export class UpdateDeviceStatusAction extends AppLogicAction {
|
|||
appVersion,
|
||||
didReboot,
|
||||
isQOrLaterNow,
|
||||
addedManipulationFlags
|
||||
addedManipulationFlags,
|
||||
platformType,
|
||||
platformLevel
|
||||
}: SerializedUpdateDeviceStatusAction) => (
|
||||
new UpdateDeviceStatusAction({
|
||||
newProtetionLevel: protectionLevel,
|
||||
|
@ -98,7 +123,9 @@ export class UpdateDeviceStatusAction extends AppLogicAction {
|
|||
newAppVersion: appVersion,
|
||||
didReboot: !!didReboot,
|
||||
isQOrLaterNow: !!isQOrLaterNow,
|
||||
addedManipulationFlags: addedManipulationFlags || 0
|
||||
addedManipulationFlags: addedManipulationFlags || 0,
|
||||
platformType: platformType,
|
||||
platformLevel: platformLevel
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -114,4 +141,6 @@ export interface SerializedUpdateDeviceStatusAction {
|
|||
didReboot?: boolean
|
||||
isQOrLaterNow?: boolean
|
||||
addedManipulationFlags?: number
|
||||
platformType?: string
|
||||
platformLevel?: number
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
@ -1982,6 +1982,12 @@ const definitions = {
|
|||
},
|
||||
"addedManipulationFlags": {
|
||||
"type": "number"
|
||||
},
|
||||
"platformType": {
|
||||
"type": "string"
|
||||
},
|
||||
"platformLevel": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
@ -2164,6 +2170,12 @@ const definitions = {
|
|||
},
|
||||
"pk": {
|
||||
"type": "string"
|
||||
},
|
||||
"pType": {
|
||||
"type": "string"
|
||||
},
|
||||
"pLevel": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
@ -2192,6 +2204,7 @@ const definitions = {
|
|||
"model",
|
||||
"name",
|
||||
"networkTime",
|
||||
"pLevel",
|
||||
"qOrLater",
|
||||
"reboot",
|
||||
"rebootIsManipulation",
|
||||
|
@ -3493,3 +3506,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#"
|
||||
})
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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
|
||||
|
@ -37,6 +37,12 @@ export const DeviceManipulationFlags = {
|
|||
ALL: 1
|
||||
}
|
||||
|
||||
export const minPlatformTypeLength = 1
|
||||
export const maxPlatformTypeLength = 8
|
||||
|
||||
export const minPlatformLevel = 0
|
||||
export const maxPlatformLevel = 128
|
||||
|
||||
export interface DeviceAttributesVersion1 {
|
||||
familyId: string
|
||||
deviceId: string
|
||||
|
@ -119,11 +125,17 @@ export interface DeviceAttributesVersion14 {
|
|||
nextKeyReplySequenceNumber: string
|
||||
}
|
||||
|
||||
export interface DeviceAttributesVersion15 {
|
||||
platformType: string | null
|
||||
platformLevel: number
|
||||
}
|
||||
|
||||
export type DeviceAttributes = DeviceAttributesVersion1 & DeviceAttributesVersion2 &
|
||||
DeviceAttributesVersion3 & DeviceAttributesVersion4 & DeviceAttributesVersion5 &
|
||||
DeviceAttributesVersion6 & DeviceAttributesVersion7 & DeviceAttributesVersion8 &
|
||||
DeviceAttributesVersion9 & DeviceAttributesVersion10 & DeviceAttributesVersion11 &
|
||||
DeviceAttributesVersion12 & DeviceAttributesVersion13 & DeviceAttributesVersion14
|
||||
DeviceAttributesVersion12 & DeviceAttributesVersion13 & DeviceAttributesVersion14 &
|
||||
DeviceAttributesVersion15
|
||||
|
||||
export type DeviceModel = Sequelize.Model<DeviceAttributes> & DeviceAttributes
|
||||
export type DeviceModelStatic = typeof Sequelize.Model & {
|
||||
|
@ -305,6 +317,26 @@ export const attributesVersion14: SequelizeAttributes<DeviceAttributesVersion14>
|
|||
}
|
||||
}
|
||||
|
||||
export const attributesVersion15: SequelizeAttributes<DeviceAttributesVersion15> = {
|
||||
platformType: {
|
||||
type: Sequelize.STRING(maxPlatformTypeLength),
|
||||
allowNull: true,
|
||||
defaultValue: null,
|
||||
validate: {
|
||||
len: [minPlatformTypeLength, maxPlatformTypeLength]
|
||||
}
|
||||
},
|
||||
platformLevel: {
|
||||
type: Sequelize.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: minPlatformLevel,
|
||||
validate: {
|
||||
min: minPlatformLevel,
|
||||
max: maxPlatformLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const attributes: SequelizeAttributes<DeviceAttributes> = {
|
||||
...attributesVersion1,
|
||||
...attributesVersion2,
|
||||
|
@ -319,7 +351,8 @@ export const attributes: SequelizeAttributes<DeviceAttributes> = {
|
|||
...attributesVersion11,
|
||||
...attributesVersion12,
|
||||
...attributesVersion13,
|
||||
...attributesVersion14
|
||||
...attributesVersion14,
|
||||
...attributesVersion15
|
||||
}
|
||||
|
||||
export const createDeviceModel = (sequelize: Sequelize.Sequelize): DeviceModelStatic => sequelize.define('Device', attributes) as DeviceModelStatic
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* server component for the TimeLimit App
|
||||
* Copyright (C) 2019 - 2022 Jonas Lochmann
|
||||
* Copyright (C) 2019 - 2024 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,7 +26,7 @@ export const config = {
|
|||
expireTimeRounding: 1000 * 60 * 15
|
||||
}
|
||||
|
||||
export function calculateExpireTime(now: bigint): BigInt {
|
||||
export function calculateExpireTime(now: bigint): bigint {
|
||||
const expireBaseTime = now + BigInt(config.expireDelay)
|
||||
const expireTime = expireBaseTime - expireBaseTime % BigInt(config.expireTimeRounding) + BigInt(config.expireTimeRounding)
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 { QueryInterface, Sequelize, Transaction } from 'sequelize'
|
||||
import { attributesVersion15 as deviceAttributes } from '../../device'
|
||||
|
||||
export async function up (queryInterface: QueryInterface, sequelize: Sequelize) {
|
||||
await sequelize.transaction({
|
||||
type: Transaction.TYPES.EXCLUSIVE
|
||||
}, async (transaction) => {
|
||||
await queryInterface.addColumn('Devices', 'platformType', {
|
||||
...deviceAttributes.platformType
|
||||
}, {
|
||||
transaction
|
||||
})
|
||||
|
||||
await queryInterface.addColumn('Devices', 'platformLevel', {
|
||||
...deviceAttributes.platformLevel
|
||||
}, {
|
||||
transaction
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function down (queryInterface: QueryInterface, sequelize: Sequelize) {
|
||||
await sequelize.transaction({
|
||||
type: Transaction.TYPES.EXCLUSIVE
|
||||
}, async (transaction) => {
|
||||
await queryInterface.removeColumn('Devices', 'platformLevel', { transaction })
|
||||
await queryInterface.removeColumn('Devices', 'platformType', { transaction })
|
||||
})
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* server component for the TimeLimit App
|
||||
* Copyright (C) 2019 Jonas Lochmann
|
||||
* Copyright (C) 2019 - 2024 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,18 +17,26 @@
|
|||
|
||||
import { resolve } from 'path'
|
||||
import { Sequelize } from 'sequelize'
|
||||
import * as Umzug from 'umzug'
|
||||
import { Umzug, SequelizeStorage } from 'umzug'
|
||||
|
||||
export const createUmzug = (sequelize: Sequelize) => (
|
||||
new Umzug({
|
||||
storage: 'sequelize',
|
||||
storageOptions: {
|
||||
sequelize
|
||||
},
|
||||
storage: new SequelizeStorage({ sequelize }),
|
||||
migrations: {
|
||||
params: [sequelize.getQueryInterface(), sequelize],
|
||||
path: resolve(__dirname, '../../../build/database/migration/migrations'),
|
||||
pattern: /^\d+[\w-]+\.js$/
|
||||
glob: resolve(__dirname, '../../../build/database/migration/migrations/*.js'),
|
||||
resolve: ({ name, path }) => {
|
||||
if (!path) throw new Error()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const migration = require(path)
|
||||
|
||||
return {
|
||||
name,
|
||||
up: async () => migration.up(sequelize.getQueryInterface(), sequelize),
|
||||
down: async () => migration.down(sequelize.getQueryInterface(), sequelize),
|
||||
}
|
||||
},
|
||||
},
|
||||
logger: console
|
||||
})
|
||||
)
|
||||
|
|
|
@ -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
src/function/cleanup/account-deletion.ts
Normal file
107
src/function/cleanup/account-deletion.ts
Normal 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)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
|
@ -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,7 +29,6 @@ export async function deleteFamilies ({ database, familiyIds }: {
|
|||
return
|
||||
}
|
||||
|
||||
await database.transaction(async (transaction) => {
|
||||
// category
|
||||
await database.category.destroy({
|
||||
where: {
|
||||
|
@ -79,6 +79,16 @@ export async function deleteFamilies ({ database, familiyIds }: {
|
|||
transaction
|
||||
})
|
||||
|
||||
// session durations
|
||||
await database.sessionDuration.destroy({
|
||||
where: {
|
||||
familyId: {
|
||||
[Sequelize.Op.in]: familiyIds
|
||||
}
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
||||
// user
|
||||
await database.user.destroy({
|
||||
where: {
|
||||
|
@ -141,5 +151,4 @@ export async function deleteFamilies ({ database, familiyIds }: {
|
|||
},
|
||||
transaction
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,10 +26,13 @@ export async function deleteOldFamilies (database: Database) {
|
|||
if (oldFamilyIds.length > 0) {
|
||||
const familyIdsToDelete = oldFamilyIds.slice(0, 256) /* limit to 256 families per execution */
|
||||
|
||||
await database.transaction(async (transaction) => {
|
||||
await deleteFamilies({
|
||||
database,
|
||||
transaction,
|
||||
familiyIds: familyIdsToDelete
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -66,5 +66,7 @@ export const prepareDeviceEntry = ({ familyId, userId, deviceAuthToken, deviceId
|
|||
isQorLater: false,
|
||||
manipulationFlags: 0,
|
||||
publicKey: null,
|
||||
nextKeyReplySequenceNumber: '1'
|
||||
nextKeyReplySequenceNumber: '1',
|
||||
platformType: null,
|
||||
platformLevel: 0
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
@ -15,6 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import * as Sequelize from 'sequelize'
|
||||
import { AddUsedTimeActionVersion2 } from '../../../../action'
|
||||
import { EventHandler } from '../../../../monitoring/eventhandler'
|
||||
import { MinuteOfDay } from '../../../../util/minuteofday'
|
||||
|
@ -22,6 +23,8 @@ import { Cache } from '../cache'
|
|||
import { IllegalStateException, SourceDeviceNotFoundException } from '../exception/illegal-state'
|
||||
import { getRoundedTimestamp as getRoundedTimestampForUsedTime } from './addusedtime'
|
||||
|
||||
const tolerance = 5 * 1000 // 5 seconds
|
||||
|
||||
export const getRoundedTimestampForSessionDuration = () => {
|
||||
const now = Date.now()
|
||||
|
||||
|
@ -129,11 +132,32 @@ export async function dispatchAddUsedTimeVersion2 ({ deviceId, action, cache, ev
|
|||
}
|
||||
}
|
||||
} else {
|
||||
const oldTime: number | null = await cache.database.usedTime.aggregate(
|
||||
'usedTime',
|
||||
'MAX',
|
||||
{
|
||||
where: {
|
||||
familyId: cache.familyId,
|
||||
categoryId: item.categoryId,
|
||||
dayOfEpoch: action.dayOfEpoch,
|
||||
startMinuteOfDay: {
|
||||
[Sequelize.Op.gte]: start
|
||||
},
|
||||
endMinuteOfDay: {
|
||||
[Sequelize.Op.lte]: end
|
||||
}
|
||||
},
|
||||
transaction: cache.transaction
|
||||
}
|
||||
) || 0
|
||||
|
||||
if (oldTime !== null && typeof oldTime !== 'number') throw new Error()
|
||||
|
||||
await cache.database.usedTime.create({
|
||||
familyId: cache.familyId,
|
||||
categoryId: item.categoryId,
|
||||
dayOfEpoch: action.dayOfEpoch,
|
||||
usedTime: Math.max(0, Math.min(item.timeToAdd, lengthInMs)),
|
||||
usedTime: Math.max(0, Math.min(oldTime + item.timeToAdd, lengthInMs)),
|
||||
lastUpdate: roundedTimestampForUsedTime,
|
||||
startMinuteOfDay: start,
|
||||
endMinuteOfDay: end
|
||||
|
@ -168,6 +192,39 @@ export async function dispatchAddUsedTimeVersion2 ({ deviceId, action, cache, ev
|
|||
transaction: cache.transaction
|
||||
})
|
||||
|
||||
const oldDuration: () => Promise<number> = async () => {
|
||||
const fittingDurationItems = await cache.database.sessionDuration.findAll({
|
||||
where: {
|
||||
familyId: cache.familyId,
|
||||
categoryId: item.categoryId,
|
||||
startMinuteOfDay: {
|
||||
[Sequelize.Op.gte]: limit.start
|
||||
},
|
||||
endMinuteOfDay: {
|
||||
[Sequelize.Op.lte]: limit.end
|
||||
},
|
||||
maxSessionDuration: {
|
||||
[Sequelize.Op.gte]: limit.duration
|
||||
},
|
||||
sessionPauseDuration: {
|
||||
[Sequelize.Op.lte]: limit.pause
|
||||
}
|
||||
},
|
||||
transaction: cache.transaction
|
||||
})
|
||||
|
||||
const fittingDurationItemsLastUsageFiltered =
|
||||
hasTrustedTimestamp ?
|
||||
fittingDurationItems.filter((it) => {
|
||||
action.trustedTimestamp - item.timeToAdd <=
|
||||
parseInt(it.lastUsage, 10) + it.sessionPauseDuration - tolerance
|
||||
}) : fittingDurationItems
|
||||
|
||||
return fittingDurationItemsLastUsageFiltered
|
||||
.map((it) => it.lastSessionDuration)
|
||||
.reduce((a, b) => Math.max(a, b), 0)
|
||||
}
|
||||
|
||||
if (oldItem) {
|
||||
let extendSession: boolean
|
||||
|
||||
|
@ -188,14 +245,13 @@ export async function dispatchAddUsedTimeVersion2 ({ deviceId, action, cache, ev
|
|||
* Due to this, a session is reset if it would be over in a few seconds, too.
|
||||
*/
|
||||
|
||||
const tolerance = 5 * 1000 // 5 seconds
|
||||
const timeWhenStartingCurrentUsage = action.trustedTimestamp - item.timeToAdd
|
||||
const nextSessionStart = parseInt(oldItem.lastUsage, 10) + oldItem.sessionPauseDuration - tolerance
|
||||
|
||||
extendSession = timeWhenStartingCurrentUsage <= nextSessionStart
|
||||
}
|
||||
|
||||
oldItem.lastSessionDuration = extendSession ? oldItem.lastSessionDuration + item.timeToAdd : item.timeToAdd
|
||||
oldItem.lastSessionDuration = extendSession ? oldItem.lastSessionDuration + item.timeToAdd : await oldDuration() + item.timeToAdd
|
||||
oldItem.roundedLastUpdate = roundedTimestampForSessionDuration
|
||||
|
||||
if (hasTrustedTimestamp) {
|
||||
|
@ -215,7 +271,7 @@ export async function dispatchAddUsedTimeVersion2 ({ deviceId, action, cache, ev
|
|||
endMinuteOfDay: limit.end,
|
||||
// end of primary key
|
||||
lastUsage: action.trustedTimestamp.toString(10),
|
||||
lastSessionDuration: item.timeToAdd,
|
||||
lastSessionDuration: await oldDuration() + item.timeToAdd,
|
||||
roundedLastUpdate: roundedTimestampForSessionDuration
|
||||
}, { transaction: cache.transaction })
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -155,6 +155,18 @@ export async function dispatchUpdateDeviceStatus ({ deviceId, action, cache }: {
|
|||
}
|
||||
}
|
||||
|
||||
if (action.platformType !== undefined) {
|
||||
if (action.platformType !== deviceEntry.platformType) {
|
||||
deviceEntry.platformType = action.platformType
|
||||
}
|
||||
}
|
||||
|
||||
if (action.platformLevel !== undefined) {
|
||||
if (action.platformLevel !== deviceEntry.platformLevel) {
|
||||
deviceEntry.platformLevel = action.platformLevel
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const effectiveManipulationFlags = action.addedManipulationFlags & DeviceManipulationFlags.ALL
|
||||
|
||||
|
|
|
@ -66,7 +66,9 @@ export async function getDeviceList ({ database, transaction, familyEntry }: {
|
|||
activityLevelBlocking: item.activityLevelBlocking,
|
||||
qOrLater: item.isQorLater,
|
||||
mFlags: item.manipulationFlags,
|
||||
pk: item.publicKey ? item.publicKey.toString('base64') : undefined
|
||||
pk: item.publicKey ? item.publicKey.toString('base64') : undefined,
|
||||
pType: item.platformType || undefined,
|
||||
pLevel: item.platformLevel
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,6 +103,8 @@ export interface ServerDeviceData {
|
|||
qOrLater: boolean
|
||||
mFlags: number // manipulation flags
|
||||
pk?: string // public key
|
||||
pType?: string
|
||||
pLevel: number
|
||||
}
|
||||
|
||||
export interface ServerUpdatedCategoryBaseData {
|
||||
|
|
|
@ -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.'
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"noUnusedParameters": false,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true
|
||||
"sourceMap": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue