Extend transaction usage

This commit is contained in:
Jonas Lochmann
2020-10-02 09:58:13 +02:00
parent 62f4e368a6
commit abc2102da5
47 changed files with 1367 additions and 860 deletions
+22 -18
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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({