Add Device Token Support for Customer and User Entities
- Introduced a new DeviceToken field in both Customer and User entities to store device tokens for authentication and notifications. - Updated LoginForm structures for both Customer and User to include an optional DeviceToken field, allowing clients to send device tokens during login. - Enhanced the Login methods in the customer and user services to append the device token to the respective entities if it does not already exist, improving user session management and notification capabilities. - Ensured proper validation and structured responses for the new DeviceToken field in the forms, maintaining compliance with best practices for API design.
This commit is contained in:
+10
-1
@@ -2057,7 +2057,7 @@ const docTemplate = `{
|
|||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Admin Flags"
|
"Admin-Flags"
|
||||||
],
|
],
|
||||||
"summary": "Get flag by country code (Admin)",
|
"summary": "Get flag by country code (Admin)",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -7051,6 +7051,10 @@ const docTemplate = `{
|
|||||||
"customer.LoginForm": {
|
"customer.LoginForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"device_token": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "device_token"
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "App!1234"
|
"example": "App!1234"
|
||||||
@@ -8077,6 +8081,11 @@ const docTemplate = `{
|
|||||||
"user.LoginForm": {
|
"user.LoginForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"device_token": {
|
||||||
|
"description": "Device token",
|
||||||
|
"type": "string",
|
||||||
|
"example": "device_token"
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"description": "User password",
|
"description": "User password",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -2051,7 +2051,7 @@
|
|||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Admin Flags"
|
"Admin-Flags"
|
||||||
],
|
],
|
||||||
"summary": "Get flag by country code (Admin)",
|
"summary": "Get flag by country code (Admin)",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -7045,6 +7045,10 @@
|
|||||||
"customer.LoginForm": {
|
"customer.LoginForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"device_token": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "device_token"
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "App!1234"
|
"example": "App!1234"
|
||||||
@@ -8071,6 +8075,11 @@
|
|||||||
"user.LoginForm": {
|
"user.LoginForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"device_token": {
|
||||||
|
"description": "Device token",
|
||||||
|
"type": "string",
|
||||||
|
"example": "device_token"
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"description": "User password",
|
"description": "User password",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -406,6 +406,9 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
customer.LoginForm:
|
customer.LoginForm:
|
||||||
properties:
|
properties:
|
||||||
|
device_token:
|
||||||
|
example: device_token
|
||||||
|
type: string
|
||||||
password:
|
password:
|
||||||
example: App!1234
|
example: App!1234
|
||||||
type: string
|
type: string
|
||||||
@@ -1110,6 +1113,10 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
user.LoginForm:
|
user.LoginForm:
|
||||||
properties:
|
properties:
|
||||||
|
device_token:
|
||||||
|
description: Device token
|
||||||
|
example: device_token
|
||||||
|
type: string
|
||||||
password:
|
password:
|
||||||
description: User password
|
description: User password
|
||||||
example: Admin!1234
|
example: Admin!1234
|
||||||
@@ -2509,7 +2516,7 @@ paths:
|
|||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
summary: Get flag by country code (Admin)
|
summary: Get flag by country code (Admin)
|
||||||
tags:
|
tags:
|
||||||
- Admin Flags
|
- Admin-Flags
|
||||||
/admin/v1/health:
|
/admin/v1/health:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ type Customer struct {
|
|||||||
Role CustomerRole `bson:"role"`
|
Role CustomerRole `bson:"role"`
|
||||||
Phone *string `bson:"phone"`
|
Phone *string `bson:"phone"`
|
||||||
CompanyIDs []string `bson:"company_ids"`
|
CompanyIDs []string `bson:"company_ids"`
|
||||||
|
DeviceToken []string `bson:"device_token"`
|
||||||
LastLoginAt *int64 `bson:"last_login_at"`
|
LastLoginAt *int64 `bson:"last_login_at"`
|
||||||
UpdatedAt int64 `bson:"updated_at"`
|
UpdatedAt int64 `bson:"updated_at"`
|
||||||
CreatedAt int64 `bson:"created_at"`
|
CreatedAt int64 `bson:"created_at"`
|
||||||
|
|||||||
@@ -89,8 +89,9 @@ type CustomerListResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LoginForm struct {
|
type LoginForm struct {
|
||||||
Username string `json:"username" valid:"required" example:"app"`
|
Username string `json:"username" valid:"required" example:"app"`
|
||||||
Password string `json:"password" valid:"required" example:"App!1234"`
|
Password string `json:"password" valid:"required" example:"App!1234"`
|
||||||
|
DeviceToken string `json:"device_token" valid:"optional" example:"device_token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RefreshTokenForm struct {
|
type RefreshTokenForm struct {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -529,6 +530,11 @@ func (s *customerService) Login(ctx context.Context, form *LoginForm) (*AuthResp
|
|||||||
return nil, errors.New("failed to generate authentication tokens")
|
return nil, errors.New("failed to generate authentication tokens")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// append device token to customer if not exists
|
||||||
|
if !slices.Contains(customer.DeviceToken, form.DeviceToken) {
|
||||||
|
customer.DeviceToken = append(customer.DeviceToken, form.DeviceToken)
|
||||||
|
}
|
||||||
|
|
||||||
err = s.repository.Login(ctx, customer.ID.Hex())
|
err = s.repository.Login(ctx, customer.ID.Hex())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to update customer last login", map[string]interface{}{
|
s.logger.Error("Failed to update customer last login", map[string]interface{}{
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ type User struct {
|
|||||||
Position *string `bson:"position"`
|
Position *string `bson:"position"`
|
||||||
Phone *string `bson:"phone"`
|
Phone *string `bson:"phone"`
|
||||||
ProfileImage *string `bson:"profile_image"`
|
ProfileImage *string `bson:"profile_image"`
|
||||||
|
DeviceToken []string `bson:"device_token"`
|
||||||
IsVerified bool `bson:"is_verified"`
|
IsVerified bool `bson:"is_verified"`
|
||||||
LastLoginAt *int64 `bson:"last_login_at"`
|
LastLoginAt *int64 `bson:"last_login_at"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ type UpdateUserForm struct {
|
|||||||
|
|
||||||
// LoginForm represents the credentials required for user authentication
|
// LoginForm represents the credentials required for user authentication
|
||||||
type LoginForm struct {
|
type LoginForm struct {
|
||||||
Username string `json:"username" valid:"required" example:"admin"` // Username or email address
|
Username string `json:"username" valid:"required" example:"admin"` // Username or email address
|
||||||
Password string `json:"password" valid:"required" example:"Admin!1234"` // User password
|
Password string `json:"password" valid:"required" example:"Admin!1234"` // User password
|
||||||
|
DeviceToken string `json:"device_token" valid:"optional" example:"device_token"` // Device token
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshTokenForm represents the refresh token required to generate new access tokens
|
// RefreshTokenForm represents the refresh token required to generate new access tokens
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"tm/pkg/authorization"
|
"tm/pkg/authorization"
|
||||||
@@ -311,6 +312,11 @@ func (s *userService) Login(ctx context.Context, form *LoginForm) (*AuthResponse
|
|||||||
return nil, errors.New("invalid credentials")
|
return nil, errors.New("invalid credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// append device token to user if not exists
|
||||||
|
if !slices.Contains(user.DeviceToken, form.DeviceToken) {
|
||||||
|
user.DeviceToken = append(user.DeviceToken, form.DeviceToken)
|
||||||
|
}
|
||||||
|
|
||||||
// Update last login
|
// Update last login
|
||||||
err = s.repository.Login(ctx, user.ID.Hex())
|
err = s.repository.Login(ctx, user.ID.Hex())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user