mirror of
https://codeberg.org/timelimit/timelimit-server.git
synced 2026-08-31 19:03:45 +02:00
Extend transaction usage
This commit is contained in:
+22
-18
@@ -96,27 +96,31 @@ export const createAdminRouter = ({ database, websocket, eventHandler }: {
|
||||
throw new BadRequest()
|
||||
}
|
||||
|
||||
const userEntryUnsafe = await database.user.findOne({
|
||||
where: {
|
||||
mail
|
||||
},
|
||||
attributes: ['familyId']
|
||||
})
|
||||
await database.transaction(async (transaction) => {
|
||||
const userEntryUnsafe = await database.user.findOne({
|
||||
where: {
|
||||
mail
|
||||
},
|
||||
attributes: ['familyId'],
|
||||
transaction
|
||||
})
|
||||
|
||||
if (!userEntryUnsafe) {
|
||||
throw new Conflict('no user with specified mail address')
|
||||
}
|
||||
if (!userEntryUnsafe) {
|
||||
throw new Conflict('no user with specified mail address')
|
||||
}
|
||||
|
||||
const userEntry = {
|
||||
familyId: userEntryUnsafe.familyId
|
||||
}
|
||||
const userEntry = {
|
||||
familyId: userEntryUnsafe.familyId
|
||||
}
|
||||
|
||||
await addPurchase({
|
||||
database,
|
||||
familyId: userEntry.familyId,
|
||||
type,
|
||||
transactionId: 'manual-' + type + '-' + generatePurchaseId(),
|
||||
websocket
|
||||
await addPurchase({
|
||||
database,
|
||||
familyId: userEntry.familyId,
|
||||
type,
|
||||
transactionId: 'manual-' + type + '-' + generatePurchaseId(),
|
||||
websocket,
|
||||
transaction
|
||||
})
|
||||
})
|
||||
|
||||
res.json({ ok: true })
|
||||
|
||||
+35
-22
@@ -19,7 +19,7 @@ import { json } from 'body-parser'
|
||||
import { Router } from 'express'
|
||||
import { BadRequest, Forbidden, Unauthorized } from 'http-errors'
|
||||
import { config } from '../config'
|
||||
import { Database } from '../database'
|
||||
import { Database, Transaction } from '../database'
|
||||
import { removeDevice } from '../function/device/remove-device'
|
||||
import { canRecoverPassword } from '../function/parent/can-recover-password'
|
||||
import { createAddDeviceToken } from '../function/parent/create-add-device-token'
|
||||
@@ -46,7 +46,9 @@ export const createParentRouter = ({ database, websocket }: {database: Database,
|
||||
}
|
||||
|
||||
const { mailAuthToken } = req.body
|
||||
const { status, mail } = await getStatusByMailToken({ database, mailAuthToken })
|
||||
const { status, mail } = await database.transaction(async (transaction) => {
|
||||
return getStatusByMailToken({ database, mailAuthToken, transaction })
|
||||
})
|
||||
|
||||
res.json({
|
||||
status,
|
||||
@@ -148,15 +150,17 @@ export const createParentRouter = ({ database, websocket }: {database: Database,
|
||||
}
|
||||
})
|
||||
|
||||
async function assertAuthValidAndReturnDeviceEntry ({ deviceAuthToken, parentId, secondPasswordHash }: {
|
||||
async function assertAuthValidAndReturnDeviceEntry ({ deviceAuthToken, parentId, secondPasswordHash, transaction }: {
|
||||
deviceAuthToken: string
|
||||
parentId: string
|
||||
secondPasswordHash: string
|
||||
transaction: Transaction
|
||||
}) {
|
||||
const deviceEntry = await database.device.findOne({
|
||||
where: {
|
||||
deviceAuthToken: deviceAuthToken
|
||||
}
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
||||
if (!deviceEntry) {
|
||||
@@ -173,7 +177,8 @@ export const createParentRouter = ({ database, websocket }: {database: Database,
|
||||
familyId: deviceEntry.familyId,
|
||||
type: 'parent',
|
||||
userId: deviceEntry.currentUserId
|
||||
}
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
||||
if (!parentEntry) {
|
||||
@@ -186,7 +191,8 @@ export const createParentRouter = ({ database, websocket }: {database: Database,
|
||||
type: 'parent',
|
||||
userId: parentId,
|
||||
secondPasswordHash: secondPasswordHash
|
||||
}
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
||||
if (!parentEntry) {
|
||||
@@ -203,13 +209,16 @@ export const createParentRouter = ({ database, websocket }: {database: Database,
|
||||
throw new BadRequest()
|
||||
}
|
||||
|
||||
const deviceEntry = await assertAuthValidAndReturnDeviceEntry({
|
||||
deviceAuthToken: req.body.deviceAuthToken,
|
||||
parentId: req.body.parentId,
|
||||
secondPasswordHash: req.body.parentPasswordSecondHash
|
||||
})
|
||||
const { token, deviceId } = await database.transaction(async (transaction) => {
|
||||
const deviceEntry = await assertAuthValidAndReturnDeviceEntry({
|
||||
deviceAuthToken: req.body.deviceAuthToken,
|
||||
parentId: req.body.parentId,
|
||||
secondPasswordHash: req.body.parentPasswordSecondHash,
|
||||
transaction
|
||||
})
|
||||
|
||||
const { token, deviceId } = await createAddDeviceToken({ familyId: deviceEntry.familyId, database })
|
||||
return createAddDeviceToken({ familyId: deviceEntry.familyId, database, transaction })
|
||||
})
|
||||
|
||||
res.json({ token, deviceId })
|
||||
} catch (ex) {
|
||||
@@ -244,17 +253,21 @@ export const createParentRouter = ({ database, websocket }: {database: Database,
|
||||
throw new BadRequest()
|
||||
}
|
||||
|
||||
const deviceEntry = await assertAuthValidAndReturnDeviceEntry({
|
||||
deviceAuthToken: req.body.deviceAuthToken,
|
||||
parentId: req.body.parentUserId,
|
||||
secondPasswordHash: req.body.parentPasswordSecondHash
|
||||
})
|
||||
await database.transaction(async (transaction) => {
|
||||
const deviceEntry = await assertAuthValidAndReturnDeviceEntry({
|
||||
deviceAuthToken: req.body.deviceAuthToken,
|
||||
parentId: req.body.parentUserId,
|
||||
secondPasswordHash: req.body.parentPasswordSecondHash,
|
||||
transaction
|
||||
})
|
||||
|
||||
await removeDevice({
|
||||
database,
|
||||
familyId: deviceEntry.familyId,
|
||||
deviceId: req.body.deviceId,
|
||||
websocket
|
||||
await removeDevice({
|
||||
database,
|
||||
familyId: deviceEntry.familyId,
|
||||
deviceId: req.body.deviceId,
|
||||
websocket,
|
||||
transaction
|
||||
})
|
||||
})
|
||||
|
||||
res.json({ ok: true })
|
||||
|
||||
+53
-46
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* server component for the TimeLimit App
|
||||
* Copyright (C) 2019 Jonas Lochmann
|
||||
* Copyright (C) 2019 - 2020 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
|
||||
@@ -47,12 +47,15 @@ export const createPurchaseRouter = ({ database, websocket }: {
|
||||
throw new BadRequest()
|
||||
}
|
||||
|
||||
const familyEntry = await requireFamilyEntry({
|
||||
database,
|
||||
deviceAuthToken: req.body.deviceAuthToken
|
||||
})
|
||||
const result: boolean = await database.transaction(async (transaction) => {
|
||||
const familyEntry = await requireFamilyEntry({
|
||||
database,
|
||||
deviceAuthToken: req.body.deviceAuthToken,
|
||||
transaction
|
||||
})
|
||||
|
||||
const result = canDoNextPurchase({ fullVersionUntil: parseInt(familyEntry.fullVersionUntil, 10) })
|
||||
return canDoNextPurchase({ fullVersionUntil: parseInt(familyEntry.fullVersionUntil, 10) })
|
||||
})
|
||||
|
||||
res.json({
|
||||
canDoPurchase: result ? 'yes' : 'no due to old purchase',
|
||||
@@ -69,56 +72,60 @@ export const createPurchaseRouter = ({ database, websocket }: {
|
||||
throw new BadRequest()
|
||||
}
|
||||
|
||||
const deviceEntryUnsafe = await database.device.findOne({
|
||||
where: {
|
||||
deviceAuthToken: req.body.deviceAuthToken
|
||||
},
|
||||
attributes: ['familyId']
|
||||
})
|
||||
await database.transaction(async (transaction) => {
|
||||
const deviceEntryUnsafe = await database.device.findOne({
|
||||
where: {
|
||||
deviceAuthToken: req.body.deviceAuthToken
|
||||
},
|
||||
attributes: ['familyId'],
|
||||
transaction
|
||||
})
|
||||
|
||||
if (!deviceEntryUnsafe) {
|
||||
throw new Unauthorized()
|
||||
}
|
||||
if (!deviceEntryUnsafe) {
|
||||
throw new Unauthorized()
|
||||
}
|
||||
|
||||
const deviceEntry = {
|
||||
familyId: deviceEntryUnsafe.familyId
|
||||
}
|
||||
const deviceEntry = {
|
||||
familyId: deviceEntryUnsafe.familyId
|
||||
}
|
||||
|
||||
if (!isGooglePlayPurchaseSignatureValid({
|
||||
receipt: req.body.receipt,
|
||||
signature: req.body.signature
|
||||
})) {
|
||||
throw new Conflict()
|
||||
}
|
||||
if (!isGooglePlayPurchaseSignatureValid({
|
||||
receipt: req.body.receipt,
|
||||
signature: req.body.signature
|
||||
})) {
|
||||
throw new Conflict()
|
||||
}
|
||||
|
||||
const receipt = JSON.parse(req.body.receipt)
|
||||
const receipt = JSON.parse(req.body.receipt)
|
||||
|
||||
if (typeof receipt !== 'object') {
|
||||
throw new Conflict()
|
||||
}
|
||||
if (typeof receipt !== 'object') {
|
||||
throw new Conflict()
|
||||
}
|
||||
|
||||
let type: 'month' | 'year'
|
||||
let type: 'month' | 'year'
|
||||
|
||||
if (receipt.productId === 'premium_year_2018') {
|
||||
type = 'year'
|
||||
} else if (receipt.productId === 'premium_month_2018') {
|
||||
type = 'month'
|
||||
} else {
|
||||
throw new Conflict()
|
||||
}
|
||||
if (receipt.productId === 'premium_year_2018') {
|
||||
type = 'year'
|
||||
} else if (receipt.productId === 'premium_month_2018') {
|
||||
type = 'month'
|
||||
} else {
|
||||
throw new Conflict()
|
||||
}
|
||||
|
||||
const orderId = receipt.orderId
|
||||
const orderId = receipt.orderId
|
||||
|
||||
if (typeof orderId !== 'string') {
|
||||
throw new Conflict()
|
||||
}
|
||||
if (typeof orderId !== 'string') {
|
||||
throw new Conflict()
|
||||
}
|
||||
|
||||
await addPurchase({
|
||||
database,
|
||||
familyId: deviceEntry.familyId,
|
||||
type,
|
||||
transactionId: orderId,
|
||||
websocket
|
||||
await addPurchase({
|
||||
database,
|
||||
familyId: deviceEntry.familyId,
|
||||
type,
|
||||
transactionId: orderId,
|
||||
websocket,
|
||||
transaction
|
||||
})
|
||||
})
|
||||
|
||||
res.json({ ok: true })
|
||||
|
||||
+9
-7
@@ -20,13 +20,15 @@ import { optionalPasswordRegex, optionalSaltRegex } from '../util/password'
|
||||
|
||||
export interface ClientPushChangesRequest {
|
||||
deviceAuthToken: string
|
||||
actions: Array<{
|
||||
encodedAction: string
|
||||
sequenceNumber: number
|
||||
integrity: string
|
||||
type: 'appLogic' | 'parent' | 'child'
|
||||
userId: string
|
||||
}>
|
||||
actions: Array<ClientPushChangesRequestAction>
|
||||
}
|
||||
|
||||
export interface ClientPushChangesRequestAction {
|
||||
encodedAction: string
|
||||
sequenceNumber: number
|
||||
integrity: string
|
||||
type: 'appLogic' | 'parent' | 'child'
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface ClientPullChangesRequest {
|
||||
|
||||
+10
-5
@@ -158,14 +158,19 @@ export const createSyncRouter = ({ database, websocket, connectedDevicesManager,
|
||||
throw new BadRequest()
|
||||
}
|
||||
|
||||
const removedEntry = await database.oldDevice.findOne({
|
||||
where: {
|
||||
deviceAuthToken: req.body.deviceAuthToken
|
||||
}
|
||||
const isDeviceRemoved: boolean = await database.transaction(async (transaction) => {
|
||||
const removedEntry = await database.oldDevice.findOne({
|
||||
where: {
|
||||
deviceAuthToken: req.body.deviceAuthToken
|
||||
},
|
||||
transaction
|
||||
})
|
||||
|
||||
return !!removedEntry
|
||||
})
|
||||
|
||||
res.json({
|
||||
isDeviceRemoved: !!removedEntry
|
||||
isDeviceRemoved
|
||||
})
|
||||
} catch (ex) {
|
||||
next(ex)
|
||||
|
||||
+35
-31
@@ -4,6 +4,39 @@ const Ajv = require('ajv')
|
||||
const ajv = new Ajv()
|
||||
|
||||
const definitions = {
|
||||
"ClientPushChangesRequestAction": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"encodedAction": {
|
||||
"type": "string"
|
||||
},
|
||||
"sequenceNumber": {
|
||||
"type": "number"
|
||||
},
|
||||
"integrity": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"appLogic",
|
||||
"child",
|
||||
"parent"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"encodedAction",
|
||||
"integrity",
|
||||
"sequenceNumber",
|
||||
"type",
|
||||
"userId"
|
||||
]
|
||||
},
|
||||
"ClientDataStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2219,37 +2252,7 @@ export const isClientPushChangesRequest: (value: object) => value is ClientPushC
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"encodedAction": {
|
||||
"type": "string"
|
||||
},
|
||||
"sequenceNumber": {
|
||||
"type": "number"
|
||||
},
|
||||
"integrity": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"appLogic",
|
||||
"child",
|
||||
"parent"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"encodedAction",
|
||||
"integrity",
|
||||
"sequenceNumber",
|
||||
"type",
|
||||
"userId"
|
||||
]
|
||||
"$ref": "#/definitions/ClientPushChangesRequestAction"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2258,6 +2261,7 @@ export const isClientPushChangesRequest: (value: object) => value is ClientPushC
|
||||
"actions",
|
||||
"deviceAuthToken"
|
||||
],
|
||||
"definitions": definitions,
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
})
|
||||
export const isClientPullChangesRequest: (value: object) => value is ClientPullChangesRequest = ajv.compile({
|
||||
|
||||
Reference in New Issue
Block a user