From 598618f6c5d7c461a8300de0063c4d2087f30530 Mon Sep 17 00:00:00 2001 From: fetok12 Date: Wed, 20 Dec 2023 15:56:24 +0300 Subject: [PATCH 1/2] User Device Token --- server/api/controllers/user.js | 67 ++++++++++++++++- server/api/swagger/user.yaml | 73 ++++++++++++++++++ .../20231220100478-create-device-token.js | 50 +++++++++++++ server/db/models/deviceToken.js | 53 +++++++++++++ server/db/models/index.js | 2 + server/messages.js | 4 +- server/utils/hollaex-tools-lib/tools/user.js | 75 ++++++++++++++++++- 7 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 server/db/migrations/20231220100478-create-device-token.js create mode 100644 server/db/models/deviceToken.js diff --git a/server/api/controllers/user.js b/server/api/controllers/user.js index 3141a084a0..1933bfa120 100644 --- a/server/api/controllers/user.js +++ b/server/api/controllers/user.js @@ -1154,6 +1154,69 @@ const userDelete = (req, res) => { }; +const createUserDeviceToken = (req, res) => { + loggerUser.verbose( + req.uuid, + 'controllers/user/createUserDeviceToken auth', + req.auth.sub + ); + + const { id: userId } = req.auth.sub; + const { token, device } = req.swagger.params.data.value; + + loggerUser.verbose( + req.uuid, + 'controllers/user/createUserDeviceToken data', + token, + device + ); + + toolsLib.user.createUserDeviceToken(userId, token, device) + .then(() => { + return res.json({ message: 'Success' }); + }) + .catch((err) => { + loggerUser.error( + req.uuid, + 'controllers/user/createUserDeviceToken', + err.message, + err.stack + ); + return res.status(err.statusCode || 400).json({ message: errorMessageConverter(err) }); + }); +} + +const getUserDeviceToken = (req, res) => { + loggerUser.verbose( + req.uuid, + 'controllers/user/getUserDeviceToken auth', + req.auth.sub + ); + + const { id: userId } = req.auth.sub; + const { device } = req.swagger.params; + + loggerUser.verbose( + req.uuid, + 'controllers/user/getUserDeviceToken data', + device + ); + + toolsLib.user.getUserDeviceToken(userId, device.value) + .then((data) => { + return res.json(data); + }) + .catch((err) => { + loggerUser.error( + req.uuid, + 'controllers/user/getUserDeviceToken', + err.message, + err.stack + ); + return res.status(err.statusCode || 400).json({ message: errorMessageConverter(err) }); + }); +} + module.exports = { signUpUser, getVerifyUser, @@ -1183,5 +1246,7 @@ module.exports = { revokeUserSession, getUserSessions, userLogout, - userDelete + userDelete, + createUserDeviceToken, + getUserDeviceToken }; diff --git a/server/api/swagger/user.yaml b/server/api/swagger/user.yaml index d54d495c4d..53dc9d11d0 100644 --- a/server/api/swagger/user.yaml +++ b/server/api/swagger/user.yaml @@ -914,6 +914,79 @@ paths: - bearer x-security-scopes: - user + /user/device-token: + x-swagger-router-controller: user + post: + operationId: createUserDeviceToken + description: Create device token for user + tags: + - User + parameters: + - name: data + in: body + required: true + schema: + type: object + required: + - token + - device + properties: + token: + type: string + maxLength: 256 + device: + type: string + maxLength: 256 + responses: + 200: + description: Success + schema: + $ref: "#/definitions/ObjectResponse" + default: + description: Error + schema: + $ref: "#/definitions/MessageResponse" + security: + - Token: [] + x-security-types: + - bearer + - hmac + x-security-scopes: + - user + get: + operationId: getUserDeviceToken + description: get device token of authenticated user + tags: + - User + parameters: + - in: query + name: device + description: device + required: true + type: string + maxLength: 256 + responses: + 200: + description: Success + schema: + $ref: "#/definitions/ObjectResponse" + 202: + description: CSV + schema: + type: string + default: + description: Error + schema: + $ref: "#/definitions/MessageResponse" + security: + - Token: [] + x-security-types: + - bearer + - hmac + x-security-scopes: + - user + x-token-permissions: + - can_read /user/trades: x-swagger-router-controller: trade get: diff --git a/server/db/migrations/20231220100478-create-device-token.js b/server/db/migrations/20231220100478-create-device-token.js new file mode 100644 index 0000000000..81e2d5758e --- /dev/null +++ b/server/db/migrations/20231220100478-create-device-token.js @@ -0,0 +1,50 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.createTable('DeviceTokens', { + id: { + type: Sequelize.INTEGER, + allowNull: false, + autoIncrement: true, + primaryKey: true, + }, + user_id: { + type: Sequelize.INTEGER, + onDelete: 'CASCADE', + allowNull: false, + references: { + model: 'Users', + key: 'id' + } + }, + token: { + type: Sequelize.STRING, + allowNull: false, + }, + device: { + type: Sequelize.STRING, + allowNull: false, + }, + status: { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: true + }, + created_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('NOW()'), + }, + updated_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('NOW()'), + }, + }); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.dropTable('DeviceTokens'); + }, +}; diff --git a/server/db/models/deviceToken.js b/server/db/models/deviceToken.js new file mode 100644 index 0000000000..1c19488c4b --- /dev/null +++ b/server/db/models/deviceToken.js @@ -0,0 +1,53 @@ +'use strict'; + +module.exports = function (sequelize, DataTypes) { + + const DeviceToken = sequelize.define( + 'DeviceToken', + { + id: { + type: DataTypes.INTEGER, + allowNull: false, + autoIncrement: true, + primaryKey: true, + }, + user_id: { + type: DataTypes.INTEGER, + onDelete: 'CASCADE', + allowNull: false, + references: { + model: 'Users', + key: 'id' + } + }, + token: { + type: DataTypes.STRING, + allowNull: false, + }, + device: { + type: DataTypes.STRING, + allowNull: false, + }, + status: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true + }, + }, + { + timestamps: true, + underscored: true, + tableName: 'DeviceTokens' + } + ); + + DeviceToken.associate = (models) => { + DeviceToken.belongsTo(models.User, { + onDelete: 'CASCADE', + foreignKey: 'user_id', + targetKey: 'id' + }); + }; + + return DeviceToken; +}; diff --git a/server/db/models/index.js b/server/db/models/index.js index 16055d8064..30b43d6784 100644 --- a/server/db/models/index.js +++ b/server/db/models/index.js @@ -50,6 +50,8 @@ model = require(path.join(__dirname, './staker'))(sequelize, Sequelize.DataTypes db[model.name] = model; model = require(path.join(__dirname, './transactionLimit'))(sequelize, Sequelize.DataTypes); db[model.name] = model; +model = require(path.join(__dirname, './deviceToken'))(sequelize, Sequelize.DataTypes); +db[model.name] = model; Object.keys(db).forEach(function (modelName) { if ('associate' in db[modelName]) { diff --git a/server/messages.js b/server/messages.js index a6b6821e6d..b697c9e103 100644 --- a/server/messages.js +++ b/server/messages.js @@ -260,4 +260,6 @@ exports.CANNOT_CHANGE_ADMIN_EMAIL = 'Cannot change admin email'; exports.EMAIL_IS_SAME = 'New email cannot be same as the existing one'; exports.EMAIL_EXISTS = 'This email already exists'; exports.CANNOT_CHANGE_DELETED_EMAIL = 'Cannot change deleted email'; -exports.FAILED_GET_QUOTE = 'Failed to get the quote'; \ No newline at end of file +exports.FAILED_GET_QUOTE = 'Failed to get the quote'; +exports.DEVICE_TOKEN_EXITS = 'Active token already exits'; +exports.DEVICE_TOKEN_NOT_FOUND = 'Active token not found'; \ No newline at end of file diff --git a/server/utils/hollaex-tools-lib/tools/user.js b/server/utils/hollaex-tools-lib/tools/user.js index 113d90996f..ab98a2bf05 100644 --- a/server/utils/hollaex-tools-lib/tools/user.js +++ b/server/utils/hollaex-tools-lib/tools/user.js @@ -59,7 +59,9 @@ const { EMAIL_IS_SAME, EMAIL_EXISTS, CANNOT_CHANGE_DELETED_EMAIL, - SERVICE_NOT_SUPPORTED + SERVICE_NOT_SUPPORTED, + DEVICE_TOKEN_EXITS, + DEVICE_TOKEN_NOT_FOUND } = require(`${SERVER_PATH}/messages`); const { publisher, client } = require('./database/redis'); const { @@ -2342,6 +2344,73 @@ const changeKitUserEmail = async (userId, newEmail, auditInfo) => { return updatedUser; }; +const createUserDeviceToken = async (userId, token, device) => { + const user = await dbQuery.findOne('user', { + where: { + id: userId + }, + attributes: [ + 'id', + 'email', + 'is_admin', + ] + }); + + if (!user) { + throw new Error(USER_NOT_FOUND); + } + + const userToken = await dbQuery.findOne('deviceToken', { + where: { + token, + status: true + }, + }); + + if (userToken) { + throw new Error(DEVICE_TOKEN_EXITS); + } + + return getModel('deviceToken').create({ + user_id: userId, + token, + device, + }) + +} + +const getUserDeviceToken = async (userId, device) => { + + const user = await dbQuery.findOne('user', { + where: { + id: userId + }, + attributes: [ + 'id', + 'email', + 'is_admin', + ] + }); + + if (!user) { + throw new Error(USER_NOT_FOUND); + } + + const token = await dbQuery.findOne('deviceToken', { + where: { + user_id: userId, + device, + status: true + }, + }); + + + if (!token) { + throw new Error(DEVICE_TOKEN_NOT_FOUND); + } + return { token }; +} + module.exports = { loginUser, getUserTier, @@ -2402,5 +2471,7 @@ module.exports = { changeKitUserEmail, storeVerificationCode, signUpUser, - verifyUser + verifyUser, + createUserDeviceToken, + getUserDeviceToken }; From 1559649577ae4c4f346fa7bcacf4a5bf1e18354a Mon Sep 17 00:00:00 2001 From: fetok12 Date: Wed, 20 Dec 2023 16:10:56 +0300 Subject: [PATCH 2/2] Update attributes --- server/utils/hollaex-tools-lib/tools/user.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/server/utils/hollaex-tools-lib/tools/user.js b/server/utils/hollaex-tools-lib/tools/user.js index ab98a2bf05..065ef6f8e0 100644 --- a/server/utils/hollaex-tools-lib/tools/user.js +++ b/server/utils/hollaex-tools-lib/tools/user.js @@ -2351,8 +2351,7 @@ const createUserDeviceToken = async (userId, token, device) => { }, attributes: [ 'id', - 'email', - 'is_admin', + 'email' ] }); @@ -2387,8 +2386,7 @@ const getUserDeviceToken = async (userId, device) => { }, attributes: [ 'id', - 'email', - 'is_admin', + 'email' ] });