diff --git a/package.json b/package.json
index a14ced8..b68330a 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,9 @@
"build": "npm run build:json && npm run build:ts && npm run lint",
"build:json": "node ./scripts/build-schemas.js",
"build:ts": "tsc",
+ "tool:find-old-families": "node build/cli/find-old-families.js",
+ "tool:delete-old-families": "node build/cli/delete-old-families.js",
+ "tool:count-families": "node build/cli/count-families.js",
"watch": "nodemon ./build/index.js"
},
"repository": {
diff --git a/src/cli/count-families.ts b/src/cli/count-families.ts
new file mode 100644
index 0000000..d55716b
--- /dev/null
+++ b/src/cli/count-families.ts
@@ -0,0 +1,27 @@
+/*
+ * 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 .
+ */
+
+import { defaultDatabase } from '../database'
+
+const database = defaultDatabase
+
+database.family.count()
+ .then((res) => console.log(JSON.stringify(res)))
+ .catch((ex) => {
+ console.warn(ex)
+ process.exit(1)
+ })
diff --git a/src/cli/delete-old-families.ts b/src/cli/delete-old-families.ts
new file mode 100644
index 0000000..227339d
--- /dev/null
+++ b/src/cli/delete-old-families.ts
@@ -0,0 +1,28 @@
+/*
+ * 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 .
+ */
+
+import { defaultDatabase } from '../database'
+import { deleteOldFamilies } from '../function/cleanup/delete-old-families'
+
+const database = defaultDatabase
+
+deleteOldFamilies(database)
+ .then(() => console.log('done'))
+ .catch((ex) => {
+ console.warn(ex)
+ process.exit(1)
+ })
diff --git a/src/cli/find-old-families.ts b/src/cli/find-old-families.ts
new file mode 100644
index 0000000..f21e12a
--- /dev/null
+++ b/src/cli/find-old-families.ts
@@ -0,0 +1,28 @@
+/*
+ * 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 .
+ */
+
+import { defaultDatabase } from '../database'
+import { findOldFamilyIds } from '../function/cleanup/delete-old-families'
+
+const database = defaultDatabase
+
+findOldFamilyIds(database)
+ .then((res) => console.log(JSON.stringify(res)))
+ .catch((ex) => {
+ console.warn(ex)
+ process.exit(1)
+ })
diff --git a/src/function/cleanup/delete-families.ts b/src/function/cleanup/delete-families.ts
new file mode 100644
index 0000000..028c167
--- /dev/null
+++ b/src/function/cleanup/delete-families.ts
@@ -0,0 +1,149 @@
+/*
+ * 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 .
+ */
+
+import * as Sequelize from 'sequelize'
+import { Database } from '../../database'
+
+export async function deleteFamilies ({ database, familiyIds }: {
+ database: Database
+ familiyIds: Array
+}) {
+ if (familiyIds.length === 0) {
+ return
+ }
+
+ await database.transaction(async (transaction) => {
+ // app
+ await database.app.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+
+ // app activity
+ await database.appActivity.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+
+ // category
+ await database.category.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+
+ // categoryapp
+ await database.categoryApp.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+
+ // purchase
+ await database.purchase.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+
+ // timelimitrule
+ await database.timelimitRule.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+
+ // usedtime
+ await database.usedTime.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+
+ // user
+ await database.user.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+
+ // device
+ const oldDeviceAuthTokens = await database.device.findAll({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ attributes: ['deviceAuthToken'],
+ transaction
+ }).map((item) => item.deviceAuthToken)
+
+ await database.device.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+
+ // olddevice
+ if (oldDeviceAuthTokens.length > 0) {
+ await database.oldDevice.bulkCreate(
+ oldDeviceAuthTokens.map((item) => ({
+ deviceAuthToken: item
+ }))
+ )
+ }
+
+ // family
+ await database.family.destroy({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familiyIds
+ }
+ },
+ transaction
+ })
+ })
+}
diff --git a/src/function/cleanup/delete-old-families.ts b/src/function/cleanup/delete-old-families.ts
new file mode 100644
index 0000000..b3753e7
--- /dev/null
+++ b/src/function/cleanup/delete-old-families.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 .
+ */
+
+import { difference } from 'lodash'
+import * as Sequelize from 'sequelize'
+import { Database } from '../../database'
+import { deleteFamilies } from './delete-families'
+
+export async function deleteOldFamilies (database: Database) {
+ console.log('deleteOldFamilies()')
+
+ const oldFamilyIds = await findOldFamilyIds(database)
+
+ console.log('oldFamilyIds: ' + JSON.stringify(oldFamilyIds))
+
+ if (oldFamilyIds.length > 0) {
+ const familyIdsToDelete = oldFamilyIds.slice(0, 16) /* limit to 16 families per execution */
+
+ console.log('familyIdsToDelete: ' + JSON.stringify(familyIdsToDelete))
+
+ await deleteFamilies({
+ database,
+ familiyIds: familyIdsToDelete
+ })
+ }
+}
+
+export async function findOldFamilyIds (database: Database) {
+ const familyIdsWithExpiredLicenses = await database.family.findAll({
+ where: {
+ fullVersionUntil: {
+ [Sequelize.Op.lt]: (Date.now() - 1000 * 60 * 60 * 24 * 90 /* 90 days */).toString(10)
+ }
+ },
+ attributes: ['familyId']
+ }).map((item) => item.familyId)
+
+ if (familyIdsWithExpiredLicenses.length === 0) {
+ return []
+ }
+
+ const recentlyUsedFamilyIds = await database.device.findAll({
+ where: {
+ familyId: {
+ [Sequelize.Op.in]: familyIdsWithExpiredLicenses
+ },
+ lastConnectivity: {
+ [Sequelize.Op.lt]: (Date.now() - 1000 * 60 * 60 * 24 * 90 /* 90 days */).toString(10)
+ }
+ },
+ attributes: ['familyId']
+ }).map((item) => item.familyId)
+
+ return difference(familyIdsWithExpiredLicenses, recentlyUsedFamilyIds)
+}