"use strict";
/*
 * server component for the TimeLimit App
 * Copyright (C) 2019 - 2022 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/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSyncRouter = void 0;
const body_parser_1 = require("body-parser");
const express_1 = require("express");
const http_errors_1 = require("http-errors");
const report_device_removed_1 = require("../function/device/report-device-removed");
const apply_actions_1 = require("../function/sync/apply-actions");
const get_server_data_status_1 = require("../function/sync/get-server-data-status");
const validator_1 = require("./validator");
const getRoundedTimestampForLastConnectivity = () => {
    const now = Date.now();
    return now - (now % (1000 * 60 * 60 * 12 /* 12 hours */));
};
const createSyncRouter = ({ database, websocket, connectedDevicesManager, eventHandler }) => {
    const router = (0, express_1.Router)();
    router.post('/push-actions', (0, body_parser_1.json)({
        limit: '5120kb'
    }), async (req, res, next) => {
        try {
            eventHandler.countEvent('pushChangesRequest');
            if (!(0, validator_1.isClientPushChangesRequest)(req.body)) {
                eventHandler.countEvent('pushChangesRequest invalid');
                throw new http_errors_1.BadRequest();
            }
            const { shouldDoFullSync } = await (0, apply_actions_1.applyActionsFromDevice)({
                request: req.body,
                database,
                websocket,
                connectedDevicesManager,
                eventHandler
            });
            if (shouldDoFullSync) {
                eventHandler.countEvent('pushChangesRequest shouldDoFullSync');
            }
            res.json({
                shouldDoFullSync
            });
        }
        catch (ex) {
            next(ex);
        }
    });
    router.post('/pull-status', (0, body_parser_1.json)(), async (req, res, next) => {
        try {
            eventHandler.countEvent('pullStatusRequest');
            const { body } = req;
            if (!(0, validator_1.isClientPullChangesRequest)(body)) {
                eventHandler.countEvent('pullStatusRequest invalid');
                throw new http_errors_1.BadRequest();
            }
            const serverStatus = await database.transaction(async (transaction) => {
                const deviceEntryUnsafe = await database.device.findOne({
                    where: {
                        deviceAuthToken: body.deviceAuthToken
                    },
                    attributes: ['familyId', 'deviceId', 'lastConnectivity'],
                    transaction
                });
                if (!deviceEntryUnsafe) {
                    throw new http_errors_1.Unauthorized();
                }
                const { familyId, deviceId, lastConnectivity } = deviceEntryUnsafe;
                const now = getRoundedTimestampForLastConnectivity();
                if (parseInt(lastConnectivity, 10) !== now) {
                    await database.device.update({
                        lastConnectivity: now.toString(10)
                    }, {
                        where: {
                            deviceAuthToken: body.deviceAuthToken
                        },
                        transaction
                    });
                }
                return (0, get_server_data_status_1.generateServerDataStatus)({
                    database,
                    familyId,
                    deviceId,
                    clientStatus: body.status,
                    transaction,
                    eventHandler
                });
            });
            if (serverStatus.devices) {
                eventHandler.countEvent('pullStatusRequest devices');
            }
            if (serverStatus.apps) {
                eventHandler.countEvent('pullStatusRequest apps');
            }
            if (serverStatus.categoryBase) {
                eventHandler.countEvent('pullStatusRequest categoryBase');
            }
            if (serverStatus.categoryApp) {
                eventHandler.countEvent('pullStatusRequest categoryApp');
            }
            if (serverStatus.usedTimes) {
                eventHandler.countEvent('pullStatusRequest usedTimes');
            }
            if (serverStatus.rules) {
                eventHandler.countEvent('pullStatusRequest rules');
            }
            if (serverStatus.users) {
                eventHandler.countEvent('pullStatusRequest users');
            }
            if (serverStatus.krq) {
                eventHandler.countEvent('pullStatusRequest pendingKeyRequests');
            }
            if (serverStatus.kr) {
                eventHandler.countEvent('pullStatusRequest keyResponses');
            }
            if (serverStatus.dh) {
                eventHandler.countEvent('pullStatusRequest dh');
            }
            if (serverStatus.u2f) {
                eventHandler.countEvent('pullStatusRequest u2f');
            }
            res.json(serverStatus);
        }
        catch (ex) {
            next(ex);
        }
    });
    router.post('/report-removed', (0, body_parser_1.json)(), async (req, res, next) => {
        try {
            if (!(0, validator_1.isRequestWithAuthToken)(req.body)) {
                throw new http_errors_1.BadRequest();
            }
            await (0, report_device_removed_1.reportDeviceRemoved)({
                database,
                deviceAuthToken: req.body.deviceAuthToken,
                websocket
            });
            res.json({ ok: true });
        }
        catch (ex) {
            next(ex);
        }
    });
    router.post('/is-device-removed', (0, body_parser_1.json)(), async (req, res, next) => {
        try {
            if (!(0, validator_1.isRequestWithAuthToken)(req.body)) {
                throw new http_errors_1.BadRequest();
            }
            const isDeviceRemoved = await database.transaction(async (transaction) => {
                const removedEntry = await database.oldDevice.findOne({
                    where: {
                        deviceAuthToken: req.body.deviceAuthToken
                    },
                    transaction
                });
                return !!removedEntry;
            });
            res.json({
                isDeviceRemoved
            });
        }
        catch (ex) {
            next(ex);
        }
    });
    return router;
};
exports.createSyncRouter = createSyncRouter;
//# sourceMappingURL=sync.js.map