Initial commit

This commit is contained in:
Jonas L
2019-02-25 00:00:00 +00:00
commit 22c372e246
200 changed files with 27781 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 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 { Conflict } from 'http-errors'
import * as Sequelize from 'sequelize'
import { Database } from '../../database'
import { notifyClientsAboutChanges } from '../../function/websocket'
import { WebsocketApi } from '../../websocket'
const day = 1000 * 60 * 60 * 24
const month = day * 31
const year = day * 366
export const addPurchase = async ({ database, familyId, type, transactionId, websocket }: {
database: Database
familyId: string
type: 'month' | 'year'
transactionId: string
websocket: WebsocketApi
}) => {
const service = 'googleplay'
await database.transaction(async (transaction) => {
const oldPurchaseEntry = await database.purchase.findOne({
where: {
service,
transactionId
},
transaction
})
if (oldPurchaseEntry) {
return
}
const familyEntry = await database.family.findOne({
where: {
familyId
},
transaction,
lock: Sequelize.Transaction.LOCK.UPDATE
})
if (!familyEntry) {
throw new Conflict()
}
const previousFullVersionEndTime = familyEntry.fullVersionUntil
const newFullVersionUntil = Math.max(parseInt(familyEntry.fullVersionUntil, 10), Date.now()) + (type === 'year' ? year : month)
familyEntry.fullVersionUntil = newFullVersionUntil.toString(10)
familyEntry.hasFullVersion = true
await familyEntry.save({ transaction })
await database.purchase.create({
familyId,
service,
transactionId,
type,
loggedAt: Date.now().toString(10),
previousFullVersionEndTime,
newFullVersionEndTime: newFullVersionUntil.toString(10)
}, {
transaction
})
await notifyClientsAboutChanges({
familyId,
sourceDeviceId: null,
database,
websocket,
isImportant: true
})
})
}
@@ -0,0 +1,20 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 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/>.
*/
export const canDoNextPurchase = ({ fullVersionUntil }: {fullVersionUntil: number}) => (
fullVersionUntil < (Date.now() + 1000 * 60 * 60 * 24 * 31) // 31 days
)
+21
View File
@@ -0,0 +1,21 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 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/>.
*/
export { canDoNextPurchase } from './can-do-next-purchase'
export { requireFamilyEntry } from './require-family-entry'
export { isGooglePlayPurchaseSignatureValid, areGooglePlayPaymentsPossible } from './verification'
export { addPurchase } from './add-purchase'
@@ -0,0 +1,56 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 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 { InternalServerError, Unauthorized } from 'http-errors'
import { Database } from '../../database'
export const requireFamilyEntry = async ({ database, deviceAuthToken }: {
database: Database
deviceAuthToken: string
}) => {
const deviceEntryUnsafe = await database.device.findOne({
where: {
deviceAuthToken
},
attributes: ['familyId']
})
if (!deviceEntryUnsafe) {
throw new Unauthorized()
}
const deviceEntry = {
familyId: deviceEntryUnsafe.familyId
}
const familyEntryUnsafe = await database.family.findOne({
where: {
familyId: deviceEntry.familyId
},
attributes: ['fullVersionUntil']
})
if (!familyEntryUnsafe) {
throw new InternalServerError()
}
const familyEntry = {
fullVersionUntil: familyEntryUnsafe.fullVersionUntil
}
return familyEntry
}
+36
View File
@@ -0,0 +1,36 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 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/>.
*/
const IABVerifier: new (publicKey: string) => {
verifyReceipt: (data: string, signature: string) => boolean
} = require('iab_verifier')
const googlePlayPublicKey = process.env.GOOGLE_PLAY_PUBLIC_KEY || ''
const verifier = new IABVerifier(googlePlayPublicKey)
export const areGooglePlayPaymentsPossible = !!googlePlayPublicKey
export const isGooglePlayPurchaseSignatureValid = ({ receipt, signature }: {
receipt: string
signature: string
}) => {
if (googlePlayPublicKey) {
return verifier.verifyReceipt(receipt, signature)
} else {
return false
}
}