afro SMS and partial ArifPay Payment Gateway integrations

This commit is contained in:
Yared Yemane 2025-12-18 18:06:26 +03:00
parent 2bc43f5076
commit 47d70b029f
74 changed files with 3507 additions and 13783 deletions

View File

@ -11,10 +11,14 @@ import (
"Yimaru-Backend/internal/repository"
"Yimaru-Backend/internal/services/arifpay"
"Yimaru-Backend/internal/services/authentication"
"Yimaru-Backend/internal/services/currency"
issuereporting "Yimaru-Backend/internal/services/issue_reporting"
"Yimaru-Backend/internal/services/messenger"
notificationservice "Yimaru-Backend/internal/services/notification"
"Yimaru-Backend/internal/services/recommendation"
referralservice "Yimaru-Backend/internal/services/referal"
"Yimaru-Backend/internal/services/settings"
"context"
// referralservice "Yimaru-Backend/internal/services/referal"
"Yimaru-Backend/internal/services/transaction"
"Yimaru-Backend/internal/services/user"
httpserver "Yimaru-Backend/internal/web_server"
@ -71,14 +75,14 @@ func main() {
v := customvalidator.NewCustomValidator(validator.New())
// Initialize services
// settingRepo := repository.NewSettingStore(store)
settingRepo := repository.NewSettingStore(store)
// if err := settingRepo.EnsureAllSettingsExist(context.Background()); err != nil {
// log.Fatalf("failed to ensure settings: %v", err)
// }
// settingSvc := settings.NewService(settingRepo)
if err := settingRepo.EnsureAllSettingsExist(context.Background()); err != nil {
log.Fatalf("failed to ensure settings: %v", err)
}
settingSvc := settings.NewService(settingRepo)
// messengerSvc := messenger.NewService(settingSvc, cfg)
messengerSvc := messenger.NewService(settingSvc, cfg)
// statSvc := stats.NewService(
// repository.NewCompanyStatStore(store),
// repository.NewBranchStatStore(store),
@ -92,7 +96,7 @@ func main() {
userSvc := user.NewService(
repository.NewUserStore(store),
repository.NewOTPStore(store),
// messengerSvc,
messengerSvc,
cfg,
)
// leagueSvc := league.New(repository.NewLeagueStore(store))
@ -146,8 +150,8 @@ func main() {
// logger,
// )
branchSvc := branch.NewService(repository.NewBranchStore(store))
companySvc := company.NewService(repository.NewCompanyStore(store))
// branchSvc := branch.NewService(repository.NewBranchStore(store))
// companySvc := company.NewService(repository.NewCompanyStore(store))
// ticketSvc := ticke.NewService(
// repository.NewTicketStore(store),
@ -184,25 +188,25 @@ func main() {
// *userSvc,
// )
bonusSvc := bonus.NewService(
repository.NewBonusStore(store),
settingSvc,
notificationSvc,
domain.MongoDBLogger,
)
// bonusSvc := bonus.NewService(
// repository.NewBonusStore(store),
// settingSvc,
// notificationSvc,
// domain.MongoDBLogger,
// )
// virtualGamesRepo := repository.NewVirtualGameRepository(store)
recommendationRepo := repository.NewRecommendationRepository(store)
referalSvc := referralservice.New(
repository.NewReferralStore(store),
*settingSvc,
cfg,
logger,
domain.MongoDBLogger,
)
raffleSvc := raffle.NewService(
repository.NewRaffleStore(store),
)
// referalSvc := referralservice.New(
// repository.NewReferralStore(store),
// *settingSvc,
// cfg,
// logger,
// domain.MongoDBLogger,
// )
// raffleSvc := raffle.NewService(
// repository.NewRaffleStore(store),
// )
// virtualGameSvc := virtualgameservice.New(virtualGamesRepo,, store, cfg, logger)
// aleaService := alea.NewAleaPlayService(virtualGamesRepo,, cfg, logger)
// veliCLient := veli.NewClient(cfg)
@ -224,15 +228,14 @@ func main() {
// chapaClient,
// )
currRepo := repository.NewCurrencyPostgresRepository(store)
// currRepo := repository.NewCurrencyPostgresRepository(store)
fixerFertcherSvc := currency.NewFixerFetcher(
cfg.FIXER_API_KEY,
cfg.FIXER_BASE_URL,
)
// fixerFertcherSvc := currency.NewFixerFetcher(
// cfg.FIXER_API_KEY,
// cfg.FIXER_BASE_URL,
// )
transactionSvc := transaction.NewService(
repository.NewTransactionStore(store),
*branchSvc,
*userSvc,
)
@ -263,8 +266,8 @@ func main() {
// go httpserver.SetupReportandVirtualGameCronJobs(context.Background(), reportSvc, orchestrationSvc, "C:/Users/User/Desktop")
// go httpserver.ProcessBetCashback(context.TODO(), betSvc)
bankRepository := repository.NewBankRepository(store)
instSvc := institutions.New(bankRepository)
// bankRepository := repository.NewBankRepository(store)
// instSvc := institutions.New(bankRepository)
// Initialize report worker with CSV exporter
// csvExporter := infrastructure.CSVExporter{
// ExportPath: cfg.ReportExportPath, // Make sure to add this to your config
@ -297,11 +300,11 @@ func main() {
// 5*time.Minute,
// )
currSvc := currency.NewService(
currRepo,
cfg.BASE_CURRENCY,
fixerFertcherSvc,
)
// currSvc := currency.NewService(
// currRepo,
// cfg.BASE_CURRENCY,
// fixerFertcherSvc,
// )
// exchangeWorker := currency.NewExchangeRateWorker(fixerFertcherSvc, logger, cfg)
// exchangeWorker.Start(context.Background())
@ -330,13 +333,8 @@ func main() {
// Initialize and start HTTP server
app := httpserver.NewApp(
// directdeposit,
// telebirrSvc,
arifpaySvc,
// santimpaySvc,
issueReportingSvc,
instSvc,
currSvc,
cfg.Port,
v,
settingSvc,
@ -347,16 +345,9 @@ func main() {
JwtAccessExpiry: cfg.AccessExpiry,
},
userSvc,
// chapaSvc,
transactionSvc,
branchSvc,
companySvc,
notificationSvc,
referalSvc,
raffleSvc,
bonusSvc,
recommendationSvc,
statSvc,
cfg,
domain.MongoDBLogger,
)

View File

@ -70,7 +70,7 @@ VALUES
'Sarah',
'Connor',
'SarahC',
'sarah.connor@yimaru.com',
'yaredyemane1@gmail.com',
NULL,
crypt('password@123', gen_salt('bf'))::bytea,
'SUPER_ADMIN',

View File

@ -1,3 +1,8 @@
-- =========================================
-- Notifications
-- =========================================
DROP TABLE IF EXISTS global_settings;
-- =========================================
-- Notifications
-- =========================================

View File

@ -1,17 +0,0 @@
DROP TABLE IF EXISTS referrals;
DROP TABLE IF EXISTS referral_settings;
DROP TYPE IF EXISTS ReferralStatus;
ALTER TABLE users
DROP COLUMN referral_code;
ALTER TABLE users
DROP COLUMN referred_by;
ALTER TABLE wallet
DROP COLUMN bonus_balance;
ALTER TABLE wallet
DROP COLUMN cash_balance;

View File

@ -1,45 +0,0 @@
CREATE TABLE IF NOT EXISTS referral_codes (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(12) NOT NULL UNIQUE,
referrer_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
max_uses INT,
current_uses INT NOT NULL DEFAULT 0,
incentive_type TEXT NOT NULL CHECK (
incentive_type IN (
'course_access',
'discount',
'certificate_unlock',
'feature_unlock'
)
),
incentive_value TEXT, -- e.g. course_id, percentage, feature_key
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_referral_codes_referrer_id
ON referral_codes (referrer_id);
CREATE INDEX idx_referral_codes_code
ON referral_codes (code);
CREATE TABLE IF NOT EXISTS user_referrals (
id BIGSERIAL PRIMARY KEY,
referrer_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
referred_user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
referral_code_id BIGINT NOT NULL REFERENCES referral_codes(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE (referred_user_id),
UNIQUE (referrer_id, referred_user_id)
);

View File

@ -1,2 +0,0 @@
DROP TABLE IF EXISTS reported_issues;

View File

@ -1,14 +0,0 @@
CREATE TABLE IF NOT EXISTS reported_issues (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
user_role VARCHAR(255) NOT NULL,
subject TEXT NOT NULL,
description TEXT NOT NULL,
issue_type TEXT NOT NULL,
-- e.g., "deposit", "withdrawal", "bet", "technical"
status TEXT NOT NULL DEFAULT 'pending',
-- pending, in_progress, resolved, rejected
metadata JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@ -1,14 +1,3 @@
-- name: GetUserByEmailPhone :one
SELECT *
FROM users
WHERE (
email = $1
OR phone_number = $2
)
AND (
organization_id = sqlc.narg('organization_id')
OR sqlc.narg('organization_id') IS NULL
);
-- name: CreateRefreshToken :exec
INSERT INTO refresh_tokens (user_id, token, expires_at, created_at, revoked)
VALUES ($1, $2, $3, $4, $5);

View File

@ -1,37 +1,37 @@
-- -- name: CreateReportedIssue :one
-- INSERT INTO reported_issues (
-- user_id,
-- user_role,
-- subject,
-- description,
-- issue_type,
-- metadata
-- )
-- VALUES ($1, $2, $3, $4, $5, $6)
-- RETURNING *;
-- -- name: ListReportedIssues :many
-- SELECT *
-- FROM reported_issues
-- ORDER BY created_at DESC
-- LIMIT $1 OFFSET $2;
-- -- name: ListReportedIssuesByUser :many
-- SELECT *
-- FROM reported_issues
-- WHERE user_id = $1
-- ORDER BY created_at DESC
-- LIMIT $2 OFFSET $3;
-- -- name: CountReportedIssues :one
-- SELECT COUNT(*)
-- FROM reported_issues;
-- -- name: CountReportedIssuesByUser :one
-- SELECT COUNT(*)
-- FROM reported_issues
-- WHERE user_id = $1;
-- -- name: UpdateReportedIssueStatus :exec
-- UPDATE reported_issues
-- SET status = $2,
-- updated_at = NOW()
-- WHERE id = $1;
-- -- name: DeleteReportedIssue :exec
-- DELETE FROM reported_issues
-- WHERE id = $1;
-- name: CreateReportedIssue :one
INSERT INTO reported_issues (
user_id,
user_role,
subject,
description,
issue_type,
metadata
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *;
-- name: ListReportedIssues :many
SELECT *
FROM reported_issues
ORDER BY created_at DESC
LIMIT $1 OFFSET $2;
-- name: ListReportedIssuesByUser :many
SELECT *
FROM reported_issues
WHERE user_id = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3;
-- name: CountReportedIssues :one
SELECT COUNT(*)
FROM reported_issues;
-- name: CountReportedIssuesByUser :one
SELECT COUNT(*)
FROM reported_issues
WHERE user_id = $1;
-- name: UpdateReportedIssueStatus :exec
UPDATE reported_issues
SET status = $2,
updated_at = NOW()
WHERE id = $1;
-- name: DeleteReportedIssue :exec
DELETE FROM reported_issues
WHERE id = $1;

View File

@ -1,99 +1,72 @@
-- -- name: CreateNotification :one
-- INSERT INTO notifications (
-- id,
-- recipient_id,
-- type,
-- level,
-- error_severity,
-- reciever,
-- is_read,
-- delivery_status,
-- delivery_channel,
-- payload,
-- priority,
-- timestamp,
-- expires,
-- img,
-- metadata
-- )
-- VALUES (
-- $1,
-- $2,
-- $3,
-- $4,
-- $5,
-- $6,
-- $7,
-- $8,
-- $9,
-- $10,
-- $11,
-- $12,
-- $13,
-- $14,
-- $15
-- )
-- RETURNING *;
-- -- name: GetNotification :one
-- SELECT *
-- FROM notifications
-- WHERE id = $1
-- LIMIT 1;
-- -- name: GetAllNotifications :many
-- SELECT *
-- FROM notifications
-- ORDER BY timestamp DESC
-- LIMIT $1 OFFSET $2;
-- -- name: GetTotalNotificationCount :one
-- SELECT COUNT(*)
-- FROM notifications;
-- -- name: GetUserNotifications :many
-- SELECT *
-- FROM notifications
-- WHERE recipient_id = $1
-- ORDER BY timestamp DESC
-- LIMIT $2 OFFSET $3;
-- -- name: GetUserNotificationCount :one
-- SELECT COUNT(*)
-- FROM notifications
-- WHERE recipient_id = $1;
-- -- name: CountUnreadNotifications :one
-- SELECT count(id)
-- FROM notifications
-- WHERE recipient_id = $1
-- AND is_read = false;
-- -- name: UpdateNotificationStatus :one
-- UPDATE notifications
-- SET delivery_status = $2,
-- is_read = $3,
-- metadata = $4
-- WHERE id = $1
-- RETURNING *;
-- -- name: ListFailedNotifications :many
-- SELECT *
-- FROM notifications
-- WHERE delivery_status = 'failed'
-- AND timestamp < NOW() - INTERVAL '1 hour'
-- ORDER BY timestamp ASC
-- LIMIT $1;
-- -- name: ListRecipientIDsByReceiver :many
-- SELECT recipient_id
-- FROM notifications
-- WHERE reciever = $1;
-- -- name: GetNotificationCounts :many
-- SELECT COUNT(*) as total,
-- COUNT(
-- CASE
-- WHEN is_read = true THEN 1
-- END
-- ) as read,
-- COUNT(
-- CASE
-- WHEN is_read = false THEN 1
-- END
-- ) as unread
-- FROM notifications;
-- name: CreateNotification :one
INSERT INTO notifications (
user_id,
type,
level,
channel,
title,
message,
payload
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7
)
RETURNING *;
-- -- name: DeleteOldNotifications :exec
-- DELETE FROM notifications
-- WHERE expires < now();
-- name: GetNotification :one
SELECT *
FROM notifications
WHERE id = $1
LIMIT 1;
-- name: GetAllNotifications :many
SELECT *
FROM notifications
ORDER BY created_at DESC
LIMIT $1 OFFSET $2;
-- name: GetTotalNotificationCount :one
SELECT COUNT(*)
FROM notifications;
-- name: GetUserNotifications :many
SELECT *
FROM notifications
WHERE user_id = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3;
-- name: GetUserNotificationCount :one
SELECT COUNT(*)
FROM notifications
WHERE user_id = $1;
-- name: CountUnreadNotifications :one
SELECT COUNT(*)
FROM notifications
WHERE user_id = $1
AND is_read = FALSE;
-- name: MarkNotificationAsRead :one
UPDATE notifications
SET is_read = TRUE,
read_at = NOW()
WHERE id = $1
RETURNING *;
-- name: MarkAllUserNotificationsAsRead :exec
UPDATE notifications
SET is_read = TRUE,
read_at = NOW()
WHERE user_id = $1
AND is_read = FALSE;
-- name: DeleteUserNotifications :exec
DELETE FROM notifications
WHERE user_id = $1;

View File

@ -1,48 +1,17 @@
-- -- name: InsertGlobalSetting :exec
-- INSERT INTO global_settings (key, value)
-- VALUES ($1, $2) ON CONFLICT (key) DO
-- UPDATE
-- SET value = EXCLUDED.value;
-- -- name: GetGlobalSettings :many
-- SELECT *
-- FROM global_settings;
-- -- name: GetGlobalSetting :one
-- SELECT *
-- FROM global_settings
-- WHERE key = $1;
-- -- name: UpdateGlobalSetting :exec
-- UPDATE global_settings
-- SET value = $2,
-- updated_at = CURRENT_TIMESTAMP
-- WHERE key = $1;
-- -- name: InsertCompanySetting :exec
-- INSERT INTO company_settings (company_id, key, value)
-- VALUES ($1, $2, $3) ON CONFLICT (company_id, key) DO
-- UPDATE
-- SET value = EXCLUDED.value;
-- -- name: GetAllCompanySettings :many
-- SELECT *
-- FROM company_settings;
-- -- name: GetCompanySetting :many
-- SELECT *
-- FROM company_settings
-- WHERE company_id = $1;
-- -- name: GetCompanySettingsByKey :many
-- SELECT *
-- FROM company_settings
-- WHERE key = $1;
-- -- name: GetOverrideSettings :many
-- SELECT gs.key,
-- gs.created_at,
-- gs.updated_at,
-- COALESCE(cs.value, gs.value) AS value
-- FROM global_settings gs
-- LEFT JOIN company_settings cs ON cs.key = gs.key
-- AND cs.company_id = $1;
-- -- name: DeleteCompanySetting :exec
-- DELETE FROM company_settings
-- WHERE company_id = $1
-- AND key = $2;
-- -- name: DeleteAllCompanySetting :exec
-- DELETE FROM company_settings
-- WHERE company_id = $1;
-- name: InsertGlobalSetting :exec
INSERT INTO global_settings (key, value)
VALUES ($1, $2) ON CONFLICT (key) DO
UPDATE
SET value = EXCLUDED.value;
-- name: GetGlobalSettings :many
SELECT *
FROM global_settings;
-- name: GetGlobalSetting :one
SELECT *
FROM global_settings
WHERE key = $1;
-- name: UpdateGlobalSetting :exec
UPDATE global_settings
SET value = $2,
updated_at = CURRENT_TIMESTAMP
WHERE key = $1;

View File

@ -62,7 +62,9 @@ SELECT *
FROM users
WHERE id = $1;
-- name: GetAllUsers :many
SELECT id,
SELECT
COUNT(*) OVER () AS total_count,
id,
first_name,
last_name,
nick_name,
@ -81,7 +83,7 @@ SELECT id,
suspended_at,
organization_id
FROM users
wHERE (
WHERE (
role = $1
OR $1 IS NULL
)
@ -103,7 +105,8 @@ wHERE (
created_at < sqlc.narg('created_after')
OR sqlc.narg('created_after') IS NULL
)
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
LIMIT sqlc.narg('limit')
OFFSET sqlc.narg('offset');
-- name: GetTotalUsers :one
SELECT COUNT(*)
FROM users
@ -159,12 +162,6 @@ WHERE id = $4;
UPDATE users
SET organization_id = $1
WHERE id = $2;
-- name: SuspendUser :exec
UPDATE users
SET suspended = $1,
suspended_at = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $3;
-- name: DeleteUser :exec
DELETE FROM users
WHERE id = $1;
@ -183,14 +180,16 @@ SELECT EXISTS (
AND users.email IS NOT NULL
AND users.organization_id = $2
) AS email_exists;
-- name: GetUserByEmail :one
SELECT id,
-- name: GetUserByEmailPhone :one
SELECT
id,
first_name,
last_name,
nick_name,
email,
phone_number,
role,
password, -- added this line
age,
education_level,
country,
@ -203,30 +202,12 @@ SELECT id,
suspended_at,
organization_id
FROM users
WHERE email = $1
AND organization_id = $2;
-- name: GetUserByPhone :one
SELECT id,
first_name,
last_name,
nick_name,
email,
phone_number,
role,
age,
education_level,
country,
region,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
suspended_at,
organization_id
FROM users
WHERE phone_number = $1
AND organization_id = $2;
WHERE organization_id = $3
AND (
(email = $1 AND $1 IS NOT NULL)
OR (phone_number = $2 AND $2 IS NOT NULL)
)
LIMIT 1;
-- name: UpdatePassword :exec
UPDATE users
SET password = $1,
@ -240,3 +221,9 @@ SELECT users.*
FROM organizations
JOIN users ON organizations.owner_id = users.id
WHERE organizations.id = $1;
-- name: SuspendUser :exec
UPDATE users
SET suspended = $1,
suspended_at = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $3;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -75,52 +75,6 @@ func (q *Queries) GetRefreshTokenByUserID(ctx context.Context, userID int64) (Re
return i, err
}
const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one
SELECT id, first_name, last_name, nick_name, email, phone_number, role, password, age, education_level, country, region, email_verified, phone_verified, suspended, suspended_at, organization_id, created_at, updated_at
FROM users
WHERE (
email = $1
OR phone_number = $2
)
AND (
organization_id = $3
OR $3 IS NULL
)
`
type GetUserByEmailPhoneParams struct {
Email pgtype.Text `json:"email"`
PhoneNumber pgtype.Text `json:"phone_number"`
OrganizationID pgtype.Int8 `json:"organization_id"`
}
func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPhoneParams) (User, error) {
row := q.db.QueryRow(ctx, GetUserByEmailPhone, arg.Email, arg.PhoneNumber, arg.OrganizationID)
var i User
err := row.Scan(
&i.ID,
&i.FirstName,
&i.LastName,
&i.NickName,
&i.Email,
&i.PhoneNumber,
&i.Role,
&i.Password,
&i.Age,
&i.EducationLevel,
&i.Country,
&i.Region,
&i.EmailVerified,
&i.PhoneVerified,
&i.Suspended,
&i.SuspendedAt,
&i.OrganizationID,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const RevokeRefreshToken = `-- name: RevokeRefreshToken :exec
UPDATE refresh_tokens
SET revoked = TRUE

View File

@ -0,0 +1,197 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: issue_reporting.sql
package dbgen
import (
"context"
)
const CountReportedIssues = `-- name: CountReportedIssues :one
SELECT COUNT(*)
FROM reported_issues
`
func (q *Queries) CountReportedIssues(ctx context.Context) (int64, error) {
row := q.db.QueryRow(ctx, CountReportedIssues)
var count int64
err := row.Scan(&count)
return count, err
}
const CountReportedIssuesByUser = `-- name: CountReportedIssuesByUser :one
SELECT COUNT(*)
FROM reported_issues
WHERE user_id = $1
`
func (q *Queries) CountReportedIssuesByUser(ctx context.Context, userID int64) (int64, error) {
row := q.db.QueryRow(ctx, CountReportedIssuesByUser, userID)
var count int64
err := row.Scan(&count)
return count, err
}
const CreateReportedIssue = `-- name: CreateReportedIssue :one
INSERT INTO reported_issues (
user_id,
user_role,
subject,
description,
issue_type,
metadata
)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at
`
type CreateReportedIssueParams struct {
UserID int64 `json:"user_id"`
UserRole string `json:"user_role"`
Subject string `json:"subject"`
Description string `json:"description"`
IssueType string `json:"issue_type"`
Metadata []byte `json:"metadata"`
}
func (q *Queries) CreateReportedIssue(ctx context.Context, arg CreateReportedIssueParams) (ReportedIssue, error) {
row := q.db.QueryRow(ctx, CreateReportedIssue,
arg.UserID,
arg.UserRole,
arg.Subject,
arg.Description,
arg.IssueType,
arg.Metadata,
)
var i ReportedIssue
err := row.Scan(
&i.ID,
&i.UserID,
&i.UserRole,
&i.Subject,
&i.Description,
&i.IssueType,
&i.Status,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const DeleteReportedIssue = `-- name: DeleteReportedIssue :exec
DELETE FROM reported_issues
WHERE id = $1
`
func (q *Queries) DeleteReportedIssue(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteReportedIssue, id)
return err
}
const ListReportedIssues = `-- name: ListReportedIssues :many
SELECT id, user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at
FROM reported_issues
ORDER BY created_at DESC
LIMIT $1 OFFSET $2
`
type ListReportedIssuesParams struct {
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
func (q *Queries) ListReportedIssues(ctx context.Context, arg ListReportedIssuesParams) ([]ReportedIssue, error) {
rows, err := q.db.Query(ctx, ListReportedIssues, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ReportedIssue
for rows.Next() {
var i ReportedIssue
if err := rows.Scan(
&i.ID,
&i.UserID,
&i.UserRole,
&i.Subject,
&i.Description,
&i.IssueType,
&i.Status,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const ListReportedIssuesByUser = `-- name: ListReportedIssuesByUser :many
SELECT id, user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at
FROM reported_issues
WHERE user_id = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3
`
type ListReportedIssuesByUserParams struct {
UserID int64 `json:"user_id"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
func (q *Queries) ListReportedIssuesByUser(ctx context.Context, arg ListReportedIssuesByUserParams) ([]ReportedIssue, error) {
rows, err := q.db.Query(ctx, ListReportedIssuesByUser, arg.UserID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ReportedIssue
for rows.Next() {
var i ReportedIssue
if err := rows.Scan(
&i.ID,
&i.UserID,
&i.UserRole,
&i.Subject,
&i.Description,
&i.IssueType,
&i.Status,
&i.Metadata,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateReportedIssueStatus = `-- name: UpdateReportedIssueStatus :exec
UPDATE reported_issues
SET status = $2,
updated_at = NOW()
WHERE id = $1
`
type UpdateReportedIssueStatusParams struct {
ID int64 `json:"id"`
Status string `json:"status"`
}
func (q *Queries) UpdateReportedIssueStatus(ctx context.Context, arg UpdateReportedIssueStatusParams) error {
_, err := q.db.Exec(ctx, UpdateReportedIssueStatus, arg.ID, arg.Status)
return err
}

276
gen/db/notification.sql.go Normal file
View File

@ -0,0 +1,276 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: notification.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CountUnreadNotifications = `-- name: CountUnreadNotifications :one
SELECT COUNT(*)
FROM notifications
WHERE user_id = $1
AND is_read = FALSE
`
func (q *Queries) CountUnreadNotifications(ctx context.Context, userID int64) (int64, error) {
row := q.db.QueryRow(ctx, CountUnreadNotifications, userID)
var count int64
err := row.Scan(&count)
return count, err
}
const CreateNotification = `-- name: CreateNotification :one
INSERT INTO notifications (
user_id,
type,
level,
channel,
title,
message,
payload
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7
)
RETURNING id, user_id, type, level, channel, title, message, payload, is_read, created_at, read_at
`
type CreateNotificationParams struct {
UserID int64 `json:"user_id"`
Type string `json:"type"`
Level string `json:"level"`
Channel pgtype.Text `json:"channel"`
Title string `json:"title"`
Message string `json:"message"`
Payload []byte `json:"payload"`
}
func (q *Queries) CreateNotification(ctx context.Context, arg CreateNotificationParams) (Notification, error) {
row := q.db.QueryRow(ctx, CreateNotification,
arg.UserID,
arg.Type,
arg.Level,
arg.Channel,
arg.Title,
arg.Message,
arg.Payload,
)
var i Notification
err := row.Scan(
&i.ID,
&i.UserID,
&i.Type,
&i.Level,
&i.Channel,
&i.Title,
&i.Message,
&i.Payload,
&i.IsRead,
&i.CreatedAt,
&i.ReadAt,
)
return i, err
}
const DeleteUserNotifications = `-- name: DeleteUserNotifications :exec
DELETE FROM notifications
WHERE user_id = $1
`
func (q *Queries) DeleteUserNotifications(ctx context.Context, userID int64) error {
_, err := q.db.Exec(ctx, DeleteUserNotifications, userID)
return err
}
const GetAllNotifications = `-- name: GetAllNotifications :many
SELECT id, user_id, type, level, channel, title, message, payload, is_read, created_at, read_at
FROM notifications
ORDER BY created_at DESC
LIMIT $1 OFFSET $2
`
type GetAllNotificationsParams struct {
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
func (q *Queries) GetAllNotifications(ctx context.Context, arg GetAllNotificationsParams) ([]Notification, error) {
rows, err := q.db.Query(ctx, GetAllNotifications, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Notification
for rows.Next() {
var i Notification
if err := rows.Scan(
&i.ID,
&i.UserID,
&i.Type,
&i.Level,
&i.Channel,
&i.Title,
&i.Message,
&i.Payload,
&i.IsRead,
&i.CreatedAt,
&i.ReadAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetNotification = `-- name: GetNotification :one
SELECT id, user_id, type, level, channel, title, message, payload, is_read, created_at, read_at
FROM notifications
WHERE id = $1
LIMIT 1
`
func (q *Queries) GetNotification(ctx context.Context, id int64) (Notification, error) {
row := q.db.QueryRow(ctx, GetNotification, id)
var i Notification
err := row.Scan(
&i.ID,
&i.UserID,
&i.Type,
&i.Level,
&i.Channel,
&i.Title,
&i.Message,
&i.Payload,
&i.IsRead,
&i.CreatedAt,
&i.ReadAt,
)
return i, err
}
const GetTotalNotificationCount = `-- name: GetTotalNotificationCount :one
SELECT COUNT(*)
FROM notifications
`
func (q *Queries) GetTotalNotificationCount(ctx context.Context) (int64, error) {
row := q.db.QueryRow(ctx, GetTotalNotificationCount)
var count int64
err := row.Scan(&count)
return count, err
}
const GetUserNotificationCount = `-- name: GetUserNotificationCount :one
SELECT COUNT(*)
FROM notifications
WHERE user_id = $1
`
func (q *Queries) GetUserNotificationCount(ctx context.Context, userID int64) (int64, error) {
row := q.db.QueryRow(ctx, GetUserNotificationCount, userID)
var count int64
err := row.Scan(&count)
return count, err
}
const GetUserNotifications = `-- name: GetUserNotifications :many
SELECT id, user_id, type, level, channel, title, message, payload, is_read, created_at, read_at
FROM notifications
WHERE user_id = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3
`
type GetUserNotificationsParams struct {
UserID int64 `json:"user_id"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
func (q *Queries) GetUserNotifications(ctx context.Context, arg GetUserNotificationsParams) ([]Notification, error) {
rows, err := q.db.Query(ctx, GetUserNotifications, arg.UserID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Notification
for rows.Next() {
var i Notification
if err := rows.Scan(
&i.ID,
&i.UserID,
&i.Type,
&i.Level,
&i.Channel,
&i.Title,
&i.Message,
&i.Payload,
&i.IsRead,
&i.CreatedAt,
&i.ReadAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const MarkAllUserNotificationsAsRead = `-- name: MarkAllUserNotificationsAsRead :exec
UPDATE notifications
SET is_read = TRUE,
read_at = NOW()
WHERE user_id = $1
AND is_read = FALSE
`
func (q *Queries) MarkAllUserNotificationsAsRead(ctx context.Context, userID int64) error {
_, err := q.db.Exec(ctx, MarkAllUserNotificationsAsRead, userID)
return err
}
const MarkNotificationAsRead = `-- name: MarkNotificationAsRead :one
UPDATE notifications
SET is_read = TRUE,
read_at = NOW()
WHERE id = $1
RETURNING id, user_id, type, level, channel, title, message, payload, is_read, created_at, read_at
`
func (q *Queries) MarkNotificationAsRead(ctx context.Context, id int64) (Notification, error) {
row := q.db.QueryRow(ctx, MarkNotificationAsRead, id)
var i Notification
err := row.Scan(
&i.ID,
&i.UserID,
&i.Type,
&i.Level,
&i.Channel,
&i.Title,
&i.Message,
&i.Payload,
&i.IsRead,
&i.CreatedAt,
&i.ReadAt,
)
return i, err
}

92
gen/db/settings.sql.go Normal file
View File

@ -0,0 +1,92 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: settings.sql
package dbgen
import (
"context"
)
const GetGlobalSetting = `-- name: GetGlobalSetting :one
SELECT key, value, created_at, updated_at
FROM global_settings
WHERE key = $1
`
func (q *Queries) GetGlobalSetting(ctx context.Context, key string) (GlobalSetting, error) {
row := q.db.QueryRow(ctx, GetGlobalSetting, key)
var i GlobalSetting
err := row.Scan(
&i.Key,
&i.Value,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const GetGlobalSettings = `-- name: GetGlobalSettings :many
SELECT key, value, created_at, updated_at
FROM global_settings
`
func (q *Queries) GetGlobalSettings(ctx context.Context) ([]GlobalSetting, error) {
rows, err := q.db.Query(ctx, GetGlobalSettings)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GlobalSetting
for rows.Next() {
var i GlobalSetting
if err := rows.Scan(
&i.Key,
&i.Value,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const InsertGlobalSetting = `-- name: InsertGlobalSetting :exec
INSERT INTO global_settings (key, value)
VALUES ($1, $2) ON CONFLICT (key) DO
UPDATE
SET value = EXCLUDED.value
`
type InsertGlobalSettingParams struct {
Key string `json:"key"`
Value string `json:"value"`
}
func (q *Queries) InsertGlobalSetting(ctx context.Context, arg InsertGlobalSettingParams) error {
_, err := q.db.Exec(ctx, InsertGlobalSetting, arg.Key, arg.Value)
return err
}
const UpdateGlobalSetting = `-- name: UpdateGlobalSetting :exec
UPDATE global_settings
SET value = $2,
updated_at = CURRENT_TIMESTAMP
WHERE key = $1
`
type UpdateGlobalSettingParams struct {
Key string `json:"key"`
Value string `json:"value"`
}
func (q *Queries) UpdateGlobalSetting(ctx context.Context, arg UpdateGlobalSettingParams) error {
_, err := q.db.Exec(ctx, UpdateGlobalSetting, arg.Key, arg.Value)
return err
}

View File

@ -205,7 +205,9 @@ func (q *Queries) DeleteUser(ctx context.Context, id int64) error {
}
const GetAllUsers = `-- name: GetAllUsers :many
SELECT id,
SELECT
COUNT(*) OVER () AS total_count,
id,
first_name,
last_name,
nick_name,
@ -224,7 +226,7 @@ SELECT id,
suspended_at,
organization_id
FROM users
wHERE (
WHERE (
role = $1
OR $1 IS NULL
)
@ -246,7 +248,8 @@ wHERE (
created_at < $5
OR $5 IS NULL
)
LIMIT $7 OFFSET $6
LIMIT $7
OFFSET $6
`
type GetAllUsersParams struct {
@ -260,6 +263,7 @@ type GetAllUsersParams struct {
}
type GetAllUsersRow struct {
TotalCount int64 `json:"total_count"`
ID int64 `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
@ -298,6 +302,7 @@ func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]Get
for rows.Next() {
var i GetAllUsersRow
if err := rows.Scan(
&i.TotalCount,
&i.ID,
&i.FirstName,
&i.LastName,
@ -386,14 +391,16 @@ func (q *Queries) GetTotalUsers(ctx context.Context, arg GetTotalUsersParams) (i
return count, err
}
const GetUserByEmail = `-- name: GetUserByEmail :one
SELECT id,
const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one
SELECT
id,
first_name,
last_name,
nick_name,
email,
phone_number,
role,
password, -- added this line
age,
education_level,
country,
@ -406,16 +413,21 @@ SELECT id,
suspended_at,
organization_id
FROM users
WHERE email = $1
AND organization_id = $2
WHERE organization_id = $3
AND (
(email = $1 AND $1 IS NOT NULL)
OR (phone_number = $2 AND $2 IS NOT NULL)
)
LIMIT 1
`
type GetUserByEmailParams struct {
type GetUserByEmailPhoneParams struct {
Email pgtype.Text `json:"email"`
PhoneNumber pgtype.Text `json:"phone_number"`
OrganizationID pgtype.Int8 `json:"organization_id"`
}
type GetUserByEmailRow struct {
type GetUserByEmailPhoneRow struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
@ -423,6 +435,7 @@ type GetUserByEmailRow struct {
Email pgtype.Text `json:"email"`
PhoneNumber pgtype.Text `json:"phone_number"`
Role string `json:"role"`
Password []byte `json:"password"`
Age pgtype.Int4 `json:"age"`
EducationLevel pgtype.Text `json:"education_level"`
Country pgtype.Text `json:"country"`
@ -436,9 +449,9 @@ type GetUserByEmailRow struct {
OrganizationID pgtype.Int8 `json:"organization_id"`
}
func (q *Queries) GetUserByEmail(ctx context.Context, arg GetUserByEmailParams) (GetUserByEmailRow, error) {
row := q.db.QueryRow(ctx, GetUserByEmail, arg.Email, arg.OrganizationID)
var i GetUserByEmailRow
func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPhoneParams) (GetUserByEmailPhoneRow, error) {
row := q.db.QueryRow(ctx, GetUserByEmailPhone, arg.Email, arg.PhoneNumber, arg.OrganizationID)
var i GetUserByEmailPhoneRow
err := row.Scan(
&i.ID,
&i.FirstName,
@ -447,6 +460,7 @@ func (q *Queries) GetUserByEmail(ctx context.Context, arg GetUserByEmailParams)
&i.Email,
&i.PhoneNumber,
&i.Role,
&i.Password,
&i.Age,
&i.EducationLevel,
&i.Country,
@ -495,82 +509,6 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
return i, err
}
const GetUserByPhone = `-- name: GetUserByPhone :one
SELECT id,
first_name,
last_name,
nick_name,
email,
phone_number,
role,
age,
education_level,
country,
region,
email_verified,
phone_verified,
created_at,
updated_at,
suspended,
suspended_at,
organization_id
FROM users
WHERE phone_number = $1
AND organization_id = $2
`
type GetUserByPhoneParams struct {
PhoneNumber pgtype.Text `json:"phone_number"`
OrganizationID pgtype.Int8 `json:"organization_id"`
}
type GetUserByPhoneRow struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
NickName pgtype.Text `json:"nick_name"`
Email pgtype.Text `json:"email"`
PhoneNumber pgtype.Text `json:"phone_number"`
Role string `json:"role"`
Age pgtype.Int4 `json:"age"`
EducationLevel pgtype.Text `json:"education_level"`
Country pgtype.Text `json:"country"`
Region pgtype.Text `json:"region"`
EmailVerified bool `json:"email_verified"`
PhoneVerified bool `json:"phone_verified"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Suspended bool `json:"suspended"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
OrganizationID pgtype.Int8 `json:"organization_id"`
}
func (q *Queries) GetUserByPhone(ctx context.Context, arg GetUserByPhoneParams) (GetUserByPhoneRow, error) {
row := q.db.QueryRow(ctx, GetUserByPhone, arg.PhoneNumber, arg.OrganizationID)
var i GetUserByPhoneRow
err := row.Scan(
&i.ID,
&i.FirstName,
&i.LastName,
&i.NickName,
&i.Email,
&i.PhoneNumber,
&i.Role,
&i.Age,
&i.EducationLevel,
&i.Country,
&i.Region,
&i.EmailVerified,
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.Suspended,
&i.SuspendedAt,
&i.OrganizationID,
)
return i, err
}
const SearchUserByNameOrPhone = `-- name: SearchUserByNameOrPhone :many
SELECT id,
first_name,

9
go.mod
View File

@ -6,7 +6,6 @@ toolchain go1.24.11
require (
github.com/amanuelabay/afrosms-go v1.0.6
github.com/go-co-op/gocron v1.37.0
github.com/go-playground/validator/v10 v10.29.0
github.com/joho/godotenv v1.5.1
github.com/resend/resend-go/v2 v2.28.0
@ -16,6 +15,8 @@ require (
golang.org/x/crypto v0.45.0
)
require github.com/rogpeppe/go-internal v1.8.1 // indirect
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
@ -31,7 +32,7 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/jackc/pgio v1.0.0 // indirect
// github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
@ -40,14 +41,12 @@ require (
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/mod v0.29.0 // indirect
@ -66,7 +65,7 @@ require (
github.com/golang/mock v1.6.0 // indirect
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/jackc/pgtype v1.14.4
// github.com/jackc/pgtype v1.14.4
github.com/jackc/pgx/v5 v5.7.6
github.com/klauspost/compress v1.17.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect

152
go.sum
View File

@ -1,7 +1,6 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
@ -21,11 +20,7 @@ github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2N
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -33,10 +28,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@ -55,11 +46,9 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.29.0 h1:lQlF5VNJWNlRbRZNeOIkWElR+1LL/OuHcc0Kp14w1xk=
github.com/go-playground/validator/v10 v10.29.0/go.mod h1:D6QxqeMlgIPuT02L66f2ccrZ7AGgHkzKmmTMZhk/Kc4=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofiber/fiber/v2 v2.32.0/go.mod h1:CMy5ZLiXkn6qwthrl03YMyW1NLfj0rhxz2LKl4t7ZTY=
github.com/gofiber/fiber/v2 v2.52.10 h1:jRHROi2BuNti6NYXmZ6gbNSfT3zj/8c0xy94GOU5elY=
github.com/gofiber/fiber/v2 v2.52.10/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
@ -69,60 +58,17 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8=
github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
@ -130,42 +76,28 @@ github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q=
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:zt6UU74K6Z6oMOYJbJzYpYucqdcQwSMPBEdSvGiaUMw=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
@ -180,7 +112,6 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -189,40 +120,21 @@ github.com/resend/resend-go/v2 v2.28.0 h1:ttM1/VZR4fApBv3xI1TneSKi1pbfFsVrq7fXFl
github.com/resend/resend-go/v2 v2.28.0/go.mod h1:3YCb8c8+pLiqhtRFXTyFwlLvfjQtluxOr9HEh2BwCkQ=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@ -257,57 +169,29 @@ github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfS
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
@ -315,27 +199,15 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -348,48 +220,26 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -399,7 +249,6 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
@ -408,4 +257,3 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@ -381,7 +381,7 @@ func (c *Config) loadEnv() error {
c.AFRO_SMS_SENDER_NAME = os.Getenv("AFRO_SMS_SENDER_NAME")
if c.AFRO_SMS_SENDER_NAME == "" {
c.AFRO_SMS_SENDER_NAME = "FortuneBet"
c.AFRO_SMS_SENDER_NAME = "Yimaru"
}
c.AFRO_SMS_RECEIVER_PHONE_NUMBER = os.Getenv("AFRO_SMS_RECEIVER_PHONE_NUMBER")

View File

@ -1,390 +0,0 @@
package domain
import (
"errors"
"time"
)
var (
ErrInsufficientBalance = errors.New("insufficient balance")
ErrInvalidWithdrawalAmount = errors.New("invalid withdrawal amount")
ErrWithdrawalNotFound = errors.New("withdrawal not found")
ErrPaymentNotFound = errors.New("payment not found")
ErrPaymentAlreadyExists = errors.New("payment with this reference already exists")
ErrInvalidPaymentAmount = errors.New("invalid payment amount")
)
type PaymentStatus string
type WithdrawalStatus string
const (
WithdrawalStatusSuccessful WithdrawalStatus = "success"
WithdrawalStatusPending WithdrawalStatus = "pending"
WithdrawalStatusProcessing WithdrawalStatus = "processing"
WithdrawalStatusCompleted WithdrawalStatus = "completed"
WithdrawalStatusFailed WithdrawalStatus = "failed"
)
const (
PaymentStatusSuccessful PaymentStatus = "success"
PaymentStatusPending PaymentStatus = "pending"
PaymentStatusCompleted PaymentStatus = "completed"
PaymentStatusFailed PaymentStatus = "failed"
)
type ChapaInitDepositRequest struct {
Amount Currency `json:"amount"`
Currency string `json:"currency"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
TxRef string `json:"tx_ref"`
CallbackURL string `json:"callback_url"`
ReturnURL string `json:"return_url"`
PhoneNumber string `json:"phone_number"`
// PhoneNumber string `json:"phone_number"`
}
type ChapaDepositRequestPayload struct {
Amount float64 `json:"amount" validate:"required,gt=0"`
}
type ChapaWebhookPayload struct {
TxRef string `json:"trx_ref"`
Amount Currency `json:"amount"`
// Currency string `json:"currency"`
Status PaymentStatus `json:"status"`
}
type ChapaPaymentWebhookRequest struct {
TxRef string `json:"trx_ref"`
RefId string `json:"ref_id"`
Status PaymentStatus `json:"status"`
}
// PaymentResponse contains the response from payment initialization
type ChapaDepositResponse struct {
CheckoutURL string
Reference string
}
// PaymentVerification contains payment verification details
type ChapaDepositVerification struct {
Status PaymentStatus
Amount Currency
Currency string
}
type ChapaPaymentVerificationResponse struct {
Message string `json:"message"`
Status string `json:"status"`
Data struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
Currency string `json:"currency"`
Amount float64 `json:"amount"`
Charge float64 `json:"charge"`
Mode string `json:"mode"`
Method string `json:"method"`
Type string `json:"type"`
Status string `json:"status"`
Reference string `json:"reference"`
TxRef string `json:"tx_ref"`
Customization struct {
Title string `json:"title"`
Description string `json:"description"`
Logo interface{} `json:"logo"`
} `json:"customization"`
Meta interface{} `json:"meta"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
} `json:"data"`
}
type ChapaTransferVerificationResponse struct {
Message string `json:"message"`
Status string `json:"status"`
Data struct {
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
Mobile interface{} `json:"mobile"`
Currency string `json:"currency"`
Amount float64 `json:"amount"`
Charge float64 `json:"charge"`
Mode string `json:"mode"`
TransferMethod string `json:"transfer_method"`
Narration interface{} `json:"narration"`
ChapaTransferID string `json:"chapa_transfer_id"`
BankCode int `json:"bank_code"`
BankName string `json:"bank_name"`
CrossPartyReference interface{} `json:"cross_party_reference"`
IPAddress string `json:"ip_address"`
Status string `json:"status"`
TxRef string `json:"tx_ref"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
} `json:"data"`
}
type ChapaAllTransactionsResponse struct {
Message string `json:"message"`
Status string `json:"status"`
Data struct {
Transactions []struct {
Status string `json:"status"`
RefID string `json:"ref_id"`
Type string `json:"type"`
CreatedAt string `json:"created_at"`
Currency string `json:"currency"`
Amount string `json:"amount"`
Charge string `json:"charge"`
TransID *string `json:"trans_id"`
PaymentMethod string `json:"payment_method"`
Customer struct {
ID int64 `json:"id"`
Email *string `json:"email"`
FirstName *string `json:"first_name"`
LastName *string `json:"last_name"`
Mobile *string `json:"mobile"`
} `json:"customer"`
} `json:"transactions"`
Pagination struct {
PerPage int `json:"per_page"`
CurrentPage int `json:"current_page"`
FirstPageURL string `json:"first_page_url"`
NextPageURL *string `json:"next_page_url"`
PrevPageURL *string `json:"prev_page_url"`
} `json:"pagination"`
} `json:"data"`
}
type ChapaTransactionEvent struct {
Item int64 `json:"item"`
Message string `json:"message"`
Type string `json:"type"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ChapaTransaction struct {
Status string `json:"status"`
RefID string `json:"ref_id"`
Type string `json:"type"`
CreatedAt string `json:"created_at"`
Currency string `json:"currency"`
Amount string `json:"amount"`
Charge string `json:"charge"`
TransID *string `json:"trans_id"`
PaymentMethod string `json:"payment_method"`
Customer ChapaCustomer `json:"customer"`
}
type ChapaCustomer struct {
ID int64 `json:"id"`
Email *string `json:"email"`
FirstName *string `json:"first_name"`
LastName *string `json:"last_name"`
Mobile *string `json:"mobile"`
}
// type Bank struct {
// ID int `json:"id"`
// Slug string `json:"slug"`
// Swift string `json:"swift"`
// Name string `json:"name"`
// AcctLength int `json:"acct_length"`
// CountryID int `json:"country_id"`
// IsMobileMoney int `json:"is_mobilemoney"` // nullable
// IsActive int `json:"is_active"`
// IsRTGS int `json:"is_rtgs"`
// Active int `json:"active"`
// Is24Hrs int `json:"is_24hrs"` // nullable
// CreatedAt time.Time `json:"created_at"`
// UpdatedAt time.Time `json:"updated_at"`
// Currency string `json:"currency"`
// BankLogo string `json:"bank_logo"` // URL or base64
// }
type SwapResponse struct {
Message string `json:"message"`
Status string `json:"status"`
Data struct {
Status string `json:"status"`
RefID string `json:"ref_id"`
FromCurrency string `json:"from_currency"`
ToCurrency string `json:"to_currency"`
Amount float64 `json:"amount"`
ExchangedAmount float64 `json:"exchanged_amount"`
Charge float64 `json:"charge"`
Rate float64 `json:"rate"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
} `json:"data"`
}
type ChapaTransfersListResponse struct {
Message string `json:"message"`
Status string `json:"status"`
Meta struct {
CurrentPage int `json:"current_page"`
FirstPageURL string `json:"first_page_url"`
LastPage int `json:"last_page"`
LastPageURL string `json:"last_page_url"`
NextPageURL string `json:"next_page_url"`
Path string `json:"path"`
PerPage int `json:"per_page"`
PrevPageURL interface{} `json:"prev_page_url"`
To int `json:"to"`
Total int `json:"total"`
Error []interface{} `json:"error"`
} `json:"meta"`
Data []struct {
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
Currency string `json:"currency"`
Amount float64 `json:"amount"`
Charge float64 `json:"charge"`
TransferType string `json:"transfer_type"`
ChapaReference string `json:"chapa_reference"`
BankCode int `json:"bank_code"`
BankName string `json:"bank_name"`
BankReference interface{} `json:"bank_reference"`
Status string `json:"status"`
Reference interface{} `json:"reference"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
} `json:"data"`
}
type BankResponse struct {
Message string `json:"message"`
Status string `json:"status"`
Data []BankData `json:"data"`
}
type BankData struct {
ID int `json:"id"`
Slug string `json:"slug"`
Swift string `json:"swift"`
Name string `json:"name"`
AcctLength int `json:"acct_length"`
CountryID int `json:"country_id"`
IsMobileMoney int `json:"is_mobilemoney"` // nullable
IsActive int `json:"is_active"`
IsRTGS int `json:"is_rtgs"`
Active int `json:"active"`
Is24Hrs int `json:"is_24hrs"` // nullable
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Currency string `json:"currency"`
}
type ChapaWithdrawal struct {
ID string
UserID int64
Amount Currency
AccountNumber string
BankCode string
Status WithdrawalStatus
Reference string
CreatedAt time.Time
UpdatedAt time.Time
}
type ChapaWithdrawalRequest struct {
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
Amount string `json:"amount"` // string because Chapa API uses string for monetary values
Currency string `json:"currency"`
Reference string `json:"reference"`
BankCode int `json:"bank_code"`
}
// type ChapaWithdrawalRequest struct {
// AccountName string `json:"account_name"`
// AccountNumber string `json:"account_number"`
// Amount Currency `json:"amount"`
// Currency string `json:"currency"`
// BeneficiaryName string `json:"beneficiary_name"`
// BankCode string `json:"bank_code"`
// PhoneNumber string `json:"phone_number"`
// }
type ChapaWithdrawalResponse struct {
Message string `json:"message"`
Status string `json:"status"`
Data string `json:"data"` // Accepts string instead of struct
}
type ChapaTransactionType struct {
Type string `json:"type"`
}
type ChapaWebhookTransfer struct {
Event string `json:"event"`
Type string `json:"type"`
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
BankID int `json:"bank_id"`
BankName string `json:"bank_name"`
Amount string `json:"amount"`
Charge string `json:"charge"`
Currency string `json:"currency"`
Status string `json:"status"`
Reference string `json:"reference"`
ChapaReference string `json:"chapa_reference"`
BankReference string `json:"bank_reference"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
type ChapaWebhookPayment struct {
Event string `json:"event"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email *string `json:"email,omitempty"`
Mobile string `json:"mobile"`
Currency string `json:"currency"`
Amount string `json:"amount"`
Charge string `json:"charge"`
Status string `json:"status"`
Mode string `json:"mode"`
Reference string `json:"reference"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Type string `json:"type"`
TxRef string `json:"tx_ref"`
PaymentMethod string `json:"payment_method"`
Customization ChapaWebhookCustomization `json:"customization"`
Meta interface{} `json:"meta"` // may vary in structure, so kept flexible
}
type ChapaWebhookCustomization struct {
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
Logo *string `json:"logo,omitempty"`
}
type Balance struct {
Currency string `json:"currency"`
AvailableBalance float64 `json:"available_balance"`
LedgerBalance float64 `json:"ledger_balance"`
}
type SwapRequest struct {
From string `json:"from"`
To string `json:"to"`
Amount float64 `json:"amount"`
}
type ChapaCancelResponse struct {
Message string `json:"message"`
Status string `json:"status"`
TxRef string `json:"tx_ref"`
Amount float64 `json:"amount"`
Currency string `json:"currency"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}

View File

@ -1,39 +0,0 @@
package domain
import "time"
type DirectDepositStatus string
const (
DepositStatusPending DirectDepositStatus = "PENDING"
DepositStatusCompleted DirectDepositStatus = "COMPLETED"
DepositStatusRejected DirectDepositStatus = "REJECTED"
)
type DirectDeposit struct {
ID int
CustomerID int
WalletID int
BankName string
AccountNumber string
AccountHolder string
Amount float64
ReferenceNumber string
TransferScreenshot string
Status string
CreatedAt time.Time
ApprovedBy *int
ApprovedAt *time.Time
RejectionReason *string
}
type CreateDirectDeposit struct {
CustomerID int
WalletID int
BankName string
AccountNumber string
AccountHolder string
Amount float64
ReferenceNumber string
TransferScreenshot string
}

View File

@ -1,28 +0,0 @@
package domain
import "time"
type Bank struct {
ID int `json:"id"`
Slug string `json:"slug"`
Swift string `json:"swift"`
Name string `json:"name"`
AcctLength int `json:"acct_length"`
CountryID int `json:"country_id"`
IsMobileMoney int `json:"is_mobilemoney"` // nullable
IsActive int `json:"is_active"`
IsRTGS int `json:"is_rtgs"`
Active int `json:"active"`
Is24Hrs int `json:"is_24hrs"` // nullable
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Currency string `json:"currency"`
BankLogo string `json:"bank_logo"` // URL or base64
}
type InstResponse struct {
Message string `json:"message"`
Status string `json:"status"`
Data interface{} `json:"data"` // Changed to interface{} for flexibility
Pagination *Pagination `json:"pagination,omitempty"` // Made pointer and optional
}

View File

@ -123,11 +123,11 @@ func ReceiverFromRole(role Role) NotificationRecieverSide {
switch role {
case RoleAdmin:
return NotificationRecieverSideAdmin
case RoleCashier:
return NotificationRecieverSideCashier
case RoleBranchManager:
return NotificationRecieverSideBranchManager
case RoleCustomer:
case RoleSuperAdmin:
return NotificationRecieverSideAdmin
case RoleStudent:
return NotificationRecieverSideCustomer
case RoleInstructor:
return NotificationRecieverSideCustomer
default:
return ""

View File

@ -1,186 +0,0 @@
package domain
// import (
// "time"
// dbgen "Yimaru-Backend/gen/db"
// )
// type ReferralCode struct {
// ID int64
// ReferrerID int64
// ReferralCode string
// CompanyID int64
// NumberOfReferrals int64
// RewardAmount Currency
// CreatedAt time.Time
// UpdatedAt time.Time
// }
// type ReferralCodeRes struct {
// ID int64 `json:"id"`
// ReferrerID int64 `json:"referrer_id"`
// ReferralCode string `json:"referral_code"`
// CompanyID int64 `json:"company_id"`
// NumberOfReferrals int64 `json:"number_of_referrals"`
// RewardAmount float32 `json:"reward_amount"`
// CreatedAt time.Time `json:"created_at"`
// UpdatedAt time.Time `json:"updated_at"`
// }
// type CreateReferralCode struct {
// ReferrerID int64
// ReferralCode string
// CompanyID int64
// NumberOfReferrals int64
// RewardAmount Currency
// }
// type UserReferral struct {
// ReferredID int64
// ReferralCodeID int64
// }
// type CreateUserReferrals struct {
// ReferredID int64
// ReferralCodeID int64
// }
// type UpdateReferralCode struct {
// ID int64
// IsActive bool
// ReferralCode string
// RewardAmount Currency
// NumberOfReferrals int64
// }
// type ReferralStats struct {
// TotalReferrals int64
// TotalRewardEarned Currency
// }
// type ReferralStatsRes struct {
// TotalReferrals int64 `json:"total_referrals"`
// TotalRewardEarned float32 `json:"total_reward_earned"`
// }
// // type ReferralSettings struct {
// // ID int64
// // ReferralRewardAmount float64
// // CashbackPercentage float64
// // BetReferralBonusPercentage float64
// // MaxReferrals int32
// // ExpiresAfterDays int32
// // UpdatedBy string
// // CreatedAt time.Time
// // UpdatedAt time.Time
// // Version int32
// // }
// // type ReferralSettingsReq struct {
// // ReferralRewardAmount float64 `json:"referral_reward_amount" validate:"required"`
// // CashbackPercentage float64 `json:"cashback_percentage" validate:"required"`
// // MaxReferrals int32 `json:"max_referrals" validate:"required"`
// // UpdatedBy string `json:"updated_by" validate:"required"`
// // }
// func ConvertCreateReferralCode(code CreateReferralCode) dbgen.CreateReferralCodeParams {
// return dbgen.CreateReferralCodeParams{
// ReferralCode: code.ReferralCode,
// ReferrerID: code.ReferrerID,
// CompanyID: code.CompanyID,
// NumberOfReferrals: code.NumberOfReferrals,
// RewardAmount: int64(code.RewardAmount),
// }
// }
// func ConvertDBReferralCode(code dbgen.ReferralCode) ReferralCode {
// return ReferralCode{
// ID: code.ID,
// ReferrerID: code.ReferrerID,
// ReferralCode: code.ReferralCode,
// NumberOfReferrals: code.NumberOfReferrals,
// RewardAmount: Currency(code.RewardAmount),
// CompanyID: code.CompanyID,
// CreatedAt: code.CreatedAt.Time,
// UpdatedAt: code.UpdatedAt.Time,
// }
// }
// func ConvertDBReferralCodes(codes []dbgen.ReferralCode) []ReferralCode {
// result := make([]ReferralCode, len(codes))
// for i, code := range codes {
// result[i] = ConvertDBReferralCode(code)
// }
// return result
// }
// func ConvertCreateUserReferral(referral CreateUserReferrals) dbgen.CreateUserReferralParams {
// return dbgen.CreateUserReferralParams{
// ReferredID: referral.ReferredID,
// ReferralCodeID: referral.ReferralCodeID,
// }
// }
// func ConvertDBUserReferral(referral dbgen.UserReferral) UserReferral {
// return UserReferral{
// ReferredID: referral.ReferredID,
// ReferralCodeID: referral.ReferralCodeID,
// }
// }
// func ConvertDBUserReferrals(referrals []dbgen.UserReferral) []UserReferral {
// result := make([]UserReferral, len(referrals))
// for i, referral := range referrals {
// result[i] = ConvertDBUserReferral(referral)
// }
// return result
// }
// func ConvertUpdateReferralCode(referralCode UpdateReferralCode) dbgen.UpdateReferralCodeParams {
// return dbgen.UpdateReferralCodeParams{
// ID: referralCode.ID,
// IsActive: referralCode.IsActive,
// ReferralCode: referralCode.ReferralCode,
// NumberOfReferrals: referralCode.NumberOfReferrals,
// RewardAmount: int64(referralCode.RewardAmount),
// }
// }
// func ConvertDBReferralStats(stats dbgen.GetReferralStatsRow) ReferralStats {
// return ReferralStats{
// TotalReferrals: stats.TotalReferrals,
// TotalRewardEarned: Currency(stats.TotalRewardEarned),
// }
// }
// func ConvertReferralCodeRes(referral ReferralCode) ReferralCodeRes {
// return ReferralCodeRes{
// ID: referral.ID,
// ReferrerID: referral.ReferrerID,
// ReferralCode: referral.ReferralCode,
// CompanyID: referral.CompanyID,
// NumberOfReferrals: referral.NumberOfReferrals,
// RewardAmount: referral.RewardAmount.Float32(),
// CreatedAt: referral.CreatedAt,
// UpdatedAt: referral.UpdatedAt,
// }
// }
// func ConvertReferralCodeResList(referrals []ReferralCode) []ReferralCodeRes {
// result := make([]ReferralCodeRes, len(referrals))
// for i, referral := range referrals {
// result[i] = ConvertReferralCodeRes(referral)
// }
// return result
// }
// func ConvertReferralStatsRes(stats ReferralStats) ReferralStatsRes {
// return ReferralStatsRes{
// TotalReferrals: stats.TotalReferrals,
// TotalRewardEarned: stats.TotalRewardEarned.Float32(),
// }
// }

View File

@ -5,17 +5,20 @@ type Role string
const (
RoleSuperAdmin Role = "super_admin"
RoleAdmin Role = "admin"
RoleBranchManager Role = "branch_manager"
RoleCustomer Role = "customer"
RoleCashier Role = "cashier"
RoleTransactionApprover Role = "transaction_approver"
RoleStudent Role = "student"
RoleInstructor Role = "instructor"
RoleSupport Role = "support"
)
func (r Role) IsValid() bool {
switch r {
case RoleSuperAdmin, RoleAdmin, RoleBranchManager, RoleCustomer, RoleCashier, RoleTransactionApprover:
case RoleSuperAdmin, RoleAdmin, RoleStudent, RoleInstructor, RoleSupport:
return true
default:
return false
}
}
func (r Role) Value() string {
return string(r)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +1,44 @@
package domain
// import (
// "time"
import (
"time"
)
// dbgen "Yimaru-Backend/gen/db"
// )
type Setting struct {
Key string
Value string
UpdatedAt time.Time
}
// type Setting struct {
// Key string
// Value string
// UpdatedAt time.Time
// }
type CreateSetting struct {
Key string
Value string
}
// type CreateSetting struct {
// Key string
// Value string
// }
type SettingRes struct {
Key string `json:"key"`
Value string `json:"value"`
UpdatedAt time.Time `json:"updated_at"`
}
type CompanySetting struct {
Key string
Value string
CompanyID int64
UpdatedAt time.Time
CreatedAt time.Time
}
// type SettingRes struct {
// Key string `json:"key"`
// Value string `json:"value"`
// UpdatedAt time.Time `json:"updated_at"`
// }
// type CompanySetting struct {
// Key string
// Value string
// CompanyID int64
// UpdatedAt time.Time
// CreatedAt time.Time
// }
type CompanySettingRes struct {
Key string `json:"key"`
Value string `json:"value"`
CompanyID int64 `json:"company_id"`
UpdatedAt time.Time `json:"updated_at"`
CreatedAt time.Time `json:"created_at"`
}
// type CompanySettingRes struct {
// Key string `json:"key"`
// Value string `json:"value"`
// CompanyID int64 `json:"company_id"`
// UpdatedAt time.Time `json:"updated_at"`
// CreatedAt time.Time `json:"created_at"`
// }
// func ConvertSetting(setting Setting) SettingRes {
// return SettingRes(setting)
// }
func ConvertSetting(setting Setting) SettingRes {
return SettingRes(setting)
}
// func ConvertCompanySetting(companySetting dbgen.CompanySetting) CompanySetting {
// return CompanySetting{

View File

@ -8,13 +8,13 @@ import (
type NotificationStore interface {
GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error)
ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) // New method
// ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) // New method
CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error)
GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error)
GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error)
// GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error)
CreateNotification(ctx context.Context, notification *domain.Notification) (*domain.Notification, error)
UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error)
ListFailedNotifications(ctx context.Context, limit int) ([]domain.Notification, error)
DeleteOldNotifications(ctx context.Context) error
// UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error)
// ListFailedNotifications(ctx context.Context, limit int) ([]domain.Notification, error)
// DeleteOldNotifications(ctx context.Context) error
}

View File

@ -1,15 +1,16 @@
package ports
import (
"Yimaru-Backend/internal/domain"
"context"
)
type SettingStore interface {
// GetGlobalSettingList(ctx context.Context) (domain.SettingList, error)
// GetGlobalSettings(ctx context.Context) ([]domain.Setting, error)
// GetGlobalSetting(ctx context.Context, key string) (domain.Setting, error)
// UpdateGlobalSetting(ctx context.Context, key, value string) error
// UpdateGlobalSettingList(ctx context.Context, settingList domain.ValidSettingList) error
GetGlobalSettingList(ctx context.Context) (domain.SettingList, error)
GetGlobalSettings(ctx context.Context) ([]domain.Setting, error)
GetGlobalSetting(ctx context.Context, key string) (domain.Setting, error)
UpdateGlobalSetting(ctx context.Context, key, value string) error
UpdateGlobalSettingList(ctx context.Context, settingList domain.ValidSettingList) error
// InsertCompanySetting(ctx context.Context, key, value string, companyID int64) error
// InsertCompanySettingList(ctx context.Context, settingList domain.ValidSettingList, companyID int64) error
@ -18,6 +19,6 @@ type SettingStore interface {
// GetOverrideSettings(ctx context.Context, companyID int64) ([]domain.Setting, error)
// GetOverrideSettingsList(ctx context.Context, companyID int64) (domain.SettingList, error)
// DeleteCompanySetting(ctx context.Context, companyID int64, key string) error
DeleteAllCompanySetting(ctx context.Context, companyID int64) error
// DeleteAllCompanySetting(ctx context.Context, companyID int64) error
EnsureAllSettingsExist(ctx context.Context) error
}

View File

@ -2,34 +2,53 @@ package ports
import (
"context"
"time"
"Yimaru-Backend/internal/domain"
)
type UserStore interface {
CreateUser(ctx context.Context, user domain.User, usedOtpId int64, is_company bool) (domain.User, error)
CreateUserWithoutOtp(ctx context.Context, user domain.User, is_company bool) (domain.User, error)
CreateUser(ctx context.Context, user domain.User, usedOtpId int64) (domain.User, error)
CreateUserWithoutOtp(ctx context.Context, user domain.User) (domain.User, error)
GetUserByID(ctx context.Context, id int64) (domain.User, error)
GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error)
// GetAllCashiers(ctx context.Context, filter domain.UserFilter) ([]domain.GetCashier, int64, error)
// GetCashierByID(ctx context.Context, cashierID int64) (domain.GetCashier, error)
GetCashiersByBranch(ctx context.Context, branchID int64) ([]domain.User, error)
GetAdminByCompanyID(ctx context.Context, companyID int64) (domain.User, error)
UpdateUser(ctx context.Context, user domain.UpdateUserReq) error
UpdateUserCompany(ctx context.Context, id int64, companyID int64) error
GetAllUsers(
ctx context.Context,
role *string,
organizationID *int64,
query *string,
createdBefore, createdAfter *time.Time,
limit, offset int32,
) ([]domain.User, int64, error)
GetTotalUsers(ctx context.Context, role *string, organizationID *int64) (int64, error)
SearchUserByNameOrPhone(ctx context.Context, search string, organizationID *int64, role *string) ([]domain.User, error)
UpdateUser(ctx context.Context, user domain.User) error
UpdateUserOrganization(ctx context.Context, userID, organizationID int64) error
SuspendUser(ctx context.Context, userID int64, suspended bool, suspendedAt time.Time) error
DeleteUser(ctx context.Context, userID int64) error
CheckPhoneEmailExist(ctx context.Context, phone, email string, organizationID domain.ValidInt64) (phoneExists, emailExists bool, err error)
GetUserByEmailPhone(
ctx context.Context,
email string,
phone string,
organizationID domain.ValidInt64,
) (domain.User, error)
UpdatePassword(ctx context.Context, password, email, phone string, organizationID int64, updatedAt time.Time) error
GetOwnerByOrganizationID(ctx context.Context, organizationID int64) (domain.User, error)
UpdateUserSuspend(ctx context.Context, id int64, status bool) error
DeleteUser(ctx context.Context, id int64) error
CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error)
GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (domain.User, error)
GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error)
SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error)
UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64, companyId int64) error
GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error)
GetCustomerCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
GetCustomerDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.CustomerDetail, error)
GetBranchCustomerCounts(ctx context.Context, filter domain.ReportFilter) (map[int64]int64, error)
GetRoleCounts(ctx context.Context, role string, filter domain.ReportFilter) (total, active, inactive int64, err error)
// UpdateUser(ctx context.Context, user domain.UpdateUserReq) error
// UpdateUserSuspend(ctx context.Context, id int64, status bool) error
// DeleteUser(ctx context.Context, id int64) error
// CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error)
// GetUserByEmail(ctx context.Context, email string, companyID domain.ValidInt64) (domain.User, error)
// GetUserByPhone(ctx context.Context, phoneNum string, companyID domain.ValidInt64) (domain.User, error)
// SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error)
// UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64, companyId int64) error
// GetUserByEmailPhone(ctx context.Context, email, phone string, companyID domain.ValidInt64) (domain.User, error)
// GetCustomerCounts(ctx context.Context, filter domain.ReportFilter) (total, active, inactive int64, err error)
// GetCustomerDetails(ctx context.Context, filter domain.ReportFilter) (map[int64]domain.CustomerDetail, error)
// GetRoleCounts(ctx context.Context, role string, filter domain.ReportFilter) (total, active, inactive int64, err error)
}
type SmsGateway interface {
SendSMSOTP(ctx context.Context, phoneNumber, otp string) error

View File

@ -0,0 +1,64 @@
package repository
import (
dbgen "Yimaru-Backend/gen/db"
"context"
)
type ReportedIssueRepository interface {
CreateReportedIssue(ctx context.Context, arg dbgen.CreateReportedIssueParams) (dbgen.ReportedIssue, error)
ListReportedIssues(ctx context.Context, limit, offset int32) ([]dbgen.ReportedIssue, error)
ListReportedIssuesByUser(ctx context.Context, userID int64, limit, offset int32) ([]dbgen.ReportedIssue, error)
CountReportedIssues(ctx context.Context) (int64, error)
CountReportedIssuesByUser(ctx context.Context, userID int64) (int64, error)
UpdateReportedIssueStatus(ctx context.Context, id int64, status string) error
DeleteReportedIssue(ctx context.Context, id int64) error
}
type ReportedIssueRepo struct {
store *Store
}
func NewReportedIssueRepository(store *Store) ReportedIssueRepository {
return &ReportedIssueRepo{store: store}
}
func (s *ReportedIssueRepo) CreateReportedIssue(ctx context.Context, arg dbgen.CreateReportedIssueParams) (dbgen.ReportedIssue, error) {
return s.store.queries.CreateReportedIssue(ctx, arg)
}
func (s *ReportedIssueRepo) ListReportedIssues(ctx context.Context, limit, offset int32) ([]dbgen.ReportedIssue, error) {
params := dbgen.ListReportedIssuesParams{
Limit: limit,
Offset: offset,
}
return s.store.queries.ListReportedIssues(ctx, params)
}
func (s *ReportedIssueRepo) ListReportedIssuesByUser(ctx context.Context, userID int64, limit, offset int32) ([]dbgen.ReportedIssue, error) {
params := dbgen.ListReportedIssuesByUserParams{
UserID: userID,
Limit: limit,
Offset: offset,
}
return s.store.queries.ListReportedIssuesByUser(ctx, params)
}
func (s *ReportedIssueRepo) CountReportedIssues(ctx context.Context) (int64, error) {
return s.store.queries.CountReportedIssues(ctx)
}
func (s *ReportedIssueRepo) CountReportedIssuesByUser(ctx context.Context, userID int64) (int64, error) {
return s.store.queries.CountReportedIssuesByUser(ctx, userID)
}
func (s *ReportedIssueRepo) UpdateReportedIssueStatus(ctx context.Context, id int64, status string) error {
return s.store.queries.UpdateReportedIssueStatus(ctx, dbgen.UpdateReportedIssueStatusParams{
ID: id,
Status: status,
})
}
func (s *ReportedIssueRepo) DeleteReportedIssue(ctx context.Context, id int64) error {
return s.store.queries.DeleteReportedIssue(ctx, id)
}

View File

@ -1,356 +1,192 @@
package repository
// import (
// "context"
// "encoding/json"
// "fmt"
import (
"context"
"encoding/json"
// dbgen "Yimaru-Backend/gen/db"
// "Yimaru-Backend/internal/domain"
// "Yimaru-Backend/internal/ports"
// "github.com/jackc/pgx/v5/pgtype"
// )
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/ports"
// func NewNotificationStore(s *Store) ports.NotificationStore { return s }
"github.com/jackc/pgx/v5/pgtype"
)
// func (r *Store) CreateNotification(ctx context.Context, notification *domain.Notification) (*domain.Notification, error) {
// var errorSeverity pgtype.Text
// if notification.ErrorSeverity != "" {
// errorSeverity.String = string(notification.ErrorSeverity)
// errorSeverity.Valid = true
// }
func NewNotificationStore(s *Store) ports.NotificationStore {
return s
}
// var deliveryChannel pgtype.Text
// if notification.DeliveryChannel != "" {
// deliveryChannel.String = string(notification.DeliveryChannel)
// deliveryChannel.Valid = true
// }
/* =========================
Create
========================= */
// var priority pgtype.Int4
// if notification.Priority != 0 {
// priority.Int32 = int32(notification.Priority)
// priority.Valid = true
// }
func (r *Store) CreateNotification(
ctx context.Context,
n *domain.Notification,
) (*domain.Notification, error) {
// params := dbgen.CreateNotificationParams{
// ID: notification.ID,
// RecipientID: notification.RecipientID,
// Type: string(notification.Type),
// Level: string(notification.Level),
// ErrorSeverity: errorSeverity,
// Reciever: string(notification.Reciever),
// IsRead: notification.IsRead,
// DeliveryStatus: string(notification.DeliveryStatus),
// DeliveryChannel: deliveryChannel,
// Payload: marshalPayload(notification.Payload),
// Priority: priority,
// Timestamp: pgtype.Timestamptz{Time: notification.Timestamp, Valid: true},
// Expires: pgtype.Timestamptz{Time: notification.Expires, Valid: true},
// Img: pgtype.Text{String: notification.Image, Valid: notification.Image != ""},
// Metadata: notification.Metadata,
// }
params := dbgen.CreateNotificationParams{
UserID: n.RecipientID,
Type: string(n.Type),
Level: string(n.Level),
Channel: pgtype.Text{String: string(n.DeliveryChannel)},
Title: n.Payload.Headline,
Message: n.Payload.Message,
Payload: marshalPayload(n.Payload),
}
// dbNotification, err := r.queries.CreateNotification(ctx, params)
// if err != nil {
// return nil, err
// }
dbNotif, err := r.queries.CreateNotification(ctx, params)
if err != nil {
return nil, err
}
// return r.mapDBToDomain(&dbNotification), nil
// }
return mapDBToDomain(&dbNotif), nil
}
// func (r *Store) UpdateNotificationStatus(ctx context.Context, id, status string, isRead bool, metadata []byte) (*domain.Notification, error) {
// params := dbgen.UpdateNotificationStatusParams{
// ID: id,
// DeliveryStatus: status,
// IsRead: isRead,
// Metadata: metadata,
// }
/* =========================
Read
========================= */
// dbNotification, err := r.queries.UpdateNotificationStatus(ctx, params)
// if err != nil {
// return nil, err
// }
func (r *Store) GetUserNotifications(
ctx context.Context,
userID int64,
limit, offset int,
) ([]domain.Notification, int64, error) {
// return r.mapDBToDomain(&dbNotification), nil
// }
params := dbgen.GetUserNotificationsParams{
UserID: userID,
Limit: int32(limit),
Offset: int32(offset),
}
// func (r *Store) GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) {
// params := dbgen.GetUserNotificationsParams{
// RecipientID: recipientID,
// Limit: int32(limit),
// Offset: int32(offset),
// }
rows, err := r.queries.GetUserNotifications(ctx, params)
if err != nil {
return nil, 0, err
}
// dbNotifications, err := r.queries.GetUserNotifications(ctx, params)
// if err != nil {
// return nil, 0, err
// }
total, err := r.queries.GetUserNotificationCount(ctx, userID)
if err != nil {
return nil, 0, err
}
// total, err := r.queries.GetUserNotificationCount(ctx, recipientID)
result := make([]domain.Notification, 0, len(rows))
for _, row := range rows {
result = append(result, *mapDBToDomain(&row))
}
// if err != nil {
// return nil, 0, err
// }
return result, total, nil
}
// var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications))
// for _, dbNotif := range dbNotifications {
// domainNotif := r.mapDBToDomain(&dbNotif)
// result = append(result, *domainNotif)
// }
func (r *Store) GetAllNotifications(
ctx context.Context,
limit, offset int,
) ([]domain.Notification, error) {
// return result, total, nil
// }
rows, err := r.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{
Limit: int32(limit),
Offset: int32(offset),
})
if err != nil {
return nil, err
}
// func (r *Store) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) {
result := make([]domain.Notification, 0, len(rows))
for _, row := range rows {
result = append(result, *mapDBToDomain(&row))
}
// dbNotifications, err := r.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{
// Limit: int32(limit),
// Offset: int32(offset),
// })
// if err != nil {
// return nil, err
// }
return result, nil
}
// var result []domain.Notification = make([]domain.Notification, 0, len(dbNotifications))
// for _, dbNotif := range dbNotifications {
// domainNotif := r.mapDBToDomain(&dbNotif)
// result = append(result, *domainNotif)
// }
// return result, nil
// }
func (r *Store) CountUnreadNotifications(
ctx context.Context,
userID int64,
) (int64, error) {
return r.queries.CountUnreadNotifications(ctx, userID)
}
// func (r *Store) ListFailedNotifications(ctx context.Context, limit int) ([]domain.Notification, error) {
// dbNotifications, err := r.queries.ListFailedNotifications(ctx, int32(limit))
// if err != nil {
// return nil, err
// }
/* =========================
Update
========================= */
// var result []domain.Notification
// for _, dbNotif := range dbNotifications {
// domainNotif := r.mapDBToDomain(&dbNotif)
// result = append(result, *domainNotif)
// }
func (r *Store) MarkNotificationAsRead(
ctx context.Context,
id int64,
) (*domain.Notification, error) {
// return result, nil
// }
dbNotif, err := r.queries.MarkNotificationAsRead(ctx, id)
if err != nil {
return nil, err
}
// func (r *Store) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) {
// return r.queries.ListRecipientIDsByReceiver(ctx, string(receiver))
// }
return mapDBToDomain(&dbNotif), nil
}
// func (s *Store) DeleteOldNotifications(ctx context.Context) error {
// return s.queries.DeleteOldNotifications(ctx)
// }
func (r *Store) MarkAllUserNotificationsAsRead(
ctx context.Context,
userID int64,
) error {
return r.queries.MarkAllUserNotificationsAsRead(ctx, userID)
}
// func (r *Store) mapDBToDomain(dbNotif *dbgen.Notification) *domain.Notification {
// var errorSeverity domain.NotificationErrorSeverity
// if dbNotif.ErrorSeverity.Valid {
// errorSeverity = domain.NotificationErrorSeverity(dbNotif.ErrorSeverity.String)
/* =========================
Delete
========================= */
// } else {
// errorSeverity = ""
// }
func (r *Store) DeleteUserNotifications(
ctx context.Context,
userID int64,
) error {
return r.queries.DeleteUserNotifications(ctx, userID)
}
// var deliveryChannel domain.DeliveryChannel
// if dbNotif.DeliveryChannel.Valid {
// deliveryChannel = domain.DeliveryChannel(dbNotif.DeliveryChannel.String)
// } else {
// deliveryChannel = ""
// }
/* =========================
Mapping
========================= */
// var priority int
// if dbNotif.Priority.Valid {
// priority = int(dbNotif.Priority.Int32)
// }
func mapDBToDomain(db *dbgen.Notification) *domain.Notification {
payload, err := unmarshalPayload(db.Payload)
if err != nil {
payload = domain.NotificationPayload{}
}
// payload, err := unmarshalPayload(dbNotif.Payload)
// if err != nil {
// payload = domain.NotificationPayload{}
// }
var channel domain.DeliveryChannel
if db.Channel.Valid {
channel = domain.DeliveryChannel(db.Channel.String)
}
// return &domain.Notification{
// ID: dbNotif.ID,
// RecipientID: dbNotif.RecipientID,
// Type: domain.NotificationType(dbNotif.Type),
// Level: domain.NotificationLevel(dbNotif.Level),
// ErrorSeverity: errorSeverity,
// Reciever: domain.NotificationRecieverSide(dbNotif.Reciever),
// IsRead: dbNotif.IsRead,
// DeliveryStatus: domain.NotificationDeliveryStatus(dbNotif.DeliveryStatus),
// DeliveryChannel: deliveryChannel,
// Payload: payload,
// Priority: priority,
// Timestamp: dbNotif.Timestamp.Time,
// Expires: dbNotif.Expires.Time,
// Image: dbNotif.Img.String,
// Metadata: dbNotif.Metadata,
// }
// }
return &domain.Notification{
ID: string(db.ID),
RecipientID: db.UserID,
Type: domain.NotificationType(db.Type),
Level: domain.NotificationLevel(db.Level),
DeliveryChannel: channel,
DeliveryStatus: "PENDING",
Payload: domain.NotificationPayload{
Headline: payload.Headline,
Message: payload.Message,
},
IsRead: db.IsRead,
Timestamp: db.CreatedAt.Time,
// ReadAt: db.ReadAt.Time,
}
}
// func marshalPayload(payload domain.NotificationPayload) []byte {
// data, _ := json.Marshal(payload)
// return data
// }
/* =========================
JSON Helpers
========================= */
// func unmarshalPayload(data []byte) (domain.NotificationPayload, error) {
// var payload domain.NotificationPayload
// if err := json.Unmarshal(data, &payload); err != nil {
// return domain.NotificationPayload{}, err
// }
// return payload, nil
// }
func marshalPayload(p domain.NotificationPayload) []byte {
b, _ := json.Marshal(p)
return b
}
// func (r *Store) CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) {
// return r.queries.CountUnreadNotifications(ctx, recipient_id)
// }
// func (r *Store) GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error) {
// rows, err := r.queries.GetNotificationCounts(ctx)
// if err != nil {
// return 0, 0, 0, fmt.Errorf("failed to get notification counts: %w", err)
// }
// // var total, read, unread int64
// for _, row := range rows {
// total += row.Total
// read += row.Read
// unread += row.Unread
// }
// return total, read, unread, nil
// }
// func (s *Store) GetMostActiveNotificationRecipients(ctx context.Context, filter domain.ReportFilter, limit int) ([]domain.ActiveNotificationRecipient, error) {
// query := `SELECT
// n.recipient_id,
// u.first_name || ' ' || u.last_name as recipient_name,
// COUNT(*) as notification_count,
// MAX(n.timestamp) as last_notification_time
// FROM notifications n
// JOIN users u ON n.recipient_id = u.id
// WHERE n.timestamp BETWEEN $1 AND $2
// GROUP BY n.recipient_id, u.first_name, u.last_name
// ORDER BY notification_count DESC
// LIMIT $3`
// var recipients []domain.ActiveNotificationRecipient
// rows, err := s.conn.Query(ctx, query, filter.StartTime.Value, filter.EndTime.Value, limit)
// if err != nil {
// return nil, fmt.Errorf("failed to get active notification recipients: %w", err)
// }
// defer rows.Close()
// for rows.Next() {
// var r domain.ActiveNotificationRecipient
// if err := rows.Scan(&r.RecipientID, &r.RecipientName, &r.NotificationCount, &r.LastNotificationTime); err != nil {
// return nil, err
// }
// recipients = append(recipients, r)
// }
// return recipients, nil
// }
// // GetNotificationDeliveryStats
// func (s *Store) GetNotificationDeliveryStats(ctx context.Context, filter domain.ReportFilter) (domain.NotificationDeliveryStats, error) {
// query := `SELECT
// COUNT(*) as total_sent,
// COUNT(CASE WHEN delivery_status = 'failed' THEN 1 END) as failed_deliveries,
// (COUNT(CASE WHEN delivery_status = 'sent' THEN 1 END) * 100.0 / NULLIF(COUNT(*), 0)) as success_rate,
// MODE() WITHIN GROUP (ORDER BY delivery_channel) as most_used_channel
// FROM notifications
// WHERE timestamp BETWEEN $1 AND $2`
// var stats domain.NotificationDeliveryStats
// row := s.conn.QueryRow(ctx, query, filter.StartTime.Value, filter.EndTime.Value)
// err := row.Scan(&stats.TotalSent, &stats.FailedDeliveries, &stats.SuccessRate, &stats.MostUsedChannel)
// if err != nil {
// return domain.NotificationDeliveryStats{}, fmt.Errorf("failed to get notification delivery stats: %w", err)
// }
// return stats, nil
// }
// // GetNotificationCountsByType
// func (s *Store) GetNotificationCountsByType(ctx context.Context, filter domain.ReportFilter) (map[string]domain.NotificationTypeCount, error) {
// query := `SELECT
// type,
// COUNT(*) as total,
// COUNT(CASE WHEN is_read = true THEN 1 END) as read,
// COUNT(CASE WHEN is_read = false THEN 1 END) as unread
// FROM notifications
// WHERE timestamp BETWEEN $1 AND $2
// GROUP BY type`
// counts := make(map[string]domain.NotificationTypeCount)
// rows, err := s.conn.Query(ctx, query, filter.StartTime.Value, filter.EndTime.Value)
// if err != nil {
// return nil, fmt.Errorf("failed to get notification counts by type: %w", err)
// }
// defer rows.Close()
// for rows.Next() {
// var nt domain.NotificationTypeCount
// var typ string
// if err := rows.Scan(&typ, &nt.Total, &nt.Read, &nt.Unread); err != nil {
// return nil, err
// }
// counts[typ] = nt
// }
// return counts, nil
// }
// // func (s *Store) GetAllNotifications(ctx context.Context, limit, offset int) ([]domain.Notification, error) {
// // dbNotifications, err := s.queries.GetAllNotifications(ctx, dbgen.GetAllNotificationsParams{
// // Limit: int32(limit),
// // Offset: int32(offset),
// // })
// // if err != nil {
// // return nil, err
// // }
// // result := make([]domain.Notification, 0, len(dbNotifications))
// // for _, dbNotif := range dbNotifications {
// // // You may want to move this mapping logic to a shared function if not already present
// // var errorSeverity *domain.NotificationErrorSeverity
// // if dbNotif.ErrorSeverity.Valid {
// // s := domain.NotificationErrorSeverity(dbNotif.ErrorSeverity.String)
// // errorSeverity = &s
// // }
// // var deliveryChannel domain.DeliveryChannel
// // if dbNotif.DeliveryChannel.Valid {
// // deliveryChannel = domain.DeliveryChannel(dbNotif.DeliveryChannel.String)
// // } else {
// // deliveryChannel = ""
// // }
// // var priority int
// // if dbNotif.Priority.Valid {
// // priority = int(dbNotif.Priority.Int32)
// // }
// // payload, err := unmarshalPayload(dbNotif.Payload)
// // if err != nil {
// // payload = domain.NotificationPayload{}
// // }
// // result = append(result, domain.Notification{
// // ID: dbNotif.ID,
// // RecipientID: dbNotif.RecipientID,
// // Type: domain.NotificationType(dbNotif.Type),
// // Level: domain.NotificationLevel(dbNotif.Level),
// // ErrorSeverity: errorSeverity,
// // Reciever: domain.NotificationRecieverSide(dbNotif.Reciever),
// // IsRead: dbNotif.IsRead,
// // DeliveryStatus: domain.NotificationDeliveryStatus(dbNotif.DeliveryStatus),
// // DeliveryChannel: deliveryChannel,
// // Payload: payload,
// // Priority: priority,
// // Timestamp: dbNotif.Timestamp.Time,
// // Metadata: dbNotif.Metadata,
// // })
// // }
// // return result, nil
// // }
func unmarshalPayload(b []byte) (domain.NotificationPayload, error) {
var p domain.NotificationPayload
if len(b) == 0 {
return p, nil
}
if err := json.Unmarshal(b, &p); err != nil {
return p, err
}
return p, nil
}

View File

@ -0,0 +1,168 @@
package repository
import (
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/ports"
"context"
"fmt"
"go.uber.org/zap"
)
// Interface for creating new setting store
func NewSettingStore(s *Store) ports.SettingStore { return s }
func (s *Store) InsertGlobalSetting(ctx context.Context, setting domain.CreateSetting) error {
err := s.queries.InsertGlobalSetting(ctx, dbgen.InsertGlobalSettingParams{
Key: setting.Key,
Value: setting.Value,
})
if err != nil {
return err
}
return nil
}
func (s *Store) GetGlobalSettingList(ctx context.Context) (domain.SettingList, error) {
settings, err := s.queries.GetGlobalSettings(ctx)
if err != nil {
domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err))
return domain.SettingList{}, err
}
return domain.ConvertDBGlobalSettingList(settings)
}
func (s *Store) GetGlobalSettings(ctx context.Context) ([]domain.Setting, error) {
settings, err := s.queries.GetGlobalSettings(ctx)
if err != nil {
domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err))
}
var result []domain.Setting = make([]domain.Setting, 0, len(settings))
for _, setting := range settings {
result = append(result, domain.Setting{
Key: setting.Key,
Value: setting.Value,
UpdatedAt: setting.UpdatedAt.Time,
})
}
return result, nil
}
func (s *Store) GetGlobalSetting(ctx context.Context, key string) (domain.Setting, error) {
dbSetting, err := s.queries.GetGlobalSetting(ctx, key)
if err != nil {
domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err))
}
result := domain.Setting{
Key: dbSetting.Key,
Value: dbSetting.Value,
UpdatedAt: dbSetting.UpdatedAt.Time,
}
return result, nil
}
func (s *Store) UpdateGlobalSetting(ctx context.Context, key, value string) error {
err := s.queries.UpdateGlobalSetting(ctx, dbgen.UpdateGlobalSettingParams{
Key: key,
Value: value,
})
if err != nil {
domain.MongoDBLogger.Error("failed to update setting",
zap.String("key", key),
zap.String("value", value),
zap.Error(err),
)
return err
}
return err
}
func (s *Store) UpdateGlobalSettingList(ctx context.Context, settingList domain.ValidSettingList) error {
convertedSettings := settingList.ConvertAllSettings()
for _, setting := range convertedSettings {
err := s.UpdateGlobalSetting(ctx, setting.Key, setting.Value)
if err != nil {
domain.MongoDBLogger.Warn("failed to update setting list", zap.String("key", setting.Key), zap.Error(err))
return err
}
}
return nil
}
// func (s *Store) GetOverrideSettings(ctx context.Context, companyID int64) ([]domain.Setting, error) {
// settings, err := s.queries.GetOverrideSettings(ctx, companyID)
// if err != nil {
// return nil, err
// }
// result := make([]domain.Setting, 0, len(settings))
// for _, setting := range settings {
// result = append(result, domain.Setting{
// Key: setting.Key,
// Value: setting.Value,
// UpdatedAt: setting.UpdatedAt.Time,
// })
// }
// return result, nil
// }
// func (s *Store) GetOverrideSettingsList(ctx context.Context, companyID int64) (domain.SettingList, error) {
// settings, err := s.queries.GetOverrideSettings(ctx, companyID)
// if err != nil {
// return domain.SettingList{}, err
// }
// return domain.ConvertDBOverrideSettingList(settings)
// }
// func (s *Store) DeleteCompanySetting(ctx context.Context, companyID int64, key string) error {
// return s.queries.DeleteCompanySetting(ctx, dbgen.DeleteCompanySettingParams{
// CompanyID: companyID,
// Key: key,
// })
// }
// func (s *Store) DeleteAllCompanySetting(ctx context.Context, companyID int64) error {
// return s.queries.DeleteAllCompanySetting(ctx, companyID)
// }
func (s *Store) EnsureAllSettingsExist(ctx context.Context) error {
defaultSettings := domain.NewDefaultSettingList().ToSettingArray() // returns []domain.Setting from your typed struct
dbSettings, err := s.GetGlobalSettings(ctx)
if err != nil {
return fmt.Errorf("failed to fetch settings: %w", err)
}
existing := map[string]struct{}{}
for _, s := range dbSettings {
existing[s.Key] = struct{}{}
}
for _, setting := range defaultSettings {
if _, found := existing[setting.Key]; !found {
if err := s.InsertGlobalSetting(ctx, domain.CreateSetting{
Key: setting.Key,
Value: setting.Value,
}); err != nil {
return fmt.Errorf("failed to create missing setting %q: %w", setting.Key, err)
}
}
}
return nil
}

View File

@ -3,7 +3,10 @@ package repository
import (
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/ports"
"Yimaru-Backend/internal/services/authentication"
"context"
"database/sql"
"errors"
"time"
@ -23,6 +26,54 @@ import (
// }
// }
func NewUserStore(s *Store) ports.UserStore { return s }
func (s *Store) CreateUserWithoutOtp(ctx context.Context, user domain.User) (domain.User, error) {
userRes, err := s.queries.CreateUser(ctx, dbgen.CreateUserParams{
FirstName: user.FirstName,
LastName: user.LastName,
NickName: pgtype.Text{String: user.NickName},
Email: pgtype.Text{String: user.Email, Valid: user.Email != ""},
PhoneNumber: pgtype.Text{String: user.PhoneNumber, Valid: user.PhoneNumber != ""},
Role: string(user.Role),
Password: user.Password,
Age: pgtype.Int4{Int32: int32(user.Age), Valid: user.Age > 0},
EducationLevel: pgtype.Text{String: user.EducationLevel, Valid: user.EducationLevel != ""},
Country: pgtype.Text{String: user.Country, Valid: user.Country != ""},
Region: pgtype.Text{String: user.Region, Valid: user.Region != ""},
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
Suspended: user.Suspended,
SuspendedAt: pgtype.Timestamptz{Time: user.SuspendedAt, Valid: !user.SuspendedAt.IsZero()},
OrganizationID: pgtype.Int8{Int64: user.OrganizationID.Value, Valid: user.OrganizationID.Valid},
CreatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
UpdatedAt: pgtype.Timestamptz{Time: time.Now(), Valid: true},
})
if err != nil {
return domain.User{}, err
}
return domain.User{
ID: userRes.ID,
FirstName: userRes.FirstName,
LastName: userRes.LastName,
NickName: userRes.NickName.String,
Email: userRes.Email.String,
PhoneNumber: userRes.PhoneNumber.String,
Role: domain.Role(userRes.Role),
Age: int(userRes.Age.Int32),
EducationLevel: userRes.EducationLevel.String,
Country: userRes.Country.String,
Region: userRes.Region.String,
EmailVerified: userRes.EmailVerified,
PhoneVerified: userRes.PhoneVerified,
Suspended: userRes.Suspended,
SuspendedAt: userRes.SuspendedAt.Time,
OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid},
CreatedAt: userRes.CreatedAt.Time,
UpdatedAt: userRes.UpdatedAt.Time,
}, nil
}
// CreateUser inserts a new user into the database
func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int64) (domain.User, error) {
// Optional: mark OTP as used
@ -117,23 +168,54 @@ func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error)
}
// GetAllUsers retrieves users with optional filters
func (s *Store) GetAllUsers(ctx context.Context, role *string, organizationID *int64, query *string, createdBefore, createdAfter *time.Time, limit, offset int32) ([]domain.User, error) {
rows, err := s.queries.GetAllUsers(ctx, dbgen.GetAllUsersParams{
Role: *role,
OrganizationID: pgtype.Int8{Int64: *organizationID},
Query: pgtype.Text{String: *query},
CreatedBefore: pgtype.Timestamptz{Time: *createdBefore},
CreatedAfter: pgtype.Timestamptz{Time: *createdAfter},
Limit: pgtype.Int4{Int32: limit},
Offset: pgtype.Int4{Int32: offset},
})
if err != nil {
return nil, err
func (s *Store) GetAllUsers(
ctx context.Context,
role *string,
organizationID *int64,
query *string,
createdBefore, createdAfter *time.Time,
limit, offset int32,
) ([]domain.User, int64, error) {
params := dbgen.GetAllUsersParams{
Limit: pgtype.Int4{Int32: limit, Valid: true},
Offset: pgtype.Int4{Int32: offset, Valid: true},
}
users := make([]domain.User, len(rows))
for i, u := range rows {
users[i] = domain.User{
if role != nil {
params.Role = *role
}
if organizationID != nil {
params.OrganizationID = pgtype.Int8{Int64: *organizationID, Valid: true}
}
if query != nil {
params.Query = pgtype.Text{String: *query, Valid: true}
}
if createdBefore != nil {
params.CreatedBefore = pgtype.Timestamptz{Time: *createdBefore, Valid: true}
}
if createdAfter != nil {
params.CreatedAfter = pgtype.Timestamptz{Time: *createdAfter, Valid: true}
}
rows, err := s.queries.GetAllUsers(ctx, params)
if err != nil {
return nil, 0, err
}
if len(rows) == 0 {
return []domain.User{}, 0, nil
}
totalCount := rows[0].TotalCount
users := make([]domain.User, 0, len(rows))
for _, u := range rows {
users = append(users, domain.User{
ID: u.ID,
FirstName: u.FirstName,
LastName: u.LastName,
@ -149,13 +231,16 @@ func (s *Store) GetAllUsers(ctx context.Context, role *string, organizationID *i
PhoneVerified: u.PhoneVerified,
Suspended: u.Suspended,
SuspendedAt: u.SuspendedAt.Time,
OrganizationID: domain.ValidInt64{Value: u.OrganizationID.Int64, Valid: u.OrganizationID.Valid},
OrganizationID: domain.ValidInt64{
Value: u.OrganizationID.Int64,
Valid: u.OrganizationID.Valid,
},
CreatedAt: u.CreatedAt.Time,
UpdatedAt: u.UpdatedAt.Time,
}
})
}
return users, nil
return users, totalCount, nil
}
// GetTotalUsers counts users with optional filters
@ -241,11 +326,11 @@ func (s *Store) DeleteUser(ctx context.Context, userID int64) error {
}
// CheckPhoneEmailExist checks if phone or email exists in an organization
func (s *Store) CheckPhoneEmailExist(ctx context.Context, phone, email string, organizationID int64) (phoneExists, emailExists bool, err error) {
func (s *Store) CheckPhoneEmailExist(ctx context.Context, phone, email string, organizationID domain.ValidInt64) (phoneExists, emailExists bool, err error) {
res, err := s.queries.CheckPhoneEmailExist(ctx, dbgen.CheckPhoneEmailExistParams{
PhoneNumber: pgtype.Text{String: phone},
Email: pgtype.Text{String: email},
OrganizationID: pgtype.Int8{Int64: organizationID},
OrganizationID: pgtype.Int8{Int64: organizationID.Value},
})
if err != nil {
return false, false, err
@ -255,64 +340,54 @@ func (s *Store) CheckPhoneEmailExist(ctx context.Context, phone, email string, o
}
// GetUserByEmail retrieves a user by email and organization
func (s *Store) GetUserByEmail(ctx context.Context, email string, organizationID int64) (domain.User, error) {
userRes, err := s.queries.GetUserByEmail(ctx, dbgen.GetUserByEmailParams{
Email: pgtype.Text{String: email},
OrganizationID: pgtype.Int8{Int64: organizationID},
})
if err != nil {
return domain.User{}, err
}
return domain.User{
ID: userRes.ID,
FirstName: userRes.FirstName,
LastName: userRes.LastName,
NickName: userRes.NickName.String,
Email: userRes.Email.String,
PhoneNumber: userRes.PhoneNumber.String,
Role: domain.Role(userRes.Role),
Age: int(userRes.Age.Int32),
EducationLevel: userRes.EducationLevel.String,
Country: userRes.Country.String,
Region: userRes.Region.String,
EmailVerified: userRes.EmailVerified,
PhoneVerified: userRes.PhoneVerified,
Suspended: userRes.Suspended,
SuspendedAt: userRes.SuspendedAt.Time,
OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid},
CreatedAt: userRes.CreatedAt.Time,
UpdatedAt: userRes.UpdatedAt.Time,
}, nil
}
func (s *Store) GetUserByEmailPhone(
ctx context.Context,
email string,
phone string,
organizationID domain.ValidInt64,
) (domain.User, error) {
// GetUserByPhone retrieves a user by phone and organization
func (s *Store) GetUserByPhone(ctx context.Context, phone string, organizationID int64) (domain.User, error) {
userRes, err := s.queries.GetUserByPhone(ctx, dbgen.GetUserByPhoneParams{
PhoneNumber: pgtype.Text{String: phone},
OrganizationID: pgtype.Int8{Int64: organizationID},
user, err := s.queries.GetUserByEmailPhone(ctx, dbgen.GetUserByEmailPhoneParams{
Email: pgtype.Text{
String: email,
Valid: email != "",
},
PhoneNumber: pgtype.Text{
String: phone,
Valid: phone != "",
},
OrganizationID: organizationID.ToPG(),
})
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return domain.User{}, authentication.ErrUserNotFound
}
return domain.User{}, err
}
return domain.User{
ID: userRes.ID,
FirstName: userRes.FirstName,
LastName: userRes.LastName,
NickName: userRes.NickName.String,
Email: userRes.Email.String,
PhoneNumber: userRes.PhoneNumber.String,
Role: domain.Role(userRes.Role),
Age: int(userRes.Age.Int32),
EducationLevel: userRes.EducationLevel.String,
Country: userRes.Country.String,
Region: userRes.Region.String,
EmailVerified: userRes.EmailVerified,
PhoneVerified: userRes.PhoneVerified,
Suspended: userRes.Suspended,
SuspendedAt: userRes.SuspendedAt.Time,
OrganizationID: domain.ValidInt64{Value: userRes.OrganizationID.Int64, Valid: userRes.OrganizationID.Valid},
CreatedAt: userRes.CreatedAt.Time,
UpdatedAt: userRes.UpdatedAt.Time,
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
NickName: user.NickName.String,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Password: user.Password,
Role: domain.Role(user.Role),
Age: int(user.Age.Int32),
EducationLevel: user.EducationLevel.String,
Country: user.Country.String,
Region: user.Region.String,
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
Suspended: user.Suspended,
SuspendedAt: user.SuspendedAt.Time,
OrganizationID: domain.ValidInt64{
Value: user.OrganizationID.Int64,
Valid: user.OrganizationID.Valid,
},
CreatedAt: user.CreatedAt.Time,
UpdatedAt: user.UpdatedAt.Time,
}, nil
}
@ -336,6 +411,21 @@ func (s *Store) GetOwnerByOrganizationID(ctx context.Context, organizationID int
return mapUser(userRes), nil
}
func (s *Store) UpdateUserSuspend(ctx context.Context, id int64, status bool) error {
err := s.queries.SuspendUser(ctx, dbgen.SuspendUserParams{
ID: id,
Suspended: status,
SuspendedAt: pgtype.Timestamptz{
Time: time.Now(),
Valid: true,
},
})
if err != nil {
return err
}
return nil
}
// mapUser converts dbgen.User to domain.User
func mapUser(u dbgen.User) domain.User {
return domain.User{

View File

@ -1,69 +0,0 @@
package currency
import (
"Yimaru-Backend/internal/domain"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
)
type FixerFetcher struct {
apiKey string
baseURL string
httpClient *http.Client
}
func NewFixerFetcher(apiKey string, baseURL string) *FixerFetcher {
return &FixerFetcher{
apiKey: apiKey,
baseURL: baseURL,
httpClient: &http.Client{Timeout: 10 * time.Second},
}
}
type fixerResponse struct {
Success bool `json:"success"`
Base string `json:"base"`
Date string `json:"date"`
Rates map[string]float64 `json:"rates"`
}
func (f *FixerFetcher) FetchLatestRates(ctx context.Context, baseCurrency domain.IntCurrency) (map[domain.IntCurrency]float64, error) {
url := fmt.Sprintf("%s/latest?base=%s", f.baseURL, baseCurrency)
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("apikey", f.apiKey)
resp, err := f.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to fetch rates: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
var result fixerResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
if !result.Success {
return nil, fmt.Errorf("api returned unsuccessful response")
}
rates := make(map[domain.IntCurrency]float64)
for currency, rate := range result.Rates {
rates[domain.IntCurrency(currency)] = rate
}
return rates, nil
}

View File

@ -1,125 +0,0 @@
package currency
import (
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/repository"
"context"
"fmt"
"time"
)
type Service struct {
repo repository.CurrencyRepository
baseCurrency domain.IntCurrency
fixerFetcher *FixerFetcher
}
func NewService(repo repository.CurrencyRepository, baseCurrency domain.IntCurrency, fixerFetcher *FixerFetcher) *Service {
return &Service{repo: repo}
}
func (s *Service) Convert(ctx context.Context, amount float64, from, to domain.IntCurrency) (float64, error) {
if from == to {
return amount, nil
}
rate, err := s.repo.GetExchangeRate(ctx, from, to)
if err != nil {
return 0, err
}
return rate.Convert(amount)
}
func (s *Service) GetSupportedCurrencies(ctx context.Context) ([]domain.IntCurrency, error) {
return s.repo.GetSupportedCurrencies(ctx)
}
func (s *Service) UpdateRates(ctx context.Context) error {
// Implement fetching from external API (e.g., Fixer, Open Exchange Rates)
rates := map[domain.IntCurrency]map[domain.IntCurrency]float64{
domain.ETB: {
domain.USD: 0.018,
domain.EUR: 0.016,
domain.GBP: 0.014,
},
// Add other currencies...
}
for from, toRates := range rates {
for to, rate := range toRates {
err := s.repo.StoreExchangeRate(ctx, domain.IntCurrencyRate{
From: from,
To: to,
Rate: rate,
ValidUntil: time.Now().Add(24 * time.Hour), // Refresh daily
})
if err != nil {
return err
}
}
}
return nil
}
func (s *Service) FetchAndStoreRates(ctx context.Context) error {
// s.logger.Info("Starting exchange rate update")
rates, err := s.fixerFetcher.FetchLatestRates(ctx, s.baseCurrency)
if err != nil {
// s.logger.Error("Failed to fetch rates", "error", err)
return fmt.Errorf("failed to fetch rates: %w", err)
}
// Convert to integer rates with precision
const precision = 6 // 1.000000
for currency, rate := range rates {
if currency == s.baseCurrency {
continue
}
intRate := domain.IntCurrencyRate{
From: s.baseCurrency,
To: currency,
Rate: rate * float64(pow10(precision)),
ValidUntil: time.Now().Add(24 * time.Hour), // Rates valid for 24 hours
}
if err := s.repo.StoreExchangeRate(ctx, intRate); err != nil {
// s.logger.Error("Failed to store rate",
// "from", s.baseCurrency,
// "to", currency,
// "error", err)
continue // Try to store other rates even if one fails
}
// Also store the inverse rate
inverseRate := domain.IntCurrencyRate{
From: currency,
To: s.baseCurrency,
Rate: (1 / rate) * float64(pow10(precision)),
ValidUntil: time.Now().Add(24 * time.Hour),
}
if err := s.repo.StoreExchangeRate(ctx, inverseRate); err != nil {
// s.logger.Error("Failed to store inverse rate",
// "from", currency,
// "to", s.baseCurrency,
// "error", err)
return fmt.Errorf("Error storing exchange rates")
}
}
// s.logger.Info("Exchange rates updated successfully")
return nil
}
func pow10(n int) int64 {
result := int64(1)
for i := 0; i < n; i++ {
result *= 10
}
return result
}

View File

@ -1,55 +0,0 @@
package currency
import (
"Yimaru-Backend/internal/config"
"context"
"fmt"
"log/slog"
"time"
"github.com/go-co-op/gocron"
)
type ExchangeRateWorker struct {
fetcherService *FixerFetcher
scheduler *gocron.Scheduler
logger *slog.Logger
cfg *config.Config
}
func NewExchangeRateWorker(
fetcherService *FixerFetcher, logger *slog.Logger, cfg *config.Config,
) *ExchangeRateWorker {
return &ExchangeRateWorker{
fetcherService: fetcherService,
scheduler: gocron.NewScheduler(time.UTC),
logger: logger,
cfg: cfg,
}
}
func (w *ExchangeRateWorker) Start(ctx context.Context) {
_, err := w.scheduler.Every(6).Hours().Do(w.RunUpdate)
if err != nil {
return
}
// Run immediately on startup
go w.RunUpdate()
w.scheduler.StartAsync()
}
func (w *ExchangeRateWorker) RunUpdate() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if _, err := w.fetcherService.FetchLatestRates(ctx, w.cfg.BASE_CURRENCY); err != nil {
fmt.Println("Exchange rate update failed", "error", err)
}
}
func (w *ExchangeRateWorker) Stop() {
w.scheduler.Stop()
w.logger.Info("Exchange rate worker stopped")
}

View File

@ -0,0 +1,92 @@
package issuereporting
import (
"context"
"errors"
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/repository"
)
type Service struct {
repo repository.ReportedIssueRepository
}
func New(repo repository.ReportedIssueRepository) *Service {
return &Service{repo: repo}
}
func (s *Service) CreateReportedIssue(ctx context.Context, issue domain.ReportedIssueReq, userID int64, role domain.Role) (domain.ReportedIssue, error) {
// metadata, err := json.Marshal(issue.Metadata)
// if err != nil {
// return domain.ReportedIssue{}, err
// }
params := dbgen.CreateReportedIssueParams{
UserID: userID,
UserRole: string(role),
Subject: issue.Subject,
Description: issue.Description,
IssueType: string(issue.IssueType),
// Metadata: metadata,
}
dbIssue, err := s.repo.CreateReportedIssue(ctx, params)
if err != nil {
return domain.ReportedIssue{}, err
}
// Map dbgen.ReportedIssue to domain.ReportedIssue
reportedIssue := domain.ReportedIssue{
ID: dbIssue.ID,
Subject: dbIssue.Subject,
Description: dbIssue.Description,
UserID: dbIssue.UserID,
UserRole: domain.Role(dbIssue.UserRole),
Status: domain.ReportedIssueStatus(dbIssue.Status),
IssueType: domain.ReportedIssueType(dbIssue.IssueType),
CreatedAt: dbIssue.CreatedAt.Time,
UpdatedAt: dbIssue.UpdatedAt.Time,
// Add other fields as necessary
}
return reportedIssue, nil
}
func (s *Service) GetIssuesForUser(ctx context.Context, userID int64, limit, offset int) ([]domain.ReportedIssue, error) {
dbIssues, err := s.repo.ListReportedIssuesByUser(ctx, userID, int32(limit), int32(offset))
if err != nil {
return nil, err
}
reportedIssues := make([]domain.ReportedIssue, len(dbIssues))
for i, dbIssue := range dbIssues {
reportedIssues[i] = domain.ReportedIssue{
ID: dbIssue.ID,
Subject: dbIssue.Subject,
Description: dbIssue.Description,
UserID: dbIssue.UserID,
UserRole: domain.Role(dbIssue.UserRole),
Status: domain.ReportedIssueStatus(dbIssue.Status),
IssueType: domain.ReportedIssueType(dbIssue.IssueType),
CreatedAt: dbIssue.CreatedAt.Time,
UpdatedAt: dbIssue.UpdatedAt.Time,
// Add other fields as necessary
}
}
return reportedIssues, nil
}
func (s *Service) GetAllIssues(ctx context.Context, limit, offset int) ([]dbgen.ReportedIssue, error) {
return s.repo.ListReportedIssues(ctx, int32(limit), int32(offset))
}
func (s *Service) UpdateIssueStatus(ctx context.Context, issueID int64, status string) error {
validStatuses := map[string]bool{"pending": true, "in_progress": true, "resolved": true, "rejected": true}
if !validStatuses[status] {
return errors.New("invalid status")
}
return s.repo.UpdateReportedIssueStatus(ctx, issueID, status)
}
func (s *Service) DeleteIssue(ctx context.Context, issueID int64) error {
return s.repo.DeleteReportedIssue(ctx, issueID)
}

View File

@ -8,7 +8,7 @@ import (
func (s *Service) SendEmail(ctx context.Context, receiverEmail, message string, messageHTML string, subject string) error {
apiKey := s.config.ResendApiKey
client := resend.NewClient(apiKey)
formattedSenderEmail := "FortuneBets <" + s.config.ResendSenderEmail + ">"
formattedSenderEmail := "Y <" + s.config.ResendSenderEmail + ">"
params := &resend.SendEmailRequest{
From: formattedSenderEmail,
To: []string{receiverEmail},

View File

@ -19,21 +19,21 @@ var (
func (s *Service) SendSMS(ctx context.Context, receiverPhone, message string, companyID domain.ValidInt64) error {
var settingsList domain.SettingList
var err error
// var err error
if companyID.Valid {
settingsList, err = s.settingSvc.GetOverrideSettingsList(ctx, companyID.Value)
if err != nil {
// TODO: Send a log about the error
return err
}
} else {
settingsList, err = s.settingSvc.GetGlobalSettingList(ctx)
if err != nil {
// TODO: Send a log about the error
return err
}
}
// if companyID.Valid {
// settingsList, err = s.settingSvc.GetOverrideSettingsList(ctx, companyID.Value)
// if err != nil {
// // TODO: Send a log about the error
// return err
// }
// } else {
// settingsList, err = s.settingSvc.GetGlobalSettingList(ctx)
// if err != nil {
// // TODO: Send a log about the error
// return err
// }
// }
switch settingsList.SMSProvider {
case domain.AfroMessage:

View File

@ -60,7 +60,7 @@ func New(
go hub.Run()
go svc.startWorker()
go svc.startRetryWorker()
// go svc.startRetryWorker()
// go svc.RunRedisSubscriber(context.Background())
// go svc.StartKafkaConsumer(context.Background())
@ -123,42 +123,42 @@ func (s *Service) SendNotification(ctx context.Context, notification *domain.Not
return nil
}
func (s *Service) MarkAsRead(ctx context.Context, notificationIDs []string, recipientID int64) error {
for _, notificationID := range notificationIDs {
_, err := s.store.UpdateNotificationStatus(ctx, notificationID, string(domain.DeliveryStatusSent), true, nil)
if err != nil {
s.mongoLogger.Error("[NotificationSvc.MarkAsRead] Failed to mark notification as read",
zap.String("notificationID", notificationID),
zap.Int64("recipientID", recipientID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return err
}
// func (s *Service) MarkAsRead(ctx context.Context, notificationIDs []string, recipientID int64) error {
// for _, notificationID := range notificationIDs {
// _, err := s.store.UpdateNotificationStatus(ctx, notificationID, string(domain.DeliveryStatusSent), true, nil)
// if err != nil {
// s.mongoLogger.Error("[NotificationSvc.MarkAsRead] Failed to mark notification as read",
// zap.String("notificationID", notificationID),
// zap.Int64("recipientID", recipientID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return err
// }
// count, err := s.store.CountUnreadNotifications(ctx, recipientID)
// if err != nil {
// s.logger.Error("[NotificationSvc.MarkAsRead] Failed to count unread notifications", "recipientID", recipientID, "error", err)
// return err
// }
// // count, err := s.store.CountUnreadNotifications(ctx, recipientID)
// // if err != nil {
// // s.logger.Error("[NotificationSvc.MarkAsRead] Failed to count unread notifications", "recipientID", recipientID, "error", err)
// // return err
// // }
// s.Hub.Broadcast <- map[string]interface{}{
// "type": "COUNT_NOT_OPENED_NOTIFICATION",
// "recipient_id": recipientID,
// "payload": map[string]int{
// "not_opened_notifications_count": int(count),
// },
// }
// // s.Hub.Broadcast <- map[string]interface{}{
// // "type": "COUNT_NOT_OPENED_NOTIFICATION",
// // "recipient_id": recipientID,
// // "payload": map[string]int{
// // "not_opened_notifications_count": int(count),
// // },
// // }
s.mongoLogger.Info("[NotificationSvc.MarkAsRead] Notification marked as read",
zap.String("notificationID", notificationID),
zap.Int64("recipientID", recipientID),
zap.Time("timestamp", time.Now()),
)
}
// s.mongoLogger.Info("[NotificationSvc.MarkAsRead] Notification marked as read",
// zap.String("notificationID", notificationID),
// zap.Int64("recipientID", recipientID),
// zap.Time("timestamp", time.Now()),
// )
// }
return nil
}
// return nil
// }
func (s *Service) GetUserNotifications(ctx context.Context, recipientID int64, limit, offset int) ([]domain.Notification, int64, error) {
notifications, total, err := s.store.GetUserNotifications(ctx, recipientID, limit, offset)
@ -277,9 +277,9 @@ func (s *Service) startWorker() {
}
}
func (s *Service) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) {
return s.store.ListRecipientIDs(ctx, receiver)
}
// func (s *Service) ListRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) {
// return s.store.ListRecipientIDs(ctx, receiver)
// }
func (s *Service) handleNotification(notification *domain.Notification) {
ctx := context.Background()
@ -310,13 +310,13 @@ func (s *Service) handleNotification(notification *domain.Notification) {
}
}
if _, err := s.store.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
s.mongoLogger.Error("[NotificationSvc.HandleNotification] Failed to update notification status",
zap.String("id", notification.ID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
}
// if _, err := s.store.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
// s.mongoLogger.Error("[NotificationSvc.HandleNotification] Failed to update notification status",
// zap.String("id", notification.ID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// }
}
func (s *Service) SendNotificationSMS(ctx context.Context, recipientID int64, message string) error {
@ -381,94 +381,94 @@ func (s *Service) SendNotificationEmail(ctx context.Context, recipientID int64,
return nil
}
func (s *Service) startRetryWorker() {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
// func (s *Service) startRetryWorker() {
// ticker := time.NewTicker(1 * time.Minute)
// defer ticker.Stop()
for {
select {
case <-ticker.C:
s.retryFailedNotifications()
case <-s.stopCh:
s.mongoLogger.Info("[NotificationSvc.StartRetryWorker] Retry worker stopped",
zap.Time("timestamp", time.Now()),
)
return
}
}
}
// for {
// select {
// case <-ticker.C:
// s.retryFailedNotifications()
// case <-s.stopCh:
// s.mongoLogger.Info("[NotificationSvc.StartRetryWorker] Retry worker stopped",
// zap.Time("timestamp", time.Now()),
// )
// return
// }
// }
// }
func (s *Service) retryFailedNotifications() {
ctx := context.Background()
failedNotifications, err := s.store.ListFailedNotifications(ctx, 100)
if err != nil {
s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to list failed notifications",
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return
}
// func (s *Service) retryFailedNotifications() {
// ctx := context.Background()
// failedNotifications, err := s.store.ListFailedNotifications(ctx, 100)
// if err != nil {
// s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to list failed notifications",
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return
// }
for _, n := range failedNotifications {
notification := &n
go func(notification *domain.Notification) {
for attempt := 0; attempt < 3; attempt++ {
time.Sleep(time.Duration(attempt) * time.Second)
switch notification.DeliveryChannel {
case domain.DeliveryChannelSMS:
if err := s.SendNotificationSMS(ctx, notification.RecipientID, notification.Payload.Message); err == nil {
notification.DeliveryStatus = domain.DeliveryStatusSent
if _, err := s.store.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to update after retry",
zap.String("id", notification.ID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
} else {
s.mongoLogger.Info("[NotificationSvc.RetryFailedNotifications] Successfully retried notification",
zap.String("id", notification.ID),
zap.Time("timestamp", time.Now()),
)
}
// for _, n := range failedNotifications {
// notification := &n
// go func(notification *domain.Notification) {
// for attempt := 0; attempt < 3; attempt++ {
// time.Sleep(time.Duration(attempt) * time.Second)
// switch notification.DeliveryChannel {
// case domain.DeliveryChannelSMS:
// if err := s.SendNotificationSMS(ctx, notification.RecipientID, notification.Payload.Message); err == nil {
// notification.DeliveryStatus = domain.DeliveryStatusSent
// if _, err := s.store.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
// s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to update after retry",
// zap.String("id", notification.ID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// } else {
// s.mongoLogger.Info("[NotificationSvc.RetryFailedNotifications] Successfully retried notification",
// zap.String("id", notification.ID),
// zap.Time("timestamp", time.Now()),
// )
// }
return
}
case domain.DeliveryChannelEmail:
if err := s.SendNotificationEmail(ctx, notification.RecipientID, notification.Payload.Message, notification.Payload.Headline); err == nil {
notification.DeliveryStatus = domain.DeliveryStatusSent
if _, err := s.store.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to update after retry",
zap.String("id", notification.ID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
} else {
s.mongoLogger.Info("[NotificationSvc.RetryFailedNotifications] Successfully retried notification",
zap.String("id", notification.ID),
zap.Time("timestamp", time.Now()),
)
}
// return
// }
// case domain.DeliveryChannelEmail:
// if err := s.SendNotificationEmail(ctx, notification.RecipientID, notification.Payload.Message, notification.Payload.Headline); err == nil {
// notification.DeliveryStatus = domain.DeliveryStatusSent
// if _, err := s.store.UpdateNotificationStatus(ctx, notification.ID, string(notification.DeliveryStatus), notification.IsRead, notification.Metadata); err != nil {
// s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Failed to update after retry",
// zap.String("id", notification.ID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// } else {
// s.mongoLogger.Info("[NotificationSvc.RetryFailedNotifications] Successfully retried notification",
// zap.String("id", notification.ID),
// zap.Time("timestamp", time.Now()),
// )
// }
return
}
}
}
s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Max retries reached for notification",
zap.String("id", notification.ID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
}(notification)
}
}
// return
// }
// }
// }
// s.mongoLogger.Error("[NotificationSvc.RetryFailedNotifications] Max retries reached for notification",
// zap.String("id", notification.ID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// }(notification)
// }
// }
func (s *Service) CountUnreadNotifications(ctx context.Context, recipient_id int64) (int64, error) {
return s.store.CountUnreadNotifications(ctx, recipient_id)
}
func (s *Service) DeleteOldNotifications(ctx context.Context) error {
return s.store.DeleteOldNotifications(ctx)
}
// func (s *Service) DeleteOldNotifications(ctx context.Context) error {
// return s.store.DeleteOldNotifications(ctx)
// }
// func (s *Service) GetNotificationCounts(ctx context.Context, filter domain.ReportFilter) (total, read, unread int64, err error){
// return s.store.Get(ctx, filter)

View File

@ -1,16 +0,0 @@
package referralservice
// import (
// "context"
// )
// type ReferralStore interface {
// GenerateReferralCode() (string, error)
// CreateReferral(ctx context.Context, userID int64, companyID int64) error
// ProcessReferral(ctx context.Context, referredPhone, referralCode string, companyID int64) error
// ProcessDepositBonus(ctx context.Context, userPhone string, amount float64) error
// ProcessBetReferral(ctx context.Context, userId int64, betAmount float64) error
// GetReferralStats(ctx context.Context, userID int64, companyID int64) (*domain.ReferralStats, error)
// GetReferralCountByID(ctx context.Context, referrerID int64) (int64, error)
// }

View File

@ -1,260 +0,0 @@
package referralservice
import (
"Yimaru-Backend/internal/config"
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/ports"
"Yimaru-Backend/internal/services/settings"
"context"
"crypto/rand"
"encoding/base32"
"errors"
"log/slog"
"go.uber.org/zap"
)
type Service struct {
repo ports.ReferralStore
settingSvc settings.Service
config *config.Config
logger *slog.Logger
mongoLogger *zap.Logger
}
func New(
repo ports.ReferralStore,
settingSvc settings.Service,
cfg *config.Config,
logger *slog.Logger,
mongoLogger *zap.Logger,
) *Service {
return &Service{
repo: repo,
settingSvc: settingSvc,
config: cfg,
logger: logger,
mongoLogger: mongoLogger,
}
}
var (
ErrInvalidReferral = errors.New("invalid or expired referral")
ErrUserNotFound = errors.New("user not found")
ErrNoReferralFound = errors.New("no referral found for this user")
ErrUserAlreadyHasReferralCode = errors.New("user already has an active referral code")
ErrMaxReferralCountLimitReached = errors.New("referral count limit has been reached")
)
func (s *Service) GenerateReferralCode() (string, error) {
b := make([]byte, 8)
if _, err := rand.Read(b); err != nil {
s.mongoLogger.Error("Failed to generate random bytes for referral code", zap.Error(err))
return "", err
}
code := base32.StdEncoding.EncodeToString(b)[:10]
s.mongoLogger.Debug("Generated referral code", zap.String("code", code))
return code, nil
}
func (s *Service) CreateReferralCode(ctx context.Context, userID int64, companyID int64) (domain.ReferralCode, error) {
settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID)
if err != nil {
s.mongoLogger.Error("Failed to fetch settings", zap.Error(err))
return domain.ReferralCode{}, err
}
// check if user already has an active referral code
referralCodes, err := s.repo.GetReferralCodesByUser(ctx, userID)
if err != nil {
s.mongoLogger.Error("Failed to check if user already has active referral code", zap.Int64("userID", userID), zap.Error(err))
return domain.ReferralCode{}, err
}
if len(referralCodes) != 0 {
s.mongoLogger.Error("user already has an active referral code", zap.Int64("userID", userID), zap.Any("codes", referralCodes), zap.Error(err))
return domain.ReferralCode{}, ErrUserAlreadyHasReferralCode
}
code, err := s.GenerateReferralCode()
if err != nil {
return domain.ReferralCode{}, err
}
newReferralCode, err := s.repo.CreateReferralCode(ctx, domain.CreateReferralCode{
ReferrerID: userID,
ReferralCode: code,
CompanyID: companyID,
NumberOfReferrals: settingsList.DefaultMaxReferrals,
RewardAmount: settingsList.ReferralRewardAmount,
})
if err != nil {
return domain.ReferralCode{}, err
}
return newReferralCode, nil
}
func (s *Service) ProcessReferral(ctx context.Context, referredID int64, referralCode string, companyID int64) error {
paramLogger := s.mongoLogger.With(
zap.Int64("referredID", referredID),
zap.String("referralCode", referralCode),
zap.Int64("companyID", companyID),
)
referral, err := s.repo.GetReferralCode(ctx, referralCode)
if err != nil {
paramLogger.Error("Failed to get referral by code", zap.Error(err))
return err
}
// wallets, err := s.walletSvc.GetCustomerWallet(ctx, referral.ReferrerID)
// if err != nil {
// paramLogger.Error("Failed to get referrer wallets", zap.Error(err))
// return err
// }
// _, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID,
// referral.RewardAmount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
// fmt.Sprintf("Added %v to static wallet due to %v referral code being used", referral.RewardAmount, referral.ReferralCode),
// )
// if err != nil {
// paramLogger.Error("Failed to add referral reward to static wallet", zap.Int64("static_wallet_id", wallets.StaticID), zap.Error(err))
// return err
// }
_, err = s.repo.CreateUserReferral(ctx, domain.CreateUserReferrals{
ReferredID: referredID,
ReferralCodeID: referral.ID,
})
if err != nil {
paramLogger.Error("Failed to create user referral", zap.Error(err))
return err
}
paramLogger.Info("Referral processed successfully", zap.String("rewardAmount", referral.ReferralCode))
return nil
}
func (s *Service) GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) {
paramLogger := s.mongoLogger.With(zap.Int64("userID", userID), zap.Int64("companyID", companyID))
stats, err := s.repo.GetReferralStats(ctx, userID, companyID)
if err != nil {
paramLogger.Error("Failed to get referral stats", zap.Error(err))
return domain.ReferralStats{}, err
}
return stats, nil
}
func (s *Service) GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) {
count, err := s.repo.GetUserReferralCount(ctx, referrerID)
if err != nil {
s.mongoLogger.Error("Failed to get referral count", zap.Int64("referrerID", referrerID), zap.Error(err))
return 0, err
}
return count, nil
}
func (s *Service) GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error) {
return s.repo.GetReferralCodesByUser(ctx, userID)
}
func (s *Service) GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error) {
return s.repo.GetReferralCode(ctx, code)
}
func (s *Service) UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error {
return s.repo.UpdateReferralCode(ctx, referral)
}
func (s *Service) GetUserReferral(ctx context.Context, referrerID int64) (domain.UserReferral, error) {
return s.repo.GetUserReferral(ctx, referrerID)
}
func (s *Service) GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) {
return s.repo.GetUserReferralsByCode(ctx, code)
}
// func (s *Service) ProcessDepositBonus(ctx context.Context, userID int64, amount float32, companyID int64) error {
// settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID)
// if err != nil {
// s.logger.Error("Failed to fetch settings", "error", err)
// return err
// }
// s.logger.Info("Processing deposit bonus", "amount", amount)
// customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, userID)
// if err != nil {
// s.logger.Error("Failed to get wallets for user", "userID", userID, "error", err)
// return err
// }
// bonus := amount * settingsList.CashbackPercentage
// _, err = s.walletSvc.AddToWallet(ctx, customerWallet.StaticID, domain.ToCurrency(bonus), domain.ValidInt64{},
// domain.TRANSFER_DIRECT, domain.PaymentDetails{},
// fmt.Sprintf("Added to bonus wallet because of Deposit Cashback Bonus %d", bonus))
// if err != nil {
// s.logger.Error("Failed to add deposit bonus to wallet", "staticWalletID", customerWallet.StaticID, "userID", userID, "bonus", bonus, "error", err)
// return err
// }
// s.logger.Info("Deposit bonus processed successfully", "bonus", bonus)
// return nil
// }
// func (s *Service) ProcessBetReferral(ctx context.Context, userId int64, betAmount float64) error {
// s.logger.Info("Processing bet referral", "userID", userId, "betAmount", betAmount)
// settings, err := s.repo.GetSettings(ctx)
// if err != nil {
// s.logger.Error("Failed to get referral settings", "error", err)
// return err
// }
// referral, err := s.repo.GetReferralByReferredID(ctx, userId)
// if err != nil {
// s.logger.Error("Failed to get referral by referred ID", "userId", userId, "error", err)
// return err
// }
// if referral == nil || referral.Status != domain.ReferralCompleted {
// s.logger.Warn("No valid referral found", "userId", userId, "status", referral.Status)
// return ErrNoReferralFound
// }
// wallets, err := s.walletSvc.GetWalletsByUser(ctx, referral.ReferrerID)
// if err != nil {
// s.logger.Error("Failed to get wallets for referrer", "referrerID", referral.ReferrerID, "error", err)
// return err
// }
// if len(wallets) == 0 {
// s.logger.Error("Referrer has no wallet", "referrerID", referral.ReferrerID)
// return errors.New("referrer has no wallet")
// }
// bonusPercentage := settings.BetReferralBonusPercentage
// if bonusPercentage == 0 {
// bonusPercentage = 5.0
// s.logger.Debug("Using default bet referral bonus percentage", "percentage", bonusPercentage)
// }
// bonus := betAmount * (bonusPercentage / 100)
// walletID := wallets[0].ID
// currentBalance := float64(wallets[0].Balance)
// _, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBalance+bonus)), domain.ValidInt64{},
// domain.TRANSFER_DIRECT, domain.PaymentDetails{},
// fmt.Sprintf("Added %v to static wallet because of bet referral", referral.RewardAmount))
// if err != nil {
// s.logger.Error("Failed to add bet referral bonus to wallet", "walletID", walletID, "referrerID", referral.ReferrerID, "bonus", bonus, "error", err)
// return err
// }
// s.logger.Info("Bet referral processed successfully", "referrer ID", referral.ReferrerID, "referrerID", referral.ReferrerID, "bonus", bonus)
// return nil
// }

View File

@ -35,29 +35,29 @@ func (s *Service) UpdateGlobalSettingList(ctx context.Context, settingList domai
return s.settingStore.UpdateGlobalSettingList(ctx, settingList)
}
func (s *Service) InsertCompanySetting(ctx context.Context, key, value string, companyID int64) error {
return s.settingStore.InsertCompanySetting(ctx, key, value, companyID)
}
func (s *Service) InsertCompanySettingList(ctx context.Context, settingList domain.ValidSettingList, companyID int64) error {
return s.settingStore.InsertCompanySettingList(ctx, settingList, companyID)
}
func (s *Service) GetAllCompanySettings(ctx context.Context) ([]domain.CompanySetting, error) {
return s.settingStore.GetAllCompanySettings(ctx)
}
func (s *Service) GetCompanySettingsByKey(ctx context.Context, key string) ([]domain.CompanySetting, error) {
return s.settingStore.GetCompanySettingsByKey(ctx, key)
}
func (s *Service) GetOverrideSettings(ctx context.Context, companyID int64) ([]domain.Setting, error) {
return s.settingStore.GetOverrideSettings(ctx, companyID)
}
func (s *Service) GetOverrideSettingsList(ctx context.Context, companyID int64) (domain.SettingList, error) {
return s.settingStore.GetOverrideSettingsList(ctx, companyID)
}
// func (s *Service) InsertCompanySetting(ctx context.Context, key, value string, companyID int64) error {
// return s.settingStore.InsertCompanySetting(ctx, key, value, companyID)
// }
// func (s *Service) InsertCompanySettingList(ctx context.Context, settingList domain.ValidSettingList, companyID int64) error {
// return s.settingStore.InsertCompanySettingList(ctx, settingList, companyID)
// }
// func (s *Service) GetAllCompanySettings(ctx context.Context) ([]domain.CompanySetting, error) {
// return s.settingStore.GetAllCompanySettings(ctx)
// }
// func (s *Service) GetCompanySettingsByKey(ctx context.Context, key string) ([]domain.CompanySetting, error) {
// return s.settingStore.GetCompanySettingsByKey(ctx, key)
// }
// func (s *Service) GetOverrideSettings(ctx context.Context, companyID int64) ([]domain.Setting, error) {
// return s.settingStore.GetOverrideSettings(ctx, companyID)
// }
// func (s *Service) GetOverrideSettingsList(ctx context.Context, companyID int64) (domain.SettingList, error) {
// return s.settingStore.GetOverrideSettingsList(ctx, companyID)
// }
func (s *Service) DeleteCompanySetting(ctx context.Context, companyID int64, key string) error {
return s.settingStore.DeleteCompanySetting(ctx, companyID, key)
}
// func (s *Service) DeleteCompanySetting(ctx context.Context, companyID int64, key string) error {
// return s.settingStore.DeleteCompanySetting(ctx, companyID, key)
// }
func (s *Service) DeleteAllCompanySetting(ctx context.Context, companyID int64) error {
return s.settingStore.DeleteAllCompanySetting(ctx, companyID)
}
// func (s *Service) DeleteAllCompanySetting(ctx context.Context, companyID int64) error {
// return s.settingStore.DeleteAllCompanySetting(ctx, companyID)
// }

View File

@ -134,39 +134,41 @@ func (s *Service) GetBranchByRole(ctx context.Context, branchID *int64, role dom
// var companyID int64
switch role {
case domain.RoleAdmin, domain.RoleBranchManager, domain.RoleSuperAdmin:
case domain.RoleAdmin, domain.RoleSuperAdmin:
if branchID == nil {
// h.logger.Error("CashoutReq Branch ID is required for this user role")
return nil, nil, ErrBranchRequiredForRole
}
branch, err := s.branchSvc.GetBranchByID(ctx, *branchID)
if err != nil {
// h.logger.Error("CashoutReq no branches")
return nil, nil, err
}
// branch, err := s.branchSvc.GetBranchByID(ctx, *branchID)
// if err != nil {
// // h.logger.Error("CashoutReq no branches")
// return nil, nil, err
// }
// Check if the user has access to the company
if role != domain.RoleSuperAdmin {
if userCompanyID.Valid && userCompanyID.Value != branch.CompanyID {
return nil, nil, ErrUnauthorizedCompanyID
}
// if userCompanyID.Valid && userCompanyID.Value != branch.CompanyID {
// return nil, nil, ErrUnauthorizedCompanyID
// }
}
if role == domain.RoleBranchManager {
if branch.BranchManagerID != userID {
return nil, nil, ErrUnauthorizedBranchManager
}
}
// if role == domain.RoleBranchManager {
// // if branch.BranchManagerID != userID {
// // return nil, nil, ErrUnauthorizedBranchManager
// // }
// }
return &branch.ID, &branch.CompanyID, nil
case domain.RoleCashier:
branch, err := s.branchSvc.GetBranchByCashier(ctx, userID)
if err != nil {
// h.logger.Error("CashoutReq failed, branch id invalid")
return nil, nil, ErrInvalidBranchID
}
return &branch.ID, &branch.CompanyID, nil
// return &branch.ID, &branch.CompanyID, nil
case domain.RoleInstructor:
// branch, err := s.branchSvc.GetBranchByCashier(ctx, userID)
// if err != nil {
// // h.logger.Error("CashoutReq failed, branch id invalid")
// return nil, nil, ErrInvalidBranchID
// }
// return &branch.ID, &branch.CompanyID, nil
default:
return nil, nil, ErrCustomerRoleNotAuthorized
}
return nil, nil, ErrCustomerRoleNotAuthorized
}

View File

@ -13,7 +13,7 @@ import (
func (s *Service) SendOtp(ctx context.Context, sentTo string, otpFor domain.OtpFor, medium domain.OtpMedium, provider domain.SMSProvider) error {
otpCode := helpers.GenerateOTP()
message := fmt.Sprintf("Welcome to Fortune bets, your OTP is %s please don't share with anyone.", otpCode)
message := fmt.Sprintf("Welcome to Yimaru Online Learning Platform, your OTP is %s please don't share with anyone.", otpCode)
switch medium {
case domain.OtpMediumSms:
@ -31,7 +31,7 @@ func (s *Service) SendOtp(ctx context.Context, sentTo string, otpFor domain.OtpF
return fmt.Errorf("invalid sms provider: %s", provider)
}
case domain.OtpMediumEmail:
if err := s.messengerSvc.SendEmail(ctx, sentTo, message, message, "FortuneBets - One Time Password"); err != nil {
if err := s.messengerSvc.SendEmail(ctx, sentTo, message, message, "Yimaru - One Time Password"); err != nil {
return err
}
}

View File

@ -34,8 +34,8 @@ func (s *Service) CreateUser(ctx context.Context, User domain.CreateUserReq, is_
EmailVerified: true,
PhoneVerified: true,
Suspended: User.Suspended,
CompanyID: User.CompanyID,
}, is_company)
OrganizationID: User.OrganizationID,
})
}
func (s *Service) DeleteUser(ctx context.Context, id int64) error {
@ -45,24 +45,9 @@ func (s *Service) DeleteUser(ctx context.Context, id int64) error {
func (s *Service) GetAllUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error) {
// Get all Users
return s.userStore.GetAllUsers(ctx, filter)
return s.userStore.GetAllUsers(ctx, &filter.Role, &filter.OrganizationID.Value, &filter.Query.Value, &filter.CreatedBefore.Value, &filter.CreatedAfter.Value, int32(filter.PageSize.Value), int32(filter.Page.Value))
}
func (s *Service) GetUserById(ctx context.Context, id int64) (domain.User, error) {
return s.userStore.GetUserByID(ctx, id)
}
func (s *Service) GetCashiersByBranch(ctx context.Context, branchID int64) ([]domain.User, error) {
return s.userStore.GetCashiersByBranch(ctx, branchID)
}
func (s *Service) GetAdminByCompanyID(ctx context.Context, companyID int64) (domain.User, error) {
return s.userStore.GetAdminByCompanyID(ctx, companyID)
}
func (s *Service) GetAllCashiers(ctx context.Context, filter domain.UserFilter) ([]domain.GetCashier, int64, error) {
return s.userStore.GetAllCashiers(ctx, filter)
}
func (s *Service) GetCashierByID(ctx context.Context, cashierID int64) (domain.GetCashier, error) {
return s.userStore.GetCashierByID(ctx, cashierID)
}

View File

@ -9,14 +9,15 @@ import (
func (s *Service) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string, companyID domain.ValidInt64) (bool, bool, error) { // email,phone,error
return s.userStore.CheckPhoneEmailExist(ctx, phoneNum, email, companyID)
}
func (s *Service) SendRegisterCode(ctx context.Context, medium domain.OtpMedium, sentTo string, provider domain.SMSProvider, companyID domain.ValidInt64) error {
var err error
// check if user exists
switch medium {
case domain.OtpMediumEmail:
_, err = s.userStore.GetUserByEmail(ctx, sentTo, companyID)
_, err = s.userStore.GetUserByEmailPhone(ctx, sentTo, "", companyID)
case domain.OtpMediumSms:
_, err = s.userStore.GetUserByPhone(ctx, sentTo, companyID)
_, err = s.userStore.GetUserByEmailPhone(ctx, "", sentTo, companyID)
}
if err != nil && err != domain.ErrUserNotFound {
@ -27,6 +28,7 @@ func (s *Service) SendRegisterCode(ctx context.Context, medium domain.OtpMedium,
// send otp based on the medium
return s.SendOtp(ctx, sentTo, domain.OtpRegister, medium, provider)
}
func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterUserReq) (domain.User, error) { // normal
// get otp
@ -64,13 +66,13 @@ func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterU
Email: registerReq.Email,
PhoneNumber: registerReq.PhoneNumber,
Password: hashedPassword,
Role: domain.RoleCustomer,
Role: domain.RoleStudent,
EmailVerified: registerReq.OtpMedium == domain.OtpMediumEmail,
PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms,
CompanyID: registerReq.CompanyID,
OrganizationID: registerReq.OrganizationID,
}
// create the user and mark otp as used
user, err := s.userStore.CreateUser(ctx, userR, otp.ID, false)
user, err := s.userStore.CreateUser(ctx, userR, otp.ID)
if err != nil {
return domain.User{}, err
}

View File

@ -13,9 +13,9 @@ func (s *Service) SendResetCode(ctx context.Context, medium domain.OtpMedium, se
// check if user exists
switch medium {
case domain.OtpMediumEmail:
_, err = s.userStore.GetUserByEmail(ctx, sentTo, companyID)
_, err = s.userStore.GetUserByEmailPhone(ctx, sentTo, "", companyID)
case domain.OtpMediumSms:
_, err = s.userStore.GetUserByPhone(ctx, sentTo, companyID)
_, err = s.userStore.GetUserByEmailPhone(ctx, "", sentTo, companyID)
}
if err != nil {
@ -51,13 +51,13 @@ func (s *Service) ResetPassword(ctx context.Context, resetReq domain.ResetPasswo
return domain.ErrInvalidOtp
}
// hash password
hashedPassword, err := hashPassword(resetReq.Password)
if err != nil {
return err
}
// hashedPassword, err := hashPassword(resetReq.Password)
// if err != nil {
// return err
// }
// reset pass and mark otp as used
err = s.userStore.UpdatePassword(ctx, sentTo, hashedPassword, otp.ID, resetReq.CompanyID)
err = s.userStore.UpdatePassword(ctx, resetReq.Password, resetReq.Email, resetReq.PhoneNumber, resetReq.OrganizationID, time.Now())
if err != nil {
return err
}

View File

@ -5,21 +5,34 @@ import (
"context"
)
func (s *Service) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) {
func (s *Service) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *int64, companyID *string) ([]domain.User, error) {
// Search user
return s.userStore.SearchUserByNameOrPhone(ctx, searchString, role, companyID)
}
func (s *Service) UpdateUser(ctx context.Context, user domain.UpdateUserReq) error {
// update user
return s.userStore.UpdateUser(ctx, user)
newUser := domain.User{
ID: user.UserID,
FirstName: user.FirstName.Value,
LastName: user.LastName.Value,
NickName: user.NickName.Value,
Age: user.Age.Value,
EducationLevel: user.EducationLevel.Value,
Country: user.Country.Value,
Region: user.Region.Value,
Suspended: user.Suspended.Value,
OrganizationID: user.OrganizationID,
}
return s.userStore.UpdateUser(ctx, newUser)
}
func (s *Service) UpdateUserCompany(ctx context.Context, id int64, companyID int64) error {
// update user
return s.userStore.UpdateUserCompany(ctx, id, companyID)
}
// func (s *Service) UpdateUserCompany(ctx context.Context, id int64, companyID int64) error {
// // update user
// return s.userStore.UpdateUserCompany(ctx, id, companyID)
// }
func (s *Service) UpdateUserSuspend(ctx context.Context, id int64, status bool) error {
// update user

View File

@ -4,9 +4,10 @@ import (
"Yimaru-Backend/internal/config"
"Yimaru-Backend/internal/services/arifpay"
"Yimaru-Backend/internal/services/authentication"
issuereporting "Yimaru-Backend/internal/services/issue_reporting"
notificationservice "Yimaru-Backend/internal/services/notification"
"Yimaru-Backend/internal/services/recommendation"
referralservice "Yimaru-Backend/internal/services/referal"
"Yimaru-Backend/internal/services/settings"
"Yimaru-Backend/internal/services/transaction"
"Yimaru-Backend/internal/services/user"
@ -23,60 +24,36 @@ import (
)
type App struct {
// directDepositSvc *directdeposit.Service
// telebirrSvc *telebirr.TelebirrService
arifpaySvc *arifpay.ArifpayService
// santimpaySvc *santimpay.SantimPayService
// issueReportingSvc *issuereporting.Service
// instSvc *institutions.Service
// currSvc *currency.Service
issueReportingSvc *issuereporting.Service
fiber *fiber.App
recommendationSvc recommendation.RecommendationService
cfg *config.Config
logger *slog.Logger
NotidicationStore *notificationservice.Service
referralSvc *referralservice.Service
// raffleSvc *raffle.Service
// bonusSvc *bonus.Service
port int
settingSvc *settings.Service
authSvc *authentication.Service
userSvc *user.Service
// chapaSvc *chapa.Service
transactionSvc *transaction.Service
// branchSvc *branch.Service
// companySvc *company.Service
validator *customvalidator.CustomValidator
JwtConfig jwtutil.JwtConfig
Logger *slog.Logger
// statSvc *stats.Service
mongoLoggerSvc *zap.Logger
}
func NewApp(
// directDepositSvc *directdeposit.Service,
// telebirrSvc *telebirr.TelebirrService,
arifpaySvc *arifpay.ArifpayService,
// santimpaySvc *santimpay.SantimPayService,
// issueReportingSvc *issuereporting.Service,
// instSvc *institutions.Service,
// currSvc *currency.Service,
issueReportingSvc *issuereporting.Service,
port int, validator *customvalidator.CustomValidator,
settingSvc *settings.Service,
authSvc *authentication.Service,
logger *slog.Logger,
JwtConfig jwtutil.JwtConfig,
userSvc *user.Service,
// chapaSvc *chapa.Service,
transactionSvc *transaction.Service,
// branchSvc *branch.Service,
// companySvc *company.Service,
notidicationStore *notificationservice.Service,
referralSvc *referralservice.Service,
// raffleSvc *raffle.Service,
// bonusSvc *bonus.Service,
recommendationSvc recommendation.RecommendationService,
// statSvc *stats.Service,
cfg *config.Config,
mongoLoggerSvc *zap.Logger,
) *App {
@ -97,13 +74,8 @@ func NewApp(
app.Static("/static", "./static")
s := &App{
// directDepositSvc: directDepositSvc,
// telebirrSvc: telebirrSvc,
arifpaySvc: arifpaySvc,
// santimpaySvc: santimpaySvc,
// issueReportingSvc: issueReportingSvc,
// instSvc: instSvc,
// currSvc: currSvc,
fiber: app,
port: port,
settingSvc: settingSvc,
@ -112,18 +84,10 @@ func NewApp(
logger: logger,
JwtConfig: JwtConfig,
userSvc: userSvc,
// ticketSvc: ticketSvc,
// chapaSvc: chapaSvc,
transactionSvc: transactionSvc,
// branchSvc: branchSvc,
// companySvc: companySvc,
NotidicationStore: notidicationStore,
referralSvc: referralSvc,
// raffleSvc: raffleSvc,
// bonusSvc: bonusSvc,
Logger: logger,
recommendationSvc: recommendationSvc,
// statSvc: statSvc,
cfg: cfg,
mongoLoggerSvc: mongoLoggerSvc,
}

View File

@ -18,7 +18,7 @@ type CreateAdminReq struct {
Email string `json:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
Password string `json:"password" example:"password123"`
CompanyID *int64 `json:"company_id,omitempty" example:"1"`
OrganizationID *int64 `json:"company_id,omitempty" example:"1"`
}
// CreateAdmin godoc
@ -34,7 +34,7 @@ type CreateAdminReq struct {
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/admin [post]
func (h *Handler) CreateAdmin(c *fiber.Ctx) error {
var companyID domain.ValidInt64
var OrganizationID domain.ValidInt64
var req CreateAdminReq
if err := c.BodyParser(&req); err != nil {
@ -60,24 +60,24 @@ func (h *Handler) CreateAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
if req.CompanyID == nil {
companyID = domain.ValidInt64{
if req.OrganizationID == nil {
OrganizationID = domain.ValidInt64{
Value: 0,
Valid: false,
}
} else {
// _, err := h.companySvc.GetCompanyByID(c.Context(), *req.CompanyID)
// _, err := h.companySvc.GetCompanyByID(c.Context(), *req.OrganizationID)
// if err != nil {
// h.mongoLoggerSvc.Error("invalid company ID for CreateAdmin",
// zap.Int64("status_code", fiber.StatusInternalServerError),
// zap.Int64("company_id", *req.CompanyID),
// zap.Int64("company_id", *req.OrganizationID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Company ID is invalid:"+err.Error())
// }
companyID = domain.ValidInt64{
Value: *req.CompanyID,
OrganizationID = domain.ValidInt64{
Value: *req.OrganizationID,
Valid: true,
}
}
@ -89,7 +89,7 @@ func (h *Handler) CreateAdmin(c *fiber.Ctx) error {
PhoneNumber: req.PhoneNumber,
Password: req.Password,
Role: string(domain.RoleAdmin),
CompanyID: companyID,
OrganizationID: OrganizationID,
}
newUser, err := h.userSvc.CreateUser(c.Context(), user, true)
@ -103,9 +103,9 @@ func (h *Handler) CreateAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create admin:"+err.Error())
}
// if req.CompanyID != nil {
// if req.OrganizationID != nil {
// _, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
// ID: *req.CompanyID,
// ID: *req.OrganizationID,
// AdminID: domain.ValidInt64{
// Value: newUser.ID,
// Valid: true,
@ -114,7 +114,7 @@ func (h *Handler) CreateAdmin(c *fiber.Ctx) error {
// if err != nil {
// h.mongoLoggerSvc.Error("failed to update company with new admin",
// zap.Int64("status_code", fiber.StatusInternalServerError),
// zap.Int64("company_id", *req.CompanyID),
// zap.Int64("company_id", *req.OrganizationID),
// zap.Int64("admin_id", newUser.ID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
@ -200,7 +200,7 @@ func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {
companyFilter := int64(c.QueryInt("company_id"))
filter := domain.UserFilter{
Role: string(domain.RoleAdmin),
CompanyID: domain.ValidInt64{
OrganizationID: domain.ValidInt64{
Value: companyFilter,
Valid: companyFilter != 0,
},
@ -365,7 +365,7 @@ type updateAdminReq struct {
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Doe"`
Suspended bool `json:"suspended" example:"false"`
CompanyID *int64 `json:"company_id,omitempty" example:"1"`
OrganizationID *int64 `json:"company_id,omitempty" example:"1"`
}
// UpdateAdmin godoc
@ -417,16 +417,16 @@ func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Invalid Admin ID")
}
var companyID domain.ValidInt64
if req.CompanyID != nil {
companyID = domain.ValidInt64{
Value: *req.CompanyID,
var OrganizationID domain.ValidInt64
if req.OrganizationID != nil {
OrganizationID = domain.ValidInt64{
Value: *req.OrganizationID,
Valid: true,
}
}
err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
UserId: AdminID,
UserID: AdminID,
FirstName: domain.ValidString{
Value: req.FirstName,
Valid: req.FirstName != "",
@ -439,7 +439,7 @@ func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
Value: req.Suspended,
Valid: true,
},
CompanyID: companyID,
OrganizationID: OrganizationID,
})
if err != nil {
h.mongoLoggerSvc.Error("UpdateAdmin failed - user service error",
@ -451,9 +451,9 @@ func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update admin:"+err.Error())
}
// if req.CompanyID != nil {
// if req.OrganizationID != nil {
// _, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
// ID: *req.CompanyID,
// ID: *req.OrganizationID,
// AdminID: domain.ValidInt64{
// Value: AdminID,
// Valid: true,

View File

@ -40,8 +40,8 @@ type loginCustomerRes struct {
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/customer-login [post]
func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
OrganizationID := c.Locals("company_id").(domain.ValidInt64)
if !OrganizationID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
@ -63,7 +63,7 @@ func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password, companyID)
successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password, OrganizationID)
if err != nil {
switch {
@ -95,7 +95,7 @@ func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
}
}
if successRes.Role != domain.RoleCustomer {
if successRes.Role != domain.RoleStudent {
h.mongoLoggerSvc.Info("Login attempt: customer login of other role",
zap.Int("status_code", fiber.StatusForbidden),
zap.String("role", string(successRes.Role)),
@ -167,8 +167,8 @@ type LoginAdminRes struct {
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/admin-login [post]
func (h *Handler) LoginAdmin(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
OrganizationID := c.Locals("company_id").(domain.ValidInt64)
if !OrganizationID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
@ -190,7 +190,7 @@ func (h *Handler) LoginAdmin(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password, companyID)
successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password, OrganizationID)
if err != nil {
switch {
case errors.Is(err, authentication.ErrInvalidPassword), errors.Is(err, authentication.ErrUserNotFound):
@ -221,7 +221,7 @@ func (h *Handler) LoginAdmin(c *fiber.Ctx) error {
}
}
if successRes.Role == domain.RoleCustomer {
if successRes.Role == domain.RoleStudent || successRes.Role == domain.RoleInstructor {
h.mongoLoggerSvc.Warn("Login attempt: admin login of customer",
zap.Int("status_code", fiber.StatusForbidden),
zap.String("role", string(successRes.Role)),
@ -451,7 +451,7 @@ func (h *Handler) RefreshToken(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user information:"+err.Error())
}
accessToken, err := jwtutil.CreateJwt(user.ID, user.Role, user.CompanyID, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry)
accessToken, err := jwtutil.CreateJwt(user.ID, user.Role, user.OrganizationID, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry)
if err != nil {
h.mongoLoggerSvc.Error("Failed to create new access token",
zap.Int("status_code", fiber.StatusInternalServerError),

View File

@ -1,456 +0,0 @@
package handlers
import (
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/services/authentication"
"Yimaru-Backend/internal/web_server/response"
"fmt"
"strconv"
"time"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
type CreateCashierReq struct {
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Doe"`
Email string `json:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
Password string `json:"password" example:"password123"`
BranchID int64 `json:"branch_id" example:"1"`
Suspended bool `json:"suspended" example:"false"`
}
// CreateCashier godoc
// @Summary Create cashier
// @Description Create cashier
// @Tags cashier
// @Accept json
// @Produce json
// @Param cashier body CreateCashierReq true "Create cashier"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/cashiers [post]
func (h *Handler) CreateCashier(c *fiber.Ctx) error {
// Get user_id from middleware
// companyID := c.Locals("company_id").(domain.ValidInt64)
var req CreateCashierReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("failed to parse CreateCashier request body",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate CreateCashier",
zap.Any("request", req),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
// Cashiers inherit the company id from the branch id
// TODO add a check here to make sure that the admin/manager if from same company
// branch, err := h.branchSvc.GetBranchByID(c.Context(), req.BranchID)
// if err != nil {
// return fiber.NewError(fiber.StatusBadRequest, "Branch ID is invalid")
// }
// userRequest := domain.CreateUserReq{
// FirstName: req.FirstName,
// LastName: req.LastName,
// Email: req.Email,
// PhoneNumber: req.PhoneNumber,
// Password: req.Password,
// Role: string(domain.RoleCashier),
// Suspended: req.Suspended,
// CompanyID: domain.ValidInt64{
// Value: 0,
// Valid: true,
// },
// }
// fmt.Print(req.Suspended)
// newUser, err := h.userSvc.CreateUser(c.Context(), userRequest, true)
// if err != nil {
// h.mongoLoggerSvc.Error("Failed to create cashier user",
// zap.Any("userRequest", userRequest),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to create cashier user:"+err.Error())
// }
// err = h.branchSvc.CreateBranchCashier(c.Context(), req.BranchID, newUser.ID)
// if err != nil {
// h.mongoLoggerSvc.Error("failed to create branch cashier",
// zap.Int64("branchID", req.BranchID),
// zap.Int64("userID", newUser.ID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to create branch cashier:"+err.Error())
// }
return response.WriteJSON(c, fiber.StatusOK, "Cashier created successfully", nil, nil)
}
type GetCashierRes struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
PhoneNumber string `json:"phone_number"`
Role domain.Role `json:"role"`
EmailVerified bool `json:"email_verified"`
PhoneVerified bool `json:"phone_verified"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
SuspendedAt time.Time `json:"suspended_at"`
Suspended bool `json:"suspended"`
LastLogin time.Time `json:"last_login"`
BranchID int64 `json:"branch_id"`
BranchName string `json:"branch_name"`
BranchWallet int64 `json:"branch_wallet"`
BranchLocation string `json:"branch_location"`
}
// GetAllCashiers godoc
// @Summary Get all cashiers
// @Description Get all cashiers
// @Tags cashier
// @Accept json
// @Produce json
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Success 200 {array} GetCashierRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/cashiers [get]
func (h *Handler) GetAllCashiers(c *fiber.Ctx) error {
role := c.Locals("role").(domain.Role)
companyId := c.Locals("company_id").(domain.ValidInt64)
if role != domain.RoleSuperAdmin && !companyId.Valid {
h.mongoLoggerSvc.Error("Cannot get company ID in context",
zap.String("role", string(role)),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID in context")
}
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
createdBeforeQuery := c.Query("created_before")
var createdBefore domain.ValidTime
if createdBeforeQuery != "" {
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
if err != nil {
h.mongoLoggerSvc.Info("invalid created_before format",
zap.String("createdBefore", createdBeforeQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format")
}
createdBefore = domain.ValidTime{
Value: createdBeforeParsed,
Valid: true,
}
}
createdAfterQuery := c.Query("created_after")
var createdAfter domain.ValidTime
if createdAfterQuery != "" {
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
if err != nil {
h.mongoLoggerSvc.Info("invalid created_after format",
zap.String("created_after", createdAfterQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format")
}
createdAfter = domain.ValidTime{
Value: createdAfterParsed,
Valid: true,
}
}
filter := domain.UserFilter{
Role: string(domain.RoleCashier),
CompanyID: companyId,
Page: domain.ValidInt{
Value: c.QueryInt("page", 1) - 1,
Valid: true,
},
PageSize: domain.ValidInt{
Value: c.QueryInt("page_size", 10),
Valid: true,
},
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
}
valErrs, ok := h.validator.Validate(c, filter)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate filters for GetAllCashier",
zap.Any("filter", filter),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
cashiers, total, err := h.userSvc.GetAllCashiers(c.Context(), filter)
if err != nil {
h.mongoLoggerSvc.Error("failed to get all cashiers",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get cashiers:"+err.Error())
}
var result []GetCashierRes = make([]GetCashierRes, 0, len(cashiers))
for _, cashier := range cashiers {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), cashier.ID)
if err != nil {
if err == authentication.ErrRefreshTokenNotFound {
lastLogin = &cashier.CreatedAt
} else {
h.mongoLoggerSvc.Error("Failed to get user last login",
zap.Int64("userID", cashier.ID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login:"+err.Error())
}
}
result = append(result, GetCashierRes{
ID: cashier.ID,
FirstName: cashier.FirstName,
LastName: cashier.LastName,
Email: cashier.Email,
PhoneNumber: cashier.PhoneNumber,
Role: cashier.Role,
EmailVerified: cashier.EmailVerified,
PhoneVerified: cashier.PhoneVerified,
CreatedAt: cashier.CreatedAt,
UpdatedAt: cashier.UpdatedAt,
SuspendedAt: cashier.SuspendedAt,
Suspended: cashier.Suspended,
LastLogin: *lastLogin,
BranchID: cashier.BranchID,
BranchName: cashier.BranchName,
BranchWallet: cashier.BranchWallet,
BranchLocation: cashier.BranchLocation,
})
}
return response.WritePaginatedJSON(c, fiber.StatusOK, "Cashiers retrieved successfully", result, nil, filter.Page.Value, int(total))
}
// GetCashierByID godoc
// @Summary Get cashier by id
// @Description Get a single cashier by id
// @Tags cashier
// @Accept json
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {object} UserProfileRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/cashier/{id} [get]
func (h *Handler) GetCashierByID(c *fiber.Ctx) error {
// branchId := int64(12) //c.Locals("branch_id").(int64)
// filter := user.Filter{
// Role: string(domain.RoleUser),
// BranchId: user.ValidBranchId{
// Value: branchId,
// Valid: true,
// },
// Page: c.QueryInt("page", 1),
// PageSize: c.QueryInt("page_size", 10),
// }
// valErrs, ok := validator.Validate(c, filter)
// if !ok {
// return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
// }
stringID := c.Params("id")
cashierID, err := strconv.ParseInt(stringID, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("failed to parse user_id",
zap.String("stringID", stringID),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid cashier ID")
}
user, err := h.userSvc.GetCashierByID(c.Context(), cashierID)
if err != nil {
h.mongoLoggerSvc.Error("Get User By ID failed",
zap.Int64("cashierID", cashierID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get cashiers:"+err.Error())
}
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
if err != nil {
if err != authentication.ErrRefreshTokenNotFound {
h.mongoLoggerSvc.Error("Failed to get user last login",
zap.Int64("userID", user.ID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login:"+err.Error())
}
lastLogin = &user.CreatedAt
}
res := GetCashierRes{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email,
PhoneNumber: user.PhoneNumber,
Role: user.Role,
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
SuspendedAt: user.SuspendedAt,
Suspended: user.Suspended,
LastLogin: *lastLogin,
BranchID: user.BranchID,
BranchName: user.BranchName,
BranchWallet: user.BranchWallet,
BranchLocation: user.BranchLocation,
}
return response.WriteJSON(c, fiber.StatusOK, "User retrieved successfully", res, nil)
}
type updateCashierReq struct {
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Doe"`
Suspended bool `json:"suspended" example:"false"`
}
// UpdateCashier godoc
// @Summary Update cashier
// @Description Update cashier
// @Tags cashier
// @Accept json
// @Produce json
// @Param id path int true "Cashier ID"
// @Param cashier body updateCashierReq true "Update cashier"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/cashiers/{id} [put]
func (h *Handler) UpdateCashier(c *fiber.Ctx) error {
cashierIdStr := c.Params("id")
cashierId, err := strconv.ParseInt(cashierIdStr, 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("UpdateCashier invalid cashier ID",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid cashier ID")
}
var req updateCashierReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("UpdateCashier failed to parse request body",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate update cashier request",
zap.Any("request", req),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
UserId: cashierId,
FirstName: domain.ValidString{
Value: req.FirstName,
Valid: req.FirstName != "",
},
LastName: domain.ValidString{
Value: req.LastName,
Valid: req.LastName != "",
},
Suspended: domain.ValidBool{
Value: req.Suspended,
Valid: true,
},
},
)
if err != nil {
h.mongoLoggerSvc.Error("failed to update cashier",
zap.Int64("userID", cashierId),
zap.Any("request", req),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update cashier"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Cashier updated successfully", nil, nil)
}

View File

@ -1,30 +1,24 @@
package handlers
import (
"Yimaru-Backend/internal/domain"
"github.com/gofiber/fiber/v2"
)
// @Summary Get supported currencies
// @Description Returns list of supported currencies
// @Tags Multi-Currency
// @Produce json
// @Success 200 {object} domain.Response{data=[]domain.Currency}
// @Router /api/v1/currencies [get]
func (h *Handler) GetSupportedCurrencies(c *fiber.Ctx) error {
currencies, err := h.currSvc.GetSupportedCurrencies(c.Context())
if err != nil {
return domain.UnExpectedErrorResponse(c)
}
// func (h *Handler) GetSupportedCurrencies(c *fiber.Ctx) error {
// currencies, err := h.currSvc.GetSupportedCurrencies(c.Context())
// if err != nil {
// return domain.UnExpectedErrorResponse(c)
// }
return c.Status(fiber.StatusOK).JSON(domain.Response{
Success: true,
Message: "Supported currencies retrieved successfully",
Data: currencies,
StatusCode: fiber.StatusOK,
})
}
// return c.Status(fiber.StatusOK).JSON(domain.Response{
// Success: true,
// Message: "Supported currencies retrieved successfully",
// Data: currencies,
// StatusCode: fiber.StatusOK,
// })
// }
// @Summary Convert currency
// @Description Converts amount from one currency to another
@ -36,23 +30,23 @@ func (h *Handler) GetSupportedCurrencies(c *fiber.Ctx) error {
// @Success 200 {object} domain.Response{data=float64}
// @Failure 400 {object} domain.ErrorResponse
// @Router /api/v1/currencies/convert [get]
func (h *Handler) ConvertCurrency(c *fiber.Ctx) error {
from := domain.IntCurrency(c.Query("from"))
to := domain.IntCurrency(c.Query("to"))
amount := c.QueryFloat("amount", 0)
// if err != nil {
// return domain.BadRequestResponse(c)
// }
// func (h *Handler) ConvertCurrency(c *fiber.Ctx) error {
// from := domain.IntCurrency(c.Query("from"))
// to := domain.IntCurrency(c.Query("to"))
// amount := c.QueryFloat("amount", 0)
// // if err != nil {
// // return domain.BadRequestResponse(c)
// // }
converted, err := h.currSvc.Convert(c.Context(), amount, from, to)
if err != nil {
return domain.UnExpectedErrorResponse(c)
}
// converted, err := h.currSvc.Convert(c.Context(), amount, from, to)
// if err != nil {
// return domain.UnExpectedErrorResponse(c)
// }
return c.Status(fiber.StatusOK).JSON(domain.Response{
Success: true,
Message: "Currency converted successfully",
Data: converted,
StatusCode: fiber.StatusOK,
})
}
// return c.Status(fiber.StatusOK).JSON(domain.Response{
// Success: true,
// Message: "Currency converted successfully",
// Data: converted,
// StatusCode: fiber.StatusOK,
// })
// }

View File

@ -4,10 +4,9 @@ import (
"Yimaru-Backend/internal/config"
"Yimaru-Backend/internal/services/arifpay"
"Yimaru-Backend/internal/services/authentication"
"Yimaru-Backend/internal/services/currency"
notificationservice "Yimaru-Backend/internal/services/notification"
"Yimaru-Backend/internal/services/recommendation"
referralservice "Yimaru-Backend/internal/services/referal"
// referralservice "Yimaru-Backend/internal/services/referal"
"Yimaru-Backend/internal/services/settings"
@ -22,13 +21,10 @@ import (
type Handler struct {
arifpaySvc *arifpay.ArifpayService
// instSvc *institutions.Service
currSvc *currency.Service
logger *slog.Logger
settingSvc *settings.Service
notificationSvc *notificationservice.Service
userSvc *user.Service
referralSvc *referralservice.Service
transactionSvc *transaction.Service
recommendationSvc recommendation.RecommendationService
authSvc *authentication.Service
@ -39,23 +35,14 @@ type Handler struct {
}
func New(
// directDepositSvc *directdeposit.Service,
// telebirrSvc *telebirr.TelebirrService,
arifpaySvc *arifpay.ArifpayService,
// santimpaySvc *santimpay.SantimPayService,
// instSvc *institutions.Service,
currSvc *currency.Service,
logger *slog.Logger,
settingSvc *settings.Service,
notificationSvc *notificationservice.Service,
validator *customvalidator.CustomValidator,
// reportSvc report.ReportService,
// chapaSvc *chapa.Service,
referralSvc *referralservice.Service,
recommendationSvc recommendation.RecommendationService,
userSvc *user.Service,
transactionSvc *transaction.Service,
// ticketSvc *ticket.Service,
authSvc *authentication.Service,
jwtConfig jwtutil.JwtConfig,
cfg *config.Config,
@ -63,12 +50,9 @@ func New(
) *Handler {
return &Handler{
arifpaySvc: arifpaySvc,
instSvc: instSvc,
currSvc: currSvc,
logger: logger,
settingSvc: settingSvc,
notificationSvc: notificationSvc,
referralSvc: referralSvc,
validator: validator,
userSvc: userSvc,
transactionSvc: transactionSvc,

View File

@ -1,13 +1,5 @@
package handlers
import (
"Yimaru-Backend/internal/domain"
"strconv"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
// @Summary Create a new bank
// @Tags Institutions - Banks
// @Accept json
@ -16,18 +8,18 @@ import (
// @Success 201 {object} domain.Bank
// @Failure 400 {object} domain.ErrorResponse
// @Router /api/v1/banks [post]
func (h *Handler) CreateBank(c *fiber.Ctx) error {
var bank domain.Bank
if err := c.BodyParser(&bank); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
}
// func (h *Handler) CreateBank(c *fiber.Ctx) error {
// var bank domain.Bank
// if err := c.BodyParser(&bank); err != nil {
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
// }
err := h.instSvc.Create(c.Context(), &bank)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.Status(fiber.StatusCreated).JSON(bank)
}
// // err := h.instSvc.Create(c.Context(), &bank)
// // if err != nil {
// // return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
// // }
// return c.Status(fiber.StatusCreated).JSON(bank)
// }
// @Summary Get a bank by ID
// @Tags Institutions - Banks
@ -38,19 +30,19 @@ func (h *Handler) CreateBank(c *fiber.Ctx) error {
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/banks/{id} [get]
func (h *Handler) GetBankByID(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid bank ID"})
}
// func (h *Handler) GetBankByID(c *fiber.Ctx) error {
// id, err := c.ParamsInt("id")
// if err != nil || id <= 0 {
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid bank ID"})
// }
bank, err := h.instSvc.GetByID(c.Context(), int64(id))
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "bank not found"})
}
// // bank, err := h.instSvc.GetByID(c.Context(), int64(id))
// // if err != nil {
// // return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "bank not found"})
// // }
return c.JSON(bank)
}
// return c.JSON(bank)
// }
// @Summary Update a bank
// @Tags Institutions - Banks
@ -63,42 +55,42 @@ func (h *Handler) GetBankByID(c *fiber.Ctx) error {
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/banks/{id} [put]
func (h *Handler) UpdateBank(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid bank ID"})
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Failed to update bank",
Error: err.Error(),
})
}
// func (h *Handler) UpdateBank(c *fiber.Ctx) error {
// id, err := c.ParamsInt("id")
// if err != nil || id <= 0 {
// // return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid bank ID"})
// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
// Message: "Failed to update bank",
// Error: err.Error(),
// })
// }
var bank domain.Bank
if err := c.BodyParser(&bank); err != nil {
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Failed to update bank",
Error: err.Error(),
})
}
bank.ID = id
// var bank domain.Bank
// if err := c.BodyParser(&bank); err != nil {
// // return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
// return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
// Message: "Failed to update bank",
// Error: err.Error(),
// })
// }
// bank.ID = id
err = h.instSvc.Update(c.Context(), &bank)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to update bank",
Error: err.Error(),
})
}
// err = h.instSvc.Update(c.Context(), &bank)
// if err != nil {
// return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
// Message: "Failed to update bank",
// Error: err.Error(),
// })
// }
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Bank updated successfully",
StatusCode: fiber.StatusOK,
Success: true,
Data: bank,
})
// return c.JSON(bank)
}
// return c.Status(fiber.StatusOK).JSON(domain.Response{
// Message: "Bank updated successfully",
// StatusCode: fiber.StatusOK,
// Success: true,
// Data: bank,
// })
// // return c.JSON(bank)
// }
// @Summary Delete a bank
// @Tags Institutions - Banks
@ -109,19 +101,19 @@ func (h *Handler) UpdateBank(c *fiber.Ctx) error {
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/banks/{id} [delete]
func (h *Handler) DeleteBank(c *fiber.Ctx) error {
id, err := c.ParamsInt("id")
if err != nil || id <= 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid bank ID"})
}
// func (h *Handler) DeleteBank(c *fiber.Ctx) error {
// id, err := c.ParamsInt("id")
// if err != nil || id <= 0 {
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid bank ID"})
// }
err = h.instSvc.Delete(c.Context(), int64(id))
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
// err = h.instSvc.Delete(c.Context(), int64(id))
// if err != nil {
// return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
// }
return c.SendStatus(fiber.StatusNoContent)
}
// return c.SendStatus(fiber.StatusNoContent)
// }
// @Summary List all banks with pagination and filtering
// @Tags Institutions - Banks
@ -135,51 +127,51 @@ func (h *Handler) DeleteBank(c *fiber.Ctx) error {
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/banks [get]
func (h *Handler) ListBanks(c *fiber.Ctx) error {
// Parse query parameters
countryID, _ := strconv.Atoi(c.Query("country_id"))
var countryIDPtr *int
if c.Query("country_id") != "" {
countryIDPtr = &countryID
}
// func (h *Handler) ListBanks(c *fiber.Ctx) error {
// // Parse query parameters
// countryID, _ := strconv.Atoi(c.Query("country_id"))
// var countryIDPtr *int
// if c.Query("country_id") != "" {
// countryIDPtr = &countryID
// }
isActive, _ := strconv.ParseBool(c.Query("is_active"))
var isActivePtr *bool
if c.Query("is_active") != "" {
isActivePtr = &isActive
}
// isActive, _ := strconv.ParseBool(c.Query("is_active"))
// var isActivePtr *bool
// if c.Query("is_active") != "" {
// isActivePtr = &isActive
// }
var searchTermPtr *string
if searchTerm := c.Query("search"); searchTerm != "" {
searchTermPtr = &searchTerm
}
// var searchTermPtr *string
// if searchTerm := c.Query("search"); searchTerm != "" {
// searchTermPtr = &searchTerm
// }
page, _ := strconv.Atoi(c.Query("page", "1"))
pageSize, _ := strconv.Atoi(c.Query("page_size", "50"))
// page, _ := strconv.Atoi(c.Query("page", "1"))
// pageSize, _ := strconv.Atoi(c.Query("page_size", "50"))
banks, pagination, err := h.instSvc.List(
c.Context(),
countryIDPtr,
isActivePtr,
searchTermPtr,
page,
pageSize,
)
if err != nil {
h.mongoLoggerSvc.Error("failed to list banks",
zap.Error(err),
zap.Any("query_params", c.Queries()),
)
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to retrieve banks",
Error: err.Error(),
})
}
// banks, pagination, err := h.instSvc.List(
// c.Context(),
// countryIDPtr,
// isActivePtr,
// searchTermPtr,
// page,
// pageSize,
// )
// if err != nil {
// h.mongoLoggerSvc.Error("failed to list banks",
// zap.Error(err),
// zap.Any("query_params", c.Queries()),
// )
// return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
// Message: "Failed to retrieve banks",
// Error: err.Error(),
// })
// }
return c.Status(fiber.StatusOK).JSON(domain.InstResponse{
Message: "Banks retrieved successfully",
Status: "success",
Data: banks,
Pagination: pagination,
})
}
// return c.Status(fiber.StatusOK).JSON(domain.InstResponse{
// Message: "Banks retrieved successfully",
// Status: "success",
// Data: banks,
// Pagination: pagination,
// })
// }

View File

@ -2,15 +2,12 @@ package handlers
import (
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/services/authentication"
"Yimaru-Backend/internal/web_server/response"
"fmt"
"strconv"
"time"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
type CreateManagerReq struct {
@ -34,75 +31,75 @@ type CreateManagerReq struct {
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/managers [post]
func (h *Handler) CreateManager(c *fiber.Ctx) error {
// func (h *Handler) CreateManager(c *fiber.Ctx) error {
// Get user_id from middleware
// // Get user_id from middleware
var req CreateManagerReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("RegisterUser failed", "error", err)
h.mongoLoggerSvc.Info("CreateManager failed to create manager",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate CreateManager",
zap.Any("request", req),
zap.Int("status_code", fiber.StatusBadRequest),
zap.String("errMsg", errMsg),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
// var req CreateManagerReq
// if err := c.BodyParser(&req); err != nil {
// h.logger.Error("RegisterUser failed", "error", err)
// h.mongoLoggerSvc.Info("CreateManager failed to create manager",
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
// }
// valErrs, ok := h.validator.Validate(c, req)
// if !ok {
// var errMsg string
// for field, msg := range valErrs {
// errMsg += fmt.Sprintf("%s: %s; ", field, msg)
// }
// h.mongoLoggerSvc.Info("Failed to validate CreateManager",
// zap.Any("request", req),
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.String("errMsg", errMsg),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, errMsg)
// }
var companyID domain.ValidInt64
role := c.Locals("role").(domain.Role)
if role == domain.RoleSuperAdmin {
if req.CompanyID == nil {
h.logger.Error("RegisterUser failed error: company id is required")
h.mongoLoggerSvc.Info("RegisterUser failed error: company id is required",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Company ID is required for super-admin")
}
companyID = domain.ValidInt64{
Value: *req.CompanyID,
Valid: true,
}
} else {
companyID = c.Locals("company_id").(domain.ValidInt64)
}
// var companyID domain.ValidInt64
// role := c.Locals("role").(domain.Role)
// if role == domain.RoleSuperAdmin {
// if req.CompanyID == nil {
// h.logger.Error("RegisterUser failed error: company id is required")
// h.mongoLoggerSvc.Info("RegisterUser failed error: company id is required",
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, "Company ID is required for super-admin")
// }
// companyID = domain.ValidInt64{
// Value: *req.CompanyID,
// Valid: true,
// }
// } else {
// companyID = c.Locals("company_id").(domain.ValidInt64)
// }
user := domain.CreateUserReq{
FirstName: req.FirstName,
LastName: req.LastName,
Email: req.Email,
PhoneNumber: req.PhoneNumber,
Password: req.Password,
Role: string(domain.RoleBranchManager),
CompanyID: companyID,
}
_, err := h.userSvc.CreateUser(c.Context(), user, true)
if err != nil {
h.mongoLoggerSvc.Error("CreateManager failed to create manager",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create manager:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Manager created successfully", nil, nil)
// user := domain.CreateUserReq{
// FirstName: req.FirstName,
// LastName: req.LastName,
// Email: req.Email,
// PhoneNumber: req.PhoneNumber,
// Password: req.Password,
// Role: string(domain.RoleBranchManager),
// OrganizationID: companyID,
// }
// _, err := h.userSvc.CreateUser(c.Context(), user, true)
// if err != nil {
// h.mongoLoggerSvc.Error("CreateManager failed to create manager",
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to create manager:"+err.Error())
// }
// return response.WriteJSON(c, fiber.StatusOK, "Manager created successfully", nil, nil)
}
// }
type ManagersRes struct {
ID int64 `json:"id"`
@ -133,141 +130,141 @@ type ManagersRes struct {
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/managers [get]
func (h *Handler) GetAllManagers(c *fiber.Ctx) error {
role := c.Locals("role").(domain.Role)
companyId := c.Locals("company_id").(domain.ValidInt64)
// func (h *Handler) GetAllManagers(c *fiber.Ctx) error {
// role := c.Locals("role").(domain.Role)
// companyId := c.Locals("company_id").(domain.ValidInt64)
// Checking to make sure that admin user has a company id in the token
if role != domain.RoleSuperAdmin && !companyId.Valid {
h.mongoLoggerSvc.Error("Cannot get company ID from context",
zap.String("role", string(role)),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID from context")
}
// // Checking to make sure that admin user has a company id in the token
// if role != domain.RoleSuperAdmin && !companyId.Valid {
// h.mongoLoggerSvc.Error("Cannot get company ID from context",
// zap.String("role", string(role)),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID from context")
// }
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
// searchQuery := c.Query("query")
// searchString := domain.ValidString{
// Value: searchQuery,
// Valid: searchQuery != "",
// }
createdBeforeQuery := c.Query("created_before")
var createdBefore domain.ValidTime
if createdBeforeQuery != "" {
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
if err != nil {
h.mongoLoggerSvc.Info("invalid created_before format",
zap.String("created_before", createdBeforeQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format")
}
createdBefore = domain.ValidTime{
Value: createdBeforeParsed,
Valid: true,
}
}
// createdBeforeQuery := c.Query("created_before")
// var createdBefore domain.ValidTime
// if createdBeforeQuery != "" {
// createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
// if err != nil {
// h.mongoLoggerSvc.Info("invalid created_before format",
// zap.String("created_before", createdBeforeQuery),
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format")
// }
// createdBefore = domain.ValidTime{
// Value: createdBeforeParsed,
// Valid: true,
// }
// }
createdAfterQuery := c.Query("created_after")
var createdAfter domain.ValidTime
if createdAfterQuery != "" {
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
if err != nil {
h.mongoLoggerSvc.Info("invalid created_after format",
zap.String("created_after", createdAfterQuery),
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format")
}
createdAfter = domain.ValidTime{
Value: createdAfterParsed,
Valid: true,
}
}
// createdAfterQuery := c.Query("created_after")
// var createdAfter domain.ValidTime
// if createdAfterQuery != "" {
// createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
// if err != nil {
// h.mongoLoggerSvc.Info("invalid created_after format",
// zap.String("created_after", createdAfterQuery),
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format")
// }
// createdAfter = domain.ValidTime{
// Value: createdAfterParsed,
// Valid: true,
// }
// }
filter := domain.UserFilter{
Role: string(domain.RoleBranchManager),
CompanyID: companyId,
Page: domain.ValidInt{
Value: c.QueryInt("page", 1) - 1,
Valid: true,
},
PageSize: domain.ValidInt{
Value: c.QueryInt("page_size", 10),
Valid: true,
},
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
}
valErrs, ok := h.validator.Validate(c, filter)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate get all filters",
zap.Any("filter", filter),
zap.Int("status_code", fiber.StatusBadRequest),
zap.String("errMsg", errMsg),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
managers, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
if err != nil {
h.logger.Error("GetAllManagers failed", "error", err)
h.mongoLoggerSvc.Error("GetAllManagers failed to get all managers",
zap.Any("filter", filter),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get Managers"+err.Error())
}
// filter := domain.UserFilter{
// Role: string(domain.RoleBranchManager),
// OrganizationID: companyId,
// Page: domain.ValidInt{
// Value: c.QueryInt("page", 1) - 1,
// Valid: true,
// },
// PageSize: domain.ValidInt{
// Value: c.QueryInt("page_size", 10),
// Valid: true,
// },
// Query: searchString,
// CreatedBefore: createdBefore,
// CreatedAfter: createdAfter,
// }
// valErrs, ok := h.validator.Validate(c, filter)
// if !ok {
// var errMsg string
// for field, msg := range valErrs {
// errMsg += fmt.Sprintf("%s: %s; ", field, msg)
// }
// h.mongoLoggerSvc.Info("Failed to validate get all filters",
// zap.Any("filter", filter),
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.String("errMsg", errMsg),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, errMsg)
// }
// managers, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
// if err != nil {
// h.logger.Error("GetAllManagers failed", "error", err)
// h.mongoLoggerSvc.Error("GetAllManagers failed to get all managers",
// zap.Any("filter", filter),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to get Managers"+err.Error())
// }
var result []ManagersRes = make([]ManagersRes, len(managers))
for index, manager := range managers {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), manager.ID)
if err != nil {
if err == authentication.ErrRefreshTokenNotFound {
lastLogin = &manager.CreatedAt
} else {
h.mongoLoggerSvc.Error("Failed to get user last login",
zap.Int64("userID", manager.ID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login:"+err.Error())
}
}
result[index] = ManagersRes{
ID: manager.ID,
FirstName: manager.FirstName,
LastName: manager.LastName,
Email: manager.Email,
PhoneNumber: manager.PhoneNumber,
Role: manager.Role,
EmailVerified: manager.EmailVerified,
PhoneVerified: manager.PhoneVerified,
CreatedAt: manager.CreatedAt,
UpdatedAt: manager.UpdatedAt,
SuspendedAt: manager.SuspendedAt,
Suspended: manager.Suspended,
LastLogin: *lastLogin,
}
}
// var result []ManagersRes = make([]ManagersRes, len(managers))
// for index, manager := range managers {
// lastLogin, err := h.authSvc.GetLastLogin(c.Context(), manager.ID)
// if err != nil {
// if err == authentication.ErrRefreshTokenNotFound {
// lastLogin = &manager.CreatedAt
// } else {
// h.mongoLoggerSvc.Error("Failed to get user last login",
// zap.Int64("userID", manager.ID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login:"+err.Error())
// }
// }
// result[index] = ManagersRes{
// ID: manager.ID,
// FirstName: manager.FirstName,
// LastName: manager.LastName,
// Email: manager.Email,
// PhoneNumber: manager.PhoneNumber,
// Role: manager.Role,
// EmailVerified: manager.EmailVerified,
// PhoneVerified: manager.PhoneVerified,
// CreatedAt: manager.CreatedAt,
// UpdatedAt: manager.UpdatedAt,
// SuspendedAt: manager.SuspendedAt,
// Suspended: manager.Suspended,
// LastLogin: *lastLogin,
// }
// }
return response.WritePaginatedJSON(c, fiber.StatusOK, "Managers retrieved successfully", result, nil, filter.Page.Value, int(total))
// return response.WritePaginatedJSON(c, fiber.StatusOK, "Managers retrieved successfully", result, nil, filter.Page.Value, int(total))
}
// }
// GetManagerByID godoc
// @Summary Get manager by id
@ -281,103 +278,103 @@ func (h *Handler) GetAllManagers(c *fiber.Ctx) error {
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/managers/{id} [get]
func (h *Handler) GetManagerByID(c *fiber.Ctx) error {
role := c.Locals("role").(domain.Role)
companyId := c.Locals("company_id").(domain.ValidInt64)
requestUserID := c.Locals("user_id").(int64)
// func (h *Handler) GetManagerByID(c *fiber.Ctx) error {
// role := c.Locals("role").(domain.Role)
// companyId := c.Locals("company_id").(domain.ValidInt64)
// requestUserID := c.Locals("user_id").(int64)
// Only Super Admin / Admin / Branch Manager can view this route
if role != domain.RoleSuperAdmin && role != domain.RoleAdmin && role != domain.RoleBranchManager {
h.mongoLoggerSvc.Warn("Attempt to access from unauthorized role",
zap.Int64("userID", requestUserID),
zap.String("role", string(role)),
zap.Int("status_code", fiber.StatusForbidden),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusForbidden, "This role cannot view this route")
}
// // Only Super Admin / Admin / Branch Manager can view this route
// if role != domain.RoleSuperAdmin && role != domain.RoleAdmin && role != domain.RoleBranchManager {
// h.mongoLoggerSvc.Warn("Attempt to access from unauthorized role",
// zap.Int64("userID", requestUserID),
// zap.String("role", string(role)),
// zap.Int("status_code", fiber.StatusForbidden),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusForbidden, "This role cannot view this route")
// }
if role != domain.RoleSuperAdmin && !companyId.Valid {
h.mongoLoggerSvc.Error("Cannot get company ID in context",
zap.String("role", string(role)),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID in context")
}
// if role != domain.RoleSuperAdmin && !companyId.Valid {
// h.mongoLoggerSvc.Error("Cannot get company ID in context",
// zap.String("role", string(role)),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Cannot get company ID in context")
// }
userIDstr := c.Params("id")
userID, err := strconv.ParseInt(userIDstr, 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "Invalid managers ID")
}
// userIDstr := c.Params("id")
// userID, err := strconv.ParseInt(userIDstr, 10, 64)
// if err != nil {
// return fiber.NewError(fiber.StatusBadRequest, "Invalid managers ID")
// }
user, err := h.userSvc.GetUserByID(c.Context(), userID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get manager by id",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get managers:"+err.Error())
}
// user, err := h.userSvc.GetUserByID(c.Context(), userID)
// if err != nil {
// h.mongoLoggerSvc.Error("Failed to get manager by id",
// zap.Int64("userID", userID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to get managers:"+err.Error())
// }
// A Branch Manager can only fetch his own branch info
if role == domain.RoleBranchManager && user.ID != requestUserID {
h.mongoLoggerSvc.Warn("Attempt to access another branch manager info",
zap.String("userID", userIDstr),
zap.Int("status_code", fiber.StatusForbidden),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusForbidden, "User Access Not Allowed")
}
// // A Branch Manager can only fetch his own branch info
// if role == domain.RoleBranchManager && user.ID != requestUserID {
// h.mongoLoggerSvc.Warn("Attempt to access another branch manager info",
// zap.String("userID", userIDstr),
// zap.Int("status_code", fiber.StatusForbidden),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusForbidden, "User Access Not Allowed")
// }
// Check that only admin from company can view this route
if role != domain.RoleSuperAdmin && user.CompanyID.Value != companyId.Value {
h.mongoLoggerSvc.Warn("Attempt to access info from another company",
zap.String("userID", userIDstr),
zap.Int("status_code", fiber.StatusForbidden),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusForbidden, "Cannot access another company information")
}
// // Check that only admin from company can view this route
// if role != domain.RoleSuperAdmin && user.OrganizationID.Value != companyId.Value {
// h.mongoLoggerSvc.Warn("Attempt to access info from another company",
// zap.String("userID", userIDstr),
// zap.Int("status_code", fiber.StatusForbidden),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusForbidden, "Cannot access another company information")
// }
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
if err != nil {
if err != authentication.ErrRefreshTokenNotFound {
h.mongoLoggerSvc.Error("Failed to get user last login",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login"+err.Error())
}
// lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
// if err != nil {
// if err != authentication.ErrRefreshTokenNotFound {
// h.mongoLoggerSvc.Error("Failed to get user last login",
// zap.Int64("userID", userID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login"+err.Error())
// }
lastLogin = &user.CreatedAt
}
// lastLogin = &user.CreatedAt
// }
res := ManagersRes{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email,
PhoneNumber: user.PhoneNumber,
Role: user.Role,
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
SuspendedAt: user.SuspendedAt,
Suspended: user.Suspended,
LastLogin: *lastLogin,
}
// res := ManagersRes{
// ID: user.ID,
// FirstName: user.FirstName,
// LastName: user.LastName,
// Email: user.Email,
// PhoneNumber: user.PhoneNumber,
// Role: user.Role,
// EmailVerified: user.EmailVerified,
// PhoneVerified: user.PhoneVerified,
// CreatedAt: user.CreatedAt,
// UpdatedAt: user.UpdatedAt,
// SuspendedAt: user.SuspendedAt,
// Suspended: user.Suspended,
// LastLogin: *lastLogin,
// }
return response.WriteJSON(c, fiber.StatusOK, "User retrieved successfully", res, nil)
}
// return response.WriteJSON(c, fiber.StatusOK, "User retrieved successfully", res, nil)
// }
type updateManagerReq struct {
FirstName string `json:"first_name" example:"John"`
@ -433,7 +430,7 @@ func (h *Handler) UpdateManagers(c *fiber.Ctx) error {
}
err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
UserId: ManagersId,
UserID: ManagersId,
FirstName: domain.ValidString{
Value: req.FirstName,
Valid: req.FirstName != "",
@ -446,7 +443,7 @@ func (h *Handler) UpdateManagers(c *fiber.Ctx) error {
Value: req.Suspended,
Valid: true,
},
CompanyID: companyID,
OrganizationID: companyID,
},
)
if err != nil {

View File

@ -5,11 +5,9 @@ import (
"Yimaru-Backend/internal/web_server/ws"
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"strconv"
"strings"
"time"
"github.com/gofiber/fiber/v2"
@ -131,45 +129,45 @@ func (h *Handler) ConnectSocket(c *fiber.Ctx) error {
return nil
}
func (h *Handler) MarkNotificationAsRead(c *fiber.Ctx) error {
type Request struct {
NotificationIDs []string `json:"notification_ids" validate:"required"`
}
// func (h *Handler) MarkNotificationAsRead(c *fiber.Ctx) error {
// type Request struct {
// NotificationIDs []string `json:"notification_ids" validate:"required"`
// }
var req Request
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse request body",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
}
// var req Request
// if err := c.BodyParser(&req); err != nil {
// h.mongoLoggerSvc.Info("Failed to parse request body",
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
// }
userID, ok := c.Locals("user_id").(int64)
if !ok || userID == 0 {
h.mongoLoggerSvc.Error("Invalid user ID in context",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "invalid user ID in context")
}
// userID, ok := c.Locals("user_id").(int64)
// if !ok || userID == 0 {
// h.mongoLoggerSvc.Error("Invalid user ID in context",
// zap.Int64("userID", userID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "invalid user ID in context")
// }
fmt.Printf("Notification IDs: %v \n", req.NotificationIDs)
if err := h.notificationSvc.MarkAsRead(context.Background(), req.NotificationIDs, userID); err != nil {
h.mongoLoggerSvc.Error("Failed to mark notifications as read",
zap.String("notificationID", strings.Join(req.NotificationIDs, ",")),
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update notification status:", err.Error())
}
// fmt.Printf("Notification IDs: %v \n", req.NotificationIDs)
// if err := h.notificationSvc.MarkAsRead(context.Background(), req.NotificationIDs, userID); err != nil {
// h.mongoLoggerSvc.Error("Failed to mark notifications as read",
// zap.String("notificationID", strings.Join(req.NotificationIDs, ",")),
// zap.Int64("userID", userID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to update notification status:", err.Error())
// }
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Notification marked as read"})
}
// return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Notification marked as read"})
// }
func (h *Handler) CreateAndSendNotification(c *fiber.Ctx) error {
type Request struct {
@ -373,9 +371,9 @@ func (h *Handler) GetUserNotification(c *fiber.Ctx) error {
}
func (h *Handler) GetAllRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) {
return h.notificationSvc.ListRecipientIDs(ctx, receiver)
}
// func (h *Handler) GetAllRecipientIDs(ctx context.Context, receiver domain.NotificationRecieverSide) ([]int64, error) {
// return h.notificationSvc.ListRecipientIDs(ctx, receiver)
// }
func (h *Handler) CountUnreadNotifications(c *fiber.Ctx) error {

View File

@ -26,23 +26,23 @@ func (h *Handler) CreateReferralCode(c *fiber.Ctx) error {
)
return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification")
}
referralCode, err := h.referralSvc.CreateReferralCode(c.Context(), userID, companyID.Value)
// referralCode, err := h.referralSvc.CreateReferralCode(c.Context(), userID, companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to create referral",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create referral")
}
// if err != nil {
// h.mongoLoggerSvc.Error("Failed to create referral",
// zap.Int64("userID", userID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to create referral")
// }
fmt.Printf("Successfully created referral!")
res := domain.ConvertReferralCodeRes(referralCode)
// res := domain.ConvertReferralCodeRes(referralCode)
return response.WriteJSON(c, fiber.StatusOK, "Referral created successfully", res, nil)
return response.WriteJSON(c, fiber.StatusOK, "Referral created successfully", nil, nil)
}
func (h *Handler) GetReferralCode(c *fiber.Ctx) error {
@ -72,7 +72,7 @@ func (h *Handler) GetReferralCode(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user")
}
if !user.CompanyID.Valid || user.CompanyID.Value != companyID.Value {
if !user.OrganizationID.Valid || user.OrganizationID.Value != companyID.Value {
h.mongoLoggerSvc.Warn("User attempt to login to different company",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
@ -82,88 +82,24 @@ func (h *Handler) GetReferralCode(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Failed to retrieve user")
}
referrals, err := h.referralSvc.GetReferralCodesByUser(c.Context(), user.ID)
// referrals, err := h.referralSvc.GetReferralCodesByUser(c.Context(), user.ID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get user referrals",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user referral codes")
}
// if err != nil {
// h.mongoLoggerSvc.Error("Failed to get user referrals",
// zap.Int64("userID", userID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user referral codes")
// }
result := domain.ConvertReferralCodeResList(referrals)
// result := domain.ConvertReferralCodeResList(referrals)
return response.WriteJSON(c, fiber.StatusOK, "Referral Code Fetched Successfully", result, nil)
return response.WriteJSON(c, fiber.StatusOK, "Referral Code Fetched Successfully", nil, nil)
}
// GetReferralStats godoc
// @Summary Get referral statistics
// @Description Retrieves referral statistics for the authenticated user
// @Tags referral
// @Accept json
// @Produce json
// @Success 200 {object} domain.ReferralStats
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Security Bearer
// @Router /api/v1/tenant/{tenant_slug}/referral/stats [get]
func (h *Handler) GetReferralStats(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
userID, ok := c.Locals("user_id").(int64)
if !ok || userID == 0 {
h.mongoLoggerSvc.Error("Invalid user ID in context",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Invalid user id")
}
user, err := h.userSvc.GetUserByID(c.Context(), userID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get user",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user")
}
if !user.CompanyID.Valid || user.CompanyID.Value != companyID.Value {
h.mongoLoggerSvc.Warn("User attempt to login to different company",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to retrieve user")
}
stats, err := h.referralSvc.GetReferralStats(c.Context(), user.ID, companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get referral stats",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve referral stats")
}
res := domain.ConvertReferralStatsRes(stats)
return response.WriteJSON(c, fiber.StatusOK, "Referral stats retrieved successfully", res, nil)
}
// // UpdateReferralSettings godoc
// // @Summary Update referral settings
// // @Description Updates referral settings (admin only)

View File

@ -108,85 +108,85 @@ func (h *Handler) UpdateGlobalSettingList(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "setting updated", res, nil)
}
func (h *Handler) SaveCompanySettingList(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
// func (h *Handler) SaveCompanySettingList(c *fiber.Ctx) error {
// companyID := c.Locals("company_id").(domain.ValidInt64)
// if !companyID.Valid {
// h.BadRequestLogger().Error("invalid company id")
// return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
// }
var req domain.SaveSettingListReq
// var req domain.SaveSettingListReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse SaveSettingListReq",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate SaveSettingListReq",
zap.Int("status_code", fiber.StatusBadRequest),
zap.String("errMsg", errMsg),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
settingList := domain.ConvertSaveSettingListReq(req)
err := h.settingSvc.InsertCompanySettingList(c.Context(), settingList, companyID.Value)
// if err := c.BodyParser(&req); err != nil {
// h.mongoLoggerSvc.Info("Failed to parse SaveSettingListReq",
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
// }
// valErrs, ok := h.validator.Validate(c, req)
// if !ok {
// var errMsg string
// for field, msg := range valErrs {
// errMsg += fmt.Sprintf("%s: %s; ", field, msg)
// }
// h.mongoLoggerSvc.Info("Failed to validate SaveSettingListReq",
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.String("errMsg", errMsg),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, errMsg)
// }
// // settingList := domain.ConvertSaveSettingListReq(req)
// // err := h.settingSvc.InsertCompanySettingList(c.Context(), settingList, companyID.Value)
if err != nil {
h.mongoLoggerSvc.Info("failed to save setting",
zap.Any("setting_list", settingList),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "failed to save setting")
}
// // if err != nil {
// // h.mongoLoggerSvc.Info("failed to save setting",
// // zap.Any("setting_list", settingList),
// // zap.Int("status_code", fiber.StatusInternalServerError),
// // zap.Time("timestamp", time.Now()),
// // )
// // return fiber.NewError(fiber.StatusInternalServerError, "failed to save setting")
// // }
settingsList, err := h.settingSvc.GetOverrideSettingsList(c.Context(), companyID.Value)
// settingsList, err := h.settingSvc.GetOverrideSettingsList(c.Context(), companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to fetch settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
}
// if err != nil {
// h.mongoLoggerSvc.Error("Failed to fetch settings",
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
// }
return response.WriteJSON(c, fiber.StatusOK, "setting updated", settingsList, nil)
// return response.WriteJSON(c, fiber.StatusOK, "setting updated", settingsList, nil)
}
// }
func (h *Handler) GetCompanySettingList(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
// func (h *Handler) GetCompanySettingList(c *fiber.Ctx) error {
// companyID := c.Locals("company_id").(domain.ValidInt64)
// if !companyID.Valid {
// h.BadRequestLogger().Error("invalid company id")
// return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
// }
settingsList, err := h.settingSvc.GetOverrideSettingsList(c.Context(), companyID.Value)
// settingsList, err := h.settingSvc.GetOverrideSettingsList(c.Context(), companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to fetch settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
}
// if err != nil {
// h.mongoLoggerSvc.Error("Failed to fetch settings",
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
// }
res := domain.ConvertSettingListRes(settingsList)
// res := domain.ConvertSettingListRes(settingsList)
return response.WriteJSON(c, fiber.StatusOK, "All Settings retrieved successfully", res, nil)
}
// return response.WriteJSON(c, fiber.StatusOK, "All Settings retrieved successfully", res, nil)
// }
// /api/v1/{tenant_slug}/settings/{key}
func (h *Handler) DeleteCompanySetting(c *fiber.Ctx) error {
@ -205,16 +205,16 @@ func (h *Handler) DeleteCompanySetting(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "setting key must be passed")
}
err := h.settingSvc.DeleteCompanySetting(c.Context(), companyID.Value, settingKey)
// err := h.settingSvc.DeleteCompanySetting(c.Context(), companyID.Value, settingKey)
if err != nil {
h.mongoLoggerSvc.Error("Failed to delete company override settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete company override settings:"+err.Error())
}
// if err != nil {
// h.mongoLoggerSvc.Error("Failed to delete company override settings",
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete company override settings:"+err.Error())
// }
return response.WriteJSON(c, fiber.StatusOK, "setting deleted", nil, nil)
}
@ -226,16 +226,16 @@ func (h *Handler) DeleteAllCompanySetting(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
err := h.settingSvc.DeleteAllCompanySetting(c.Context(), companyID.Value)
// err := h.settingSvc.DeleteAllCompanySetting(c.Context(), companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to delete company override settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete company override settings:"+err.Error())
}
// if err != nil {
// h.mongoLoggerSvc.Error("Failed to delete company override settings",
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete company override settings:"+err.Error())
// }
return response.WriteJSON(c, fiber.StatusOK, "setting deleted", nil, nil)
}

View File

@ -33,77 +33,77 @@ type CreateTransactionApproverReq struct {
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/admin [post]
func (h *Handler) CreateTransactionApprover(c *fiber.Ctx) error {
var companyID domain.ValidInt64
var req CreateTransactionApproverReq
// func (h *Handler) CreateTransactionApprover(c *fiber.Ctx) error {
// var companyID domain.ValidInt64
// var req CreateTransactionApproverReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("failed to parse CreateAdmin request",
zap.Int64("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request:"+err.Error())
}
// if err := c.BodyParser(&req); err != nil {
// h.mongoLoggerSvc.Info("failed to parse CreateAdmin request",
// zap.Int64("status_code", fiber.StatusBadRequest),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, "Invalid request:"+err.Error())
// }
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Error("validation failed for CreateAdmin request",
zap.Int64("status_code", fiber.StatusBadRequest),
zap.Any("validation_errors", valErrs),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
// valErrs, ok := h.validator.Validate(c, req)
// if !ok {
// var errMsg string
// for field, msg := range valErrs {
// errMsg += fmt.Sprintf("%s: %s; ", field, msg)
// }
// h.mongoLoggerSvc.Error("validation failed for CreateAdmin request",
// zap.Int64("status_code", fiber.StatusBadRequest),
// zap.Any("validation_errors", valErrs),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, errMsg)
// }
// _, err := h.companySvc.GetCompanyByID(c.Context(), req.CompanyID)
// if err != nil {
// h.mongoLoggerSvc.Error("invalid company ID for CreateAdmin",
// zap.Int64("status_code", fiber.StatusInternalServerError),
// zap.Int64("company_id", req.CompanyID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Company ID is invalid:"+err.Error())
// }
companyID = domain.ValidInt64{
Value: req.CompanyID,
Valid: true,
}
// // _, err := h.companySvc.GetCompanyByID(c.Context(), req.CompanyID)
// // if err != nil {
// // h.mongoLoggerSvc.Error("invalid company ID for CreateAdmin",
// // zap.Int64("status_code", fiber.StatusInternalServerError),
// // zap.Int64("company_id", req.CompanyID),
// // zap.Error(err),
// // zap.Time("timestamp", time.Now()),
// // )
// // return fiber.NewError(fiber.StatusInternalServerError, "Company ID is invalid:"+err.Error())
// // }
// companyID = domain.ValidInt64{
// Value: req.CompanyID,
// Valid: true,
// }
user := domain.CreateUserReq{
FirstName: req.FirstName,
LastName: req.LastName,
Email: req.Email,
PhoneNumber: req.PhoneNumber,
Password: req.Password,
Role: string(domain.RoleTransactionApprover),
CompanyID: companyID,
}
// user := domain.CreateUserReq{
// FirstName: req.FirstName,
// LastName: req.LastName,
// Email: req.Email,
// PhoneNumber: req.PhoneNumber,
// Password: req.Password,
// Role: string(domain.RoleTransactionApprover),
// OrganizationID: companyID,
// }
newUser, err := h.userSvc.CreateUser(c.Context(), user, true)
if err != nil {
h.mongoLoggerSvc.Error("failed to create admin user",
zap.Int64("status_code", fiber.StatusInternalServerError),
zap.Any("request", req),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create admin:"+err.Error())
}
// newUser, err := h.userSvc.CreateUser(c.Context(), user, true)
// if err != nil {
// h.mongoLoggerSvc.Error("failed to create admin user",
// zap.Int64("status_code", fiber.StatusInternalServerError),
// zap.Any("request", req),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to create admin:"+err.Error())
// }
h.mongoLoggerSvc.Info("transaction_approver created successfully",
zap.Int64("transaction_approver_id", newUser.ID),
zap.String("email", newUser.Email),
zap.Time("timestamp", time.Now()),
)
// h.mongoLoggerSvc.Info("transaction_approver created successfully",
// zap.Int64("transaction_approver_id", newUser.ID),
// zap.String("email", newUser.Email),
// zap.Time("timestamp", time.Now()),
// )
return response.WriteJSON(c, fiber.StatusOK, "Transaction Approver created successfully", nil, nil)
}
// return response.WriteJSON(c, fiber.StatusOK, "Transaction Approver created successfully", nil, nil)
// }
type TransactionApproverRes struct {
ID int64 `json:"id"`
@ -134,144 +134,144 @@ type TransactionApproverRes struct {
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/t-approver [get]
func (h *Handler) GetAllTransactionApprovers(c *fiber.Ctx) error {
role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
// func (h *Handler) GetAllTransactionApprovers(c *fiber.Ctx) error {
// role := c.Locals("role").(domain.Role)
// companyID := c.Locals("company_id").(domain.ValidInt64)
searchQuery := c.Query("query")
searchString := domain.ValidString{
Value: searchQuery,
Valid: searchQuery != "",
}
// searchQuery := c.Query("query")
// searchString := domain.ValidString{
// Value: searchQuery,
// Valid: searchQuery != "",
// }
createdBeforeQuery := c.Query("created_before")
var createdBefore domain.ValidTime
if createdBeforeQuery != "" {
createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
if err != nil {
h.logger.Info("invalid start_time format", "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
}
createdBefore = domain.ValidTime{
Value: createdBeforeParsed,
Valid: true,
}
}
// createdBeforeQuery := c.Query("created_before")
// var createdBefore domain.ValidTime
// if createdBeforeQuery != "" {
// createdBeforeParsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
// if err != nil {
// h.logger.Info("invalid start_time format", "error", err)
// return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
// }
// createdBefore = domain.ValidTime{
// Value: createdBeforeParsed,
// Valid: true,
// }
// }
createdAfterQuery := c.Query("created_after")
var createdAfter domain.ValidTime
if createdAfterQuery != "" {
createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
if err != nil {
h.logger.Info("invalid start_time format", "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
}
createdAfter = domain.ValidTime{
Value: createdAfterParsed,
Valid: true,
}
}
// createdAfterQuery := c.Query("created_after")
// var createdAfter domain.ValidTime
// if createdAfterQuery != "" {
// createdAfterParsed, err := time.Parse(time.RFC3339, createdAfterQuery)
// if err != nil {
// h.logger.Info("invalid start_time format", "error", err)
// return fiber.NewError(fiber.StatusBadRequest, "Invalid start_time format")
// }
// createdAfter = domain.ValidTime{
// Value: createdAfterParsed,
// Valid: true,
// }
// }
var companyIDFilter domain.ValidInt64
if role == domain.RoleSuperAdmin {
companyIDQuery := int64(c.QueryInt("company_id"))
companyIDFilter = domain.ValidInt64{
Value: companyIDQuery,
Valid: companyIDQuery != 0,
}
} else {
if !companyID.Valid {
h.logger.Info("invalid companyID")
return fiber.NewError(fiber.StatusBadRequest, "Unable to get company ID")
}
// var companyIDFilter domain.ValidInt64
// if role == domain.RoleSuperAdmin {
// companyIDQuery := int64(c.QueryInt("company_id"))
// companyIDFilter = domain.ValidInt64{
// Value: companyIDQuery,
// Valid: companyIDQuery != 0,
// }
// } else {
// if !companyID.Valid {
// h.logger.Info("invalid companyID")
// return fiber.NewError(fiber.StatusBadRequest, "Unable to get company ID")
// }
companyIDFilter = companyID
}
// companyIDFilter = companyID
// }
filter := domain.UserFilter{
Role: string(domain.RoleTransactionApprover),
CompanyID: companyIDFilter,
Page: domain.ValidInt{
Value: c.QueryInt("page", 1) - 1,
Valid: true,
},
PageSize: domain.ValidInt{
Value: c.QueryInt("page_size", 10),
Valid: true,
},
Query: searchString,
CreatedBefore: createdBefore,
CreatedAfter: createdAfter,
}
// filter := domain.UserFilter{
// Role: string(domain.RoleTransactionApprover),
// OrganizationID: companyIDFilter,
// Page: domain.ValidInt{
// Value: c.QueryInt("page", 1) - 1,
// Valid: true,
// },
// PageSize: domain.ValidInt{
// Value: c.QueryInt("page_size", 10),
// Valid: true,
// },
// Query: searchString,
// CreatedBefore: createdBefore,
// CreatedAfter: createdAfter,
// }
valErrs, ok := h.validator.Validate(c, filter)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("invalid filter values in GetAllAdmins request",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Any("validation_errors", valErrs),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
// valErrs, ok := h.validator.Validate(c, filter)
// if !ok {
// var errMsg string
// for field, msg := range valErrs {
// errMsg += fmt.Sprintf("%s: %s; ", field, msg)
// }
// h.mongoLoggerSvc.Info("invalid filter values in GetAllAdmins request",
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Any("validation_errors", valErrs),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusBadRequest, errMsg)
// }
users, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
if err != nil {
h.mongoLoggerSvc.Error("failed to get users from user service",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Any("filter", filter),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get users"+err.Error())
}
// users, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
// if err != nil {
// h.mongoLoggerSvc.Error("failed to get users from user service",
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Any("filter", filter),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to get users"+err.Error())
// }
result := make([]TransactionApproverRes, len(users))
for index, admin := range users {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), admin.ID)
if err != nil {
if err == authentication.ErrRefreshTokenNotFound {
lastLogin = &admin.CreatedAt
} else {
h.mongoLoggerSvc.Error("failed to get last login for admin",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Int64("admin_id", admin.ID),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login"+err.Error())
}
}
// result := make([]TransactionApproverRes, len(users))
// for index, admin := range users {
// lastLogin, err := h.authSvc.GetLastLogin(c.Context(), admin.ID)
// if err != nil {
// if err == authentication.ErrRefreshTokenNotFound {
// lastLogin = &admin.CreatedAt
// } else {
// h.mongoLoggerSvc.Error("failed to get last login for admin",
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Int64("admin_id", admin.ID),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login"+err.Error())
// }
// }
result[index] = TransactionApproverRes{
ID: admin.ID,
FirstName: admin.FirstName,
LastName: admin.LastName,
Email: admin.Email,
PhoneNumber: admin.PhoneNumber,
Role: admin.Role,
EmailVerified: admin.EmailVerified,
PhoneVerified: admin.PhoneVerified,
CreatedAt: admin.CreatedAt,
UpdatedAt: admin.UpdatedAt,
SuspendedAt: admin.SuspendedAt,
Suspended: admin.Suspended,
LastLogin: *lastLogin,
}
}
// result[index] = TransactionApproverRes{
// ID: admin.ID,
// FirstName: admin.FirstName,
// LastName: admin.LastName,
// Email: admin.Email,
// PhoneNumber: admin.PhoneNumber,
// Role: admin.Role,
// EmailVerified: admin.EmailVerified,
// PhoneVerified: admin.PhoneVerified,
// CreatedAt: admin.CreatedAt,
// UpdatedAt: admin.UpdatedAt,
// SuspendedAt: admin.SuspendedAt,
// Suspended: admin.Suspended,
// LastLogin: *lastLogin,
// }
// }
h.mongoLoggerSvc.Info("approvers retrieved successfully",
zap.Int("status_code", fiber.StatusOK),
zap.Int("count", len(result)),
zap.Int("page", filter.Page.Value+1),
zap.Time("timestamp", time.Now()),
)
// h.mongoLoggerSvc.Info("approvers retrieved successfully",
// zap.Int("status_code", fiber.StatusOK),
// zap.Int("count", len(result)),
// zap.Int("page", filter.Page.Value+1),
// zap.Time("timestamp", time.Now()),
// )
return response.WritePaginatedJSON(c, fiber.StatusOK, "Admins retrieved successfully", result, nil, filter.Page.Value, int(total))
}
// return response.WritePaginatedJSON(c, fiber.StatusOK, "Admins retrieved successfully", result, nil, filter.Page.Value, int(total))
// }
// GetAdminByID godoc
// @Summary Get admin by id
@ -404,7 +404,7 @@ func (h *Handler) UpdateTransactionApprover(c *fiber.Ctx) error {
}
err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
UserId: ApproverID,
UserID: ApproverID,
FirstName: domain.ValidString{
Value: req.FirstName,
Valid: req.FirstName != "",

View File

@ -50,7 +50,7 @@ func (h *Handler) GetTenantSlugByToken(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user profile:"+err.Error())
}
if !user.CompanyID.Valid {
if !user.OrganizationID.Valid {
if user.Role == domain.RoleSuperAdmin {
return fiber.NewError(fiber.StatusBadRequest, "Role Super-Admin Doesn't have a company-id")
}
@ -75,7 +75,7 @@ func (h *Handler) GetTenantSlugByToken(c *fiber.Ctx) error {
// }
res := GetTenantSlugByToken{
Slug: strconv.FormatInt(user.CompanyID.Value,10),
Slug: strconv.FormatInt(user.OrganizationID.Value, 10),
}
return response.WriteJSON(c, fiber.StatusOK, "Tenant Slug retrieved successfully", res, nil)
@ -102,11 +102,11 @@ type CheckPhoneEmailExistRes struct {
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/checkPhoneEmailExist [post]
func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
// companyID := c.Locals("company_id").(domain.ValidInt64)
// if !companyID.Valid {
// h.BadRequestLogger().Error("invalid company id")
// return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
// }
var req CheckPhoneEmailExistReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse CheckPhoneEmailExist request",
@ -125,7 +125,7 @@ func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
emailExist, phoneExist, err := h.userSvc.CheckPhoneEmailExist(c.Context(), req.PhoneNumber, req.Email, companyID)
emailExist, phoneExist, err := h.userSvc.CheckPhoneEmailExist(c.Context(), req.PhoneNumber, req.Email, domain.ValidInt64{})
if err != nil {
h.mongoLoggerSvc.Error("Failed to check phone/email existence",
zap.Any("request", req),
@ -160,11 +160,11 @@ type RegisterCodeReq struct {
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/sendRegisterCode [post]
func (h *Handler) SendRegisterCode(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
// companyID := c.Locals("company_id").(domain.ValidInt64)
// if !companyID.Valid {
// h.BadRequestLogger().Error("invalid company id")
// return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
// }
var req RegisterCodeReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse SendRegisterCode request",
@ -195,7 +195,7 @@ func (h *Handler) SendRegisterCode(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
}
if err := h.userSvc.SendRegisterCode(c.Context(), medium, sentTo, domain.AfroMessage, companyID); err != nil {
if err := h.userSvc.SendRegisterCode(c.Context(), medium, sentTo, domain.AfroMessage, domain.ValidInt64{}); err != nil {
h.mongoLoggerSvc.Error("Failed to send register code",
zap.String("Medium", string(medium)),
zap.String("Send To", string(sentTo)),
@ -231,11 +231,6 @@ type RegisterUserReq struct {
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/register [post]
func (h *Handler) RegisterUser(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req RegisterUserReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse RegisterUser request",
@ -262,8 +257,8 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
Otp: req.Otp,
ReferralCode: req.ReferralCode,
OtpMedium: domain.OtpMediumEmail,
CompanyID: companyID,
Role: string(domain.RoleCustomer),
OrganizationID: domain.ValidInt64{},
Role: string(domain.RoleStudent),
}
medium, err := getMedium(req.Email, req.PhoneNumber)
if err != nil {
@ -279,7 +274,7 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
user.OtpMedium = medium
newUser, err := h.userSvc.RegisterUser(c.Context(), user)
_, err = h.userSvc.RegisterUser(c.Context(), user)
if err != nil {
if errors.Is(err, domain.ErrOtpAlreadyUsed) {
return fiber.NewError(fiber.StatusBadRequest, "Otp already used")
@ -303,44 +298,6 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "failed to register user:"+err.Error())
}
// _, err = h.walletSvc.CreateCustomerWallet(c.Context(), newUser.ID)
// if err != nil {
// h.mongoLoggerSvc.Error("Failed to create wallet for user",
// zap.Int64("userID", newUser.ID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Error(err),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Failed to create user wallet:"+err.Error())
// }
if req.ReferralCode != "" {
err = h.referralSvc.ProcessReferral(c.Context(), newUser.ID, req.ReferralCode, companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to process referral during registration",
zap.String("phone", req.PhoneNumber),
zap.String("code", req.ReferralCode),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
}
}
// TODO: Remove later
// _, err = h.walletSvc.AddToWallet(
// c.Context(), newWallet.RegularID, domain.ToCurrency(10000.0), domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
// "Added 10000.0 to wallet only as test for deployment")
if err != nil {
h.mongoLoggerSvc.Error("Failed to update wallet for user",
zap.Int64("userID", newUser.ID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update user wallet:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "Registration successful", nil, nil)
}
@ -424,11 +381,11 @@ func (h *Handler) SendResetCode(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/sendResetCode [post]
func (h *Handler) SendTenantResetCode(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
// companyID := c.Locals("company_id").(domain.ValidInt64)
// if !companyID.Valid {
// h.BadRequestLogger().Error("invalid company id")
// return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
// }
var req ResetCodeReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse SendResetCode request",
@ -465,7 +422,7 @@ func (h *Handler) SendTenantResetCode(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
}
if err := h.userSvc.SendResetCode(c.Context(), medium, sentTo, domain.AfroMessage, companyID); err != nil {
if err := h.userSvc.SendResetCode(c.Context(), medium, sentTo, domain.AfroMessage, domain.ValidInt64{}); err != nil {
h.mongoLoggerSvc.Error("Failed to send reset code",
zap.String("medium", string(medium)),
zap.String("sentTo", string(sentTo)),
@ -562,11 +519,11 @@ func (h *Handler) ResetPassword(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/resetPassword [post]
func (h *Handler) ResetTenantPassword(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
// companyID := c.Locals("company_id").(domain.ValidInt64)
// if !companyID.Valid {
// h.BadRequestLogger().Error("invalid company id")
// return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
// }
var req ResetPasswordReq
if err := c.BodyParser(&req); err != nil {
@ -604,7 +561,7 @@ func (h *Handler) ResetTenantPassword(c *fiber.Ctx) error {
Password: req.Password,
Otp: req.Otp,
OtpMedium: medium,
CompanyID: companyID.Value,
OrganizationID: 1,
}
if err := h.userSvc.ResetPassword(c.Context(), resetReq); err != nil {
@ -852,9 +809,14 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
companyID := c.Locals("company_id").(domain.ValidInt64)
// companyID := c.Locals("company_id").(domain.ValidInt64)
// strCompanyID := fmt.Sprintf("%v", companyID)
role, err := strconv.ParseInt(string(*req.Role), 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "failed to get users"+err.Error())
}
users, err := h.userSvc.SearchUserByNameOrPhone(c.Context(), req.SearchString, req.Role, companyID)
users, err := h.userSvc.SearchUserByNameOrPhone(c.Context(), req.SearchString, &role, nil)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get user by name or phone",
zap.Any("request", req),
@ -912,11 +874,11 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/search [post]
func (h *Handler) SearchCompanyUserByNameOrPhone(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
// companyID := c.Locals("company_id").(domain.ValidInt64)
// if !companyID.Valid {
// h.BadRequestLogger().Error("invalid company id")
// return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
// }
var req SearchUserByNameOrPhoneReq
if err := c.BodyParser(&req); err != nil {
@ -937,7 +899,13 @@ func (h *Handler) SearchCompanyUserByNameOrPhone(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
users, err := h.userSvc.SearchUserByNameOrPhone(c.Context(), req.SearchString, req.Role, companyID)
// strCompanyID := fmt.Sprintf("%v", companyID)
role, err := strconv.ParseInt(string(*req.Role), 10, 64)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "failed to get users"+err.Error())
}
users, err := h.userSvc.SearchUserByNameOrPhone(c.Context(), req.SearchString, &role, nil)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get user by name or phone",
zap.Any("request", req),
@ -1162,43 +1130,3 @@ func (h *Handler) UpdateUserSuspend(c *fiber.Ctx) error {
}
return response.WriteJSON(c, fiber.StatusOK, "User suspend status updated successfully", res, nil)
}
// GetBetByUserID godoc
// @Summary Gets user bets
// @Description Gets user bets
// @Tags user
// @Accept json
// @Produce json
// @Success 200 {array} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/{tenant_slug}/user/bets [get]
// func (h *Handler) GetBetByUserID(c *fiber.Ctx) error {
// userID, ok := c.Locals("user_id").(int64)
// if !ok || userID == 0 {
// h.mongoLoggerSvc.Error("Invalid user ID in context",
// zap.Int64("userID", userID),
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Time("timestamp", time.Now()),
// )
// return fiber.NewError(fiber.StatusInternalServerError, "Invalid user identification")
// }
// // bets, err := h.betSvc.GetBetByUserID(c.Context(), userID)
// // if err != nil {
// // h.mongoLoggerSvc.Error("Failed to get bets",
// // zap.Int64("userID", userID),
// // zap.Int("status_code", fiber.StatusInternalServerError),
// // zap.Error(err),
// // zap.Time("timestamp", time.Now()),
// // )
// // return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets:"+err.Error())
// // }
// // res := make([]domain.BetRes, len(bets))
// // for i, bet := range bets {
// // res[i] = domain.ConvertBet(bet)
// // }
// return response.WriteJSON(c, fiber.StatusOK, "User bets retrieved successfully", res, nil)
// }

View File

@ -23,17 +23,6 @@ type UserClaim struct {
CompanyID domain.NullJwtInt64
}
type PopOKClaim struct {
jwt.RegisteredClaims
UserID int64 `json:"user_id"`
Username string `json:"username"`
Currency string `json:"currency"`
Lang string `json:"lang"`
Mode string `json:"mode"`
SessionID string `json:"session_id"`
CompanyID domain.NullJwtInt64 `json:"company_id"`
}
type JwtConfig struct {
JwtAccessKey string
JwtAccessExpiry int
@ -42,9 +31,9 @@ type JwtConfig struct {
func CreateJwt(userId int64, Role domain.Role, CompanyID domain.ValidInt64, key string, expiry int) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, UserClaim{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "fortune-bet",
Issuer: "yimaru.com",
IssuedAt: jwt.NewNumericDate(time.Now()),
Audience: jwt.ClaimStrings{"api.fortunebets.net"},
Audience: jwt.ClaimStrings{"api.yimaru.com"},
NotBefore: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expiry) * time.Second)),
},
@ -59,29 +48,6 @@ func CreateJwt(userId int64, Role domain.Role, CompanyID domain.ValidInt64, key
return jwtToken, err
}
func CreatePopOKJwt(userID int64, CompanyID domain.ValidInt64, username, currency, lang, mode, sessionID, key string, expiry time.Duration) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, PopOKClaim{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "fortune-bet",
Audience: jwt.ClaimStrings{"popokgaming.com"},
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiry)),
},
UserID: userID,
Username: username, // ✅ Must be a valid string
Currency: currency,
Lang: lang,
Mode: mode,
SessionID: sessionID,
CompanyID: domain.NullJwtInt64{
Value: CompanyID.Value,
Valid: CompanyID.Valid,
},
})
return token.SignedString([]byte(key))
}
func ParseJwt(jwtToken string, key string) (*UserClaim, error) {
token, err := jwt.ParseWithClaims(jwtToken, &UserClaim{}, func(token *jwt.Token) (interface{}, error) {
return []byte(key), nil
@ -101,22 +67,3 @@ func ParseJwt(jwtToken string, key string) (*UserClaim, error) {
}
return nil, errors.New("invalid token claims")
}
func ParsePopOKJwt(jwtToken string, key string) (*PopOKClaim, error) {
token, err := jwt.ParseWithClaims(jwtToken, &PopOKClaim{}, func(token *jwt.Token) (interface{}, error) {
return []byte(key), nil
})
if err != nil {
if errors.Is(err, jwt.ErrTokenExpired) {
return nil, ErrExpiredToken
}
if errors.Is(err, jwt.ErrTokenMalformed) {
return nil, ErrMalformedToken
}
return nil, err
}
if claims, ok := token.Claims.(*PopOKClaim); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid PopOK token claims")
}

View File

@ -57,7 +57,6 @@ package jwtutil
// // Verify that the claims match the user and other values
// assert.Equal(t, strconv.Itoa(int(user.ID)), claims.UserId, "User ID should match")
// assert.Equal(t, "github.com/lafetz/snippitstash", claims.Issuer, "Issuer should match")
// assert.Equal(t, "fortune.com", claims.Audience[0], "Audience should match")
// assert.True(t, claims.ExpiresAt.Time.After(time.Now()), "Token should not be expired yet")
// // Ensure the parsing fails when using an invalid token

View File

@ -129,7 +129,7 @@ func (a *App) SuperAdminOnly(c *fiber.Ctx) error {
func (a *App) CompanyOnly(c *fiber.Ctx) error {
userID := c.Locals("user_id").(int64)
userRole := c.Locals("role").(domain.Role)
if userRole == domain.RoleCustomer {
if userRole == domain.RoleStudent {
a.mongoLoggerSvc.Warn("Attempt to access restricted CompanyOnly route",
zap.Int64("userID", userID),
zap.String("role", string(userRole)),
@ -159,7 +159,7 @@ func (a *App) OnlyAdminAndAbove(c *fiber.Ctx) error {
func (a *App) OnlyBranchManagerAndAbove(c *fiber.Ctx) error {
userID := c.Locals("user_id").(int64)
userRole := c.Locals("role").(domain.Role)
if userRole != domain.RoleSuperAdmin && userRole != domain.RoleAdmin && userRole != domain.RoleBranchManager {
if userRole != domain.RoleSuperAdmin && userRole != domain.RoleAdmin {
a.mongoLoggerSvc.Warn("Attempt to access restricted OnlyBranchMangerAndAbove route",
zap.Int64("userID", userID),
zap.String("role", string(userRole)),

View File

@ -18,7 +18,6 @@ func (a *App) initAppRoutes() {
a.settingSvc,
a.NotidicationStore,
a.validator,
a.referralSvc,
a.recommendationSvc,
a.userSvc,
a.transactionSvc,
@ -70,20 +69,12 @@ func (a *App) initAppRoutes() {
// Get S
groupV1.Get("/tenant", a.authMiddleware, h.GetTenantSlugByToken)
//Direct_deposit
// groupV1.Post("/direct-deposits", a.authMiddleware, h.CreateDirectDeposit)
// groupV1.Post("/direct-deposits/:depositID/approve", a.authMiddleware, h.ApproveDirectDeposit)
// groupV1.Post("/direct-deposits/:depositID/reject", a.authMiddleware, h.RejectDirectDeposit)
// groupV1.Get("/direct-deposits", a.authMiddleware, h.GetDirectDepositsByStatus)
// groupV1.Get("/direct-deposits/:depositID", a.authMiddleware, h.GetDirectDepositByID)
// groupV1.Delete("/direct-deposits/:depositID", a.authMiddleware, h.DeleteDirectDeposit)
// Swagger
a.fiber.Get("/swagger/*", fiberSwagger.FiberWrapHandler())
groupV1.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "FortuneBet API V1",
"message": "Welcome to Yimaru Backend API v1",
"version": "1.0.1",
})
})
@ -128,23 +119,7 @@ func (a *App) initAppRoutes() {
// groupV1.Post("/arifpay/b2c/transfer", a.authMiddleware, h.ExecuteArifpayB2CTransfer)
// groupV1.Post("/arifpay/transaction-id/verify-transaction", a.authMiddleware, h.ArifpayVerifyByTransactionIDHandler)
// groupV1.Get("/arifpay/session-id/verify-transaction/:session_id", a.authMiddleware, h.ArifpayVerifyBySessionIDHandler)
// groupV1.Get("/arifpay/payment-methods", a.authMiddleware, h.GetArifpayPaymentMethodsHandler)
//Telebirr
// groupV1.Post("/telebirr/init-payment", a.authMiddleware, h.CreateTelebirrPaymentHandler)
// groupV1.Post("/telebirr/callback", h.HandleTelebirrCallback)
//Santimpay
// groupV1.Post("/santimpay/init-payment", a.authMiddleware, h.InititateSantimPayPaymentHandler)
// groupV1.Post("/santimpay/callback", h.ProcessSantimPayCallbackHandler)
// groupV1.Post("/santimpay/direct-payment", a.authMiddleware, h.ProcessSantimPayDirectPaymentHandler)
// groupV1.Get("/santimpay/b2c/partners", h.GetSantimPayB2CPartnersHandler)
// groupV1.Post("/santimpay/b2c/withdraw", a.authMiddleware, h.ProcessSantimPayB2CWithdrawalHandler)
// groupV1.Post("/santimpay/transaction/verify", a.authMiddleware, h.CheckSantimPayTransactionStatusHandler)
// groupV1.Post("/arifpay/b2c/transfer", a.authMiddleware, h.B2CTransferHandler)
// groupV1.Post("/arifpay/transaction-id/verify-transaction", a.authMiddleware, h.ArifpayVerifyByTransactionIDHandler)
// groupV1.Get("/arifpay/session-id/verify-transaction/:session_id", a.authMiddleware, h.ArifpayVerifyBySessionIDHandler)
// groupV1.Get("/arifpay/payment-methods", a.authMiddleware, h.GetArifpayPaymentMethodsHandler
// User Routes
groupV1.Post("/user/resetPassword", h.ResetPassword)
@ -159,272 +134,26 @@ func (a *App) initAppRoutes() {
groupV1.Get("/user/admin-profile", a.authMiddleware, h.AdminProfile)
tenant.Get("/user/customer-profile", a.authMiddleware, h.CustomerProfile)
// tenant.Get("/user/bets", a.authMiddleware, h.GetBetByUserID)
groupV1.Get("/user/single/:id", a.authMiddleware, h.GetUserByID)
groupV1.Post("/user/suspend", a.authMiddleware, h.UpdateUserSuspend)
groupV1.Delete("/user/delete/:id", a.authMiddleware, h.DeleteUser)
groupV1.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone)
// tenant.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet)
// Referral Routes
tenant.Post("/referral/create", a.authMiddleware, h.CreateReferralCode)
tenant.Get("/referral/code", a.authMiddleware, h.GetReferralCode)
tenant.Get("/referral/stats", a.authMiddleware, h.GetReferralStats)
// groupV1.Post("/referral/settings", a.authMiddleware, h.CreateReferralSettings)
// groupV1.Get("/referral/settings", a.authMiddleware, h.GetReferralSettings)
// groupV1.Patch("/referral/settings", a.authMiddleware, h.UpdateReferralSettings)
// Raffle Routes
// tenant.Get("/raffle/list", h.GetTenantRaffles)
// a.fiber.Get("/raffle/standing/:id/:limit", h.GetRaffleStanding) //This needs to be accessible by non-login user
// a.fiber.Post("/raffle/create", a.authMiddleware, h.CreateRaffle)
// a.fiber.Post("/raffle/add-filter", a.authMiddleware, h.AddRaffleFilter)
// a.fiber.Get("/raffle/delete/:id", a.authMiddleware, h.DeleteRaffle)
// a.fiber.Get("/raffle/company/:id", a.authMiddleware, h.GetRafflesOfCompany)
// a.fiber.Get("raffle/winners/:id/:limit", a.authMiddleware, h.GetRaffleWinners)
// a.fiber.Post("/raffle-ticket/create", a.authMiddleware, h.CreateRaffleTicket)
// a.fiber.Get("/raffle-ticket/:id", a.authMiddleware, h.GetUserRaffleTickets)
// a.fiber.Get("/raffle-ticket/suspend/:id", a.authMiddleware, h.SuspendRaffleTicket)
// a.fiber.Get("/raffle-ticket/unsuspend/:id", a.authMiddleware, h.UnSuspendRaffleTicket)
// Bonus Routes
// tenant.Get("/bonus", a.authMiddleware, h.GetBonusesByUserID)
// tenant.Get("/bonus/stats", a.authMiddleware, h.GetBonusStats)
// tenant.Post("/bonus/claim/:id", a.authMiddleware, h.ClaimBonus)
// groupV1.Post("/bonus/create", a.authMiddleware, h.CreateBonusMultiplier)
// groupV1.Put("/bonus/update", a.authMiddleware, h.UpdateBonusMultiplier)
groupV1.Get("/cashiers", a.authMiddleware, a.CompanyOnly, h.GetAllCashiers)
groupV1.Get("/cashiers/:id", a.authMiddleware, a.CompanyOnly, h.GetCashierByID)
groupV1.Post("/cashiers", a.authMiddleware, a.CompanyOnly, h.CreateCashier)
groupV1.Put("/cashiers/:id", a.authMiddleware, a.CompanyOnly, h.UpdateCashier)
// tenant.Get("/customer", a.authMiddleware, a.CompanyOnly, h.GetAllTenantCustomers)
// tenant.Get("/customer/:id", a.authMiddleware, a.CompanyOnly, h.GetTenantCustomerByID)
// tenant.Put("/customer/:id", a.authMiddleware, a.CompanyOnly, h.UpdateTenantCustomer)
// // tenant.Get("/customer/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetTenantCustomerBets)
// groupV1.Get("/customer", a.authMiddleware, a.SuperAdminOnly, h.GetAllCustomers)
// groupV1.Get("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCustomerByID)
// groupV1.Put("/customer/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCustomer)
// groupV1.Get("/customer/:id/bets", a.authMiddleware, h.GetCustomerBets)
groupV1.Get("/admin", a.authMiddleware, a.SuperAdminOnly, h.GetAllAdmins)
groupV1.Get("/admin/:id", a.authMiddleware, a.SuperAdminOnly, h.GetAdminByID)
groupV1.Post("/admin", a.authMiddleware, a.SuperAdminOnly, h.CreateAdmin)
groupV1.Put("/admin/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateAdmin)
groupV1.Get("/t-approver", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllTransactionApprovers)
groupV1.Get("/t-approver/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.GetTransactionApproverByID)
groupV1.Post("/t-approver", a.authMiddleware, a.OnlyAdminAndAbove, h.CreateTransactionApprover)
groupV1.Put("/t-approver/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateTransactionApprover)
// groupV1.Get("/t-approver", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllTransactionApprovers)
// groupV1.Get("/t-approver/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.GetTransactionApproverByID)
// groupV1.Post("/t-approver", a.authMiddleware, a.OnlyAdminAndAbove, h.CreateTransactionApprover)
// groupV1.Put("/t-approver/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateTransactionApprover)
groupV1.Get("/managers", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllManagers)
groupV1.Get("/managers/:id", a.authMiddleware, h.GetManagerByID)
groupV1.Post("/managers", a.authMiddleware, a.OnlyAdminAndAbove, h.CreateManager)
groupV1.Put("/managers/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateManagers)
// groupV1.Get("/manager/:id/branch", a.authMiddleware, a.OnlyAdminAndAbove, h.GetBranchByManagerID)
// groupV1.Get("/odds", a.authMiddleware, a.SuperAdminOnly, h.GetAllOdds)
// groupV1.Get("/odds/upcoming/:upcoming_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByUpcomingID)
// groupV1.Get("/odds/upcoming/:upcoming_id/market/:market_id", a.authMiddleware, a.SuperAdminOnly, h.GetOddsByMarketID)
// groupV1.Post("/odds/settings", a.authMiddleware, a.SuperAdminOnly, h.SaveOddSettings)
// groupV1.Get("/odds/market-settings", a.authMiddleware, a.SuperAdminOnly, h.GetAllGlobalMarketSettings)
// groupV1.Put("/odds/bet-outcome/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateAllBetOutcomeStatusByOddID)
// groupV1.Put("/odds/bet-outcome", a.authMiddleware, a.SuperAdminOnly, h.BulkUpdateAllBetOutcomeStatusByOddID)
// tenant.Get("/odds", h.GetAllTenantOdds)
// tenant.Get("/odds/upcoming/:upcoming_id", h.GetTenantOddsByUpcomingID)
// tenant.Get("/odds/upcoming/:upcoming_id/market/:market_id", h.GetTenantOddsByMarketID)
// tenant.Post("/odds/settings", a.authMiddleware, a.CompanyOnly, h.SaveTenantOddsSetting)
// tenant.Delete("/odds/settings/:id", a.authMiddleware, a.CompanyOnly, h.RemoveOddsSettings)
// tenant.Delete("/odds/settings", a.authMiddleware, a.CompanyOnly, h.RemoveAllOddsSettings)
// tenant.Post("/odds/market-settings", a.authMiddleware, a.CompanyOnly, h.InsertCompanyMarketSettings)
// tenant.Get("/odds/market-settings", a.authMiddleware, a.CompanyOnly, h.GetAllTenantMarketSettings)
// tenant.Delete("/odds/market-settings", a.authMiddleware, a.CompanyOnly, h.DeleteAllCompanyMarketSettings)
// tenant.Delete("/odds/market-settings/:id", a.authMiddleware, a.CompanyOnly, h.DeleteCompanyMarketSettings)
// groupV1.Get("/events", h.GetAllEvents)
// groupV1.Get("/events/:id", a.authMiddleware, h.GetEventByID)
// groupV1.Delete("/events/:id", a.authMiddleware, a.SuperAdminOnly, h.SetEventStatusToRemoved)
// groupV1.Patch("/events/:id/is_monitored", a.authMiddleware, a.SuperAdminOnly, h.SetEventIsMonitored)
// groupV1.Put("/events/:id/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalSettingList)
// groupV1.Get("/events/:id/bets", a.authMiddleware, a.SuperAdminOnly, h.GetBetsByEventID)
// tenant.Get("/upcoming-events", h.GetTenantUpcomingEvents)
// tenant.Get("/top-leagues", h.GetTopLeagues)
// tenant.Get("/events", h.GetTenantEvents)
// tenant.Get("/events/:id", h.GetTenantEventByID)
// tenant.Put("/events/:id/settings", a.authMiddleware, a.CompanyOnly, h.UpdateTenantEventSettings)
// tenant.Get("/events/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetTenantBetsByEventID)
//EnetPulse
// groupV1.Get("/odds/pre-match", h.GetPreMatchOdds)
// groupV1.Get("/sports", h.GetAllSports)
// groupV1.Get("/tournament_templates", h.GetAllTournamentTemplates)
// groupV1.Get("/tournaments", h.GetAllTournaments)
// groupV1.Get("/tournament_stages", h.GetAllTournamentStages)
// groupV1.Get("/fixtures", h.GetFixturesByDate)
// groupV1.Get("/results", h.GetAllResults)
// groupV1.Get("/preodds", h.GetAllPreoddsWithBettingOffers)
// groupV1.Get("/bettingoffers", h.GetAllBettingOffers)
// groupV1.Get("/fixtures/preodds", h.GetFixturesWithPreodds)
// // Leagues
// groupV1.Get("/leagues", a.authMiddleware, a.SuperAdminOnly, h.GetAllLeagues)
// groupV1.Put("/leagues/:id/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalLeagueSetting)
// tenant.Get("/leagues", h.GetAllLeagues)
// tenant.Put("/leagues/:id/featured", a.authMiddleware, a.CompanyOnly, h.SetLeagueFeatured)
// tenant.Put("/leagues/:id/set-active", a.authMiddleware, a.CompanyOnly, h.SetLeagueActive)
// groupV1.Get("/result/b365/:id", h.GetBet365ResultsByEventID)
// Branch
// groupV1.Post("/branch", a.authMiddleware, a.CompanyOnly, h.CreateBranch)
// groupV1.Get("/branch", a.authMiddleware, a.CompanyOnly, h.GetAllBranches)
// groupV1.Get("/branch/:id", a.authMiddleware, a.CompanyOnly, h.GetBranchByID)
// groupV1.Post("/branch/:id/return", a.authMiddleware, a.CompanyOnly, h.ReturnBranchWallet)
// // groupV1.Get("/branch/:id/bets", a.authMiddleware, a.CompanyOnly, h.GetBetByBranchID)
// groupV1.Put("/branch/:id", a.authMiddleware, a.CompanyOnly, h.UpdateBranch)
// groupV1.Put("/branch/:id/set-active", a.authMiddleware, a.CompanyOnly, h.UpdateBranchStatus)
// groupV1.Put("/branch/:id/set-inactive", a.authMiddleware, a.CompanyOnly, h.UpdateBranchStatus)
// groupV1.Delete("/branch/:id", a.authMiddleware, a.CompanyOnly, h.DeleteBranch)
// groupV1.Get("/search/branch", a.authMiddleware, a.CompanyOnly, h.SearchBranch)
// groupV1.Get("/branchLocation", a.authMiddleware, a.CompanyOnly, h.GetAllBranchLocations)
// groupV1.Get("/branch/:id/cashiers", a.authMiddleware, a.CompanyOnly, h.GetBranchCashiers)
// groupV1.Get("/branchCashier", a.authMiddleware, a.CompanyOnly, h.GetBranchForCashier)
// // Branch Operation
// groupV1.Get("/supportedOperation", a.authMiddleware, h.GetAllSupportedOperations)
// groupV1.Post("/supportedOperation", a.authMiddleware, a.SuperAdminOnly, h.CreateSupportedOperation)
// groupV1.Post("/operation", a.authMiddleware, a.CompanyOnly, h.CreateBranchOperation)
// groupV1.Get("/branch/:id/operation", a.authMiddleware, a.CompanyOnly, h.GetBranchOperations)
// groupV1.Delete("/branch/:id/operation/:opID", a.authMiddleware, a.CompanyOnly, h.DeleteBranchOperation)
// // Company
// groupV1.Post("/company", a.authMiddleware, a.SuperAdminOnly, h.CreateCompany)
// groupV1.Get("/company", a.authMiddleware, a.SuperAdminOnly, h.GetAllCompanies)
// groupV1.Get("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCompanyByID)
// groupV1.Put("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCompany)
// groupV1.Delete("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteCompany)
// groupV1.Get("/company/:id/branch", a.authMiddleware, a.CompanyOnly, h.GetBranchByCompanyID)
// groupV1.Get("/search/company", a.authMiddleware, a.CompanyOnly, h.SearchCompany)
// groupV1.Get("/admin-company", a.authMiddleware, a.CompanyOnly, h.GetCompanyForAdmin)
// groupV1.Get("/ticket", h.GetAllTickets)
// groupV1.Get("/ticket/:id", h.GetTicketByID)
// Ticket Routes
// tenant.Post("/ticket", h.CreateTenantTicket)
// tenant.Get("/ticket", h.GetAllTenantTickets)
// tenant.Get("/ticket/:id", h.GetTenantTicketByID)
// Bet Routes
// tenant.Post("/sport/bet", a.authMiddleware, h.CreateBet)
// tenant.Post("/sport/bet/fastcode", a.authMiddleware, h.CreateBetWithFastCode)
// tenant.Get("/sport/bet/fastcode/:fast_code", h.GetBetByFastCode)
// tenant.Get("/sport/bet", a.authMiddleware, a.CompanyOnly, h.GetAllTenantBets)
// tenant.Get("/sport/bet/:id", a.authMiddleware, h.GetTenantBetByID)
// tenant.Patch("/sport/bet/:id", a.authMiddleware, h.UpdateCashOut)
// tenant.Delete("/sport/bet/:id", a.authMiddleware, h.DeleteTenantBet)
// groupV1.Get("/sport/bet/:id", a.authMiddleware, a.CompanyOnly, h.GetBetByID)
// groupV1.Get("/sport/bet", a.authMiddleware, a.SuperAdminOnly, h.GetAllBet)
// groupV1.Delete("/sport/bet/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteBet)
// tenant.Post("/sport/random/bet", a.authMiddleware, h.RandomBet)
// // Wallet
// groupV1.Get("/wallet", h.GetAllWallets)
// groupV1.Get("/wallet/:id", h.GetWalletByID)
// groupV1.Put("/wallet/:id", h.UpdateWalletActive)
// groupV1.Get("/branchWallet", a.authMiddleware, h.GetAllBranchWallets)
// groupV1.Get("/customerWallet", a.authMiddleware, h.GetAllCustomerWallets)
// groupV1.Get("/cashierWallet", a.authMiddleware, h.GetWalletForCashier)
// Transfer
// /transfer/wallet - transfer from one wallet to another wallet
// groupV1.Post("/transfer/wallet/:id", a.authMiddleware, h.TransferToWallet)
// groupV1.Get("/transfer/wallet/:id", a.authMiddleware, h.GetTransfersByWallet)
// groupV1.Post("/transfer/refill/:id", a.authMiddleware, h.RefillWallet)
//Chapa Routes
// groupV1.Post("/chapa/payments/webhook/verify", h.WebhookCallback)
// groupV1.Get("/chapa/transaction/manual/verify/:tx_ref", a.authMiddleware, h.ManualVerifyTransaction)
// groupV1.Put("/chapa/transaction/cancel/:tx_ref", a.authMiddleware, h.CancelDeposit)
// groupV1.Get("/chapa/transactions", a.authMiddleware, h.FetchAllTransactions)
// groupV1.Get("/chapa/transaction/events/:ref_id", a.authMiddleware, h.GetTransactionEvents)
// groupV1.Post("/chapa/payments/deposit", a.authMiddleware, h.InitiateDeposit)
// groupV1.Post("/chapa/payments/withdraw", a.authMiddleware, h.InitiateWithdrawal)
// groupV1.Get("/chapa/banks", h.GetSupportedBanks)
// groupV1.Get("/chapa/payments/receipt/:chapa_ref", a.authMiddleware, h.GetPaymentReceipt)
// groupV1.Get("/chapa/transfers", a.authMiddleware, h.GetAllTransfers)
// groupV1.Get("/chapa/balance", a.authMiddleware, h.GetAccountBalance)
// groupV1.Post("/chapa/swap", a.authMiddleware, h.SwapCurrency)
// Currencies
groupV1.Get("/currencies", h.GetSupportedCurrencies)
groupV1.Get("/currencies/convert", h.ConvertCurrency)
//Report Routes
// groupV1.Get("/reports/dashboard", a.authMiddleware, a.OnlyAdminAndAbove, h.GetDashboardReport)
// groupV1.Get("/report-files/download/:filename", h.DownloadReportFile)
// groupV1.Get("/report-files/list", a.authMiddleware, a.OnlyAdminAndAbove, h.ListReportFiles)
// groupV1.Post("/reports/requests", a.authMiddleware, a.OnlyAdminAndAbove, h.CreateReportRequest)
// groupV1.Get("/reports/requests", a.authMiddleware, a.OnlyAdminAndAbove, h.GetAllReportRequests)
// groupV1.Get("/reports/download/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.DownloadReportByID)
//Alea Play Virtual Game Routes
// groupV1.Get("/alea-play/launch", a.authMiddleware, h.LaunchAleaGame)
// groupV1.Post("/webhooks/alea-play", a.authMiddleware, h.HandleAleaCallback)
// //Veli Virtual Game Routes
// groupV1.Post("/veli/providers", h.GetProviders)
// groupV1.Post("/veli/games-list", h.GetGamesByProvider)
// groupV1.Post("/veli/start-game", a.authMiddleware, h.StartGame)
// groupV1.Post("/veli/start-demo-game", h.StartDemoGame)
// a.fiber.Post("/balance", h.GetBalance)
// groupV1.Post("/veli/gaming-activity", a.authMiddleware, h.GetGamingActivity)
// groupV1.Post("/veli/huge-wins", a.authMiddleware, h.GetHugeWins)
// groupV1.Post("/veli/credit-balances", a.authMiddleware, h.GetCreditBalances)
// //Atlas Virtual Game Routes
// groupV1.Get("/atlas/games", h.GetAtlasVGames)
// groupV1.Post("/atlas/init-game", a.authMiddleware, h.InitAtlasGame)
// a.fiber.Post("/account", h.AtlasGetUserDataCallback)
// a.fiber.Post("/betwin", h.HandleAtlasBetWin)
// a.fiber.Post("/result", h.HandleRoundResult)
// a.fiber.Post("/rollback", h.HandleRollback)
// a.fiber.Post("/freespin", h.FreeSpinResultCallback)
// a.fiber.Post("/jackpot", h.JackpotCallback)
// groupV1.Post("/atlas/freespin", a.authMiddleware, h.CreateFreeSpin)
//mongoDB logs
groupV1.Get("/logs", a.authMiddleware, a.SuperAdminOnly, handlers.GetLogsHandler(context.Background()))
// Recommendation Routes
// group.Get("/virtual-games/recommendations/:userID", h.GetRecommendations)
// Transactions /shop/transactions
// groupV1.Post("/shop/bet", a.authMiddleware, a.CompanyOnly, h.CreateShopBet)
// groupV1.Get("/shop/bet", a.authMiddleware, a.CompanyOnly, h.GetAllShopBets)
// groupV1.Get("/shop/bet/:id", a.authMiddleware, a.CompanyOnly, h.GetShopBetByBetID)
// groupV1.Post("/shop/bet/:id/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutBet)
// groupV1.Post("/shop/bet/:id/generate", a.authMiddleware, a.CompanyOnly, h.CashoutBet)
// groupV1.Get("/shop/cashout/:id", a.authMiddleware, a.CompanyOnly, h.GetShopBetByCashoutID)
// groupV1.Post("/shop/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutByCashoutID)
// groupV1.Post("/shop/deposit", a.authMiddleware, a.CompanyOnly, h.DepositForCustomer)
// groupV1.Get("/shop/deposit", a.authMiddleware, a.CompanyOnly, h.DepositForCustomer)
// groupV1.Get("/shop/transaction", a.authMiddleware, a.CompanyOnly, h.GetAllTransactions)
// groupV1.Get("/shop/transaction/:id", a.authMiddleware, a.CompanyOnly, h.GetTransactionByID)
// groupV1.Get("/shop/transaction/:id/bet", a.authMiddleware, a.CompanyOnly, h.GetShopBetByTransactionID)
@ -434,33 +163,10 @@ func (a *App) initAppRoutes() {
groupV1.Get("/ws/connect", a.WebsocketAuthMiddleware, h.ConnectSocket)
groupV1.Get("/notifications", a.authMiddleware, h.GetUserNotification)
groupV1.Get("/notifications/all", a.authMiddleware, h.GetAllNotifications)
groupV1.Post("/notifications/mark-as-read", a.authMiddleware, h.MarkNotificationAsRead)
// groupV1.Post("/notifications/mark-as-read", a.authMiddleware, h.MarkNotificationAsRead)
groupV1.Get("/notifications/unread", a.authMiddleware, h.CountUnreadNotifications)
groupV1.Post("/notifications/create", a.authMiddleware, h.CreateAndSendNotification)
// Virtual Game Routes
// a.fiber.Post("/virtual-game/launch", a.authMiddleware, h.LaunchVirtualGame)
// a.fiber.Post("/virtual-game/callback", h.HandleVirtualGameCallback)
// a.fiber.Post("/playerInfo", h.HandlePlayerInfo)
// a.fiber.Post("/bet", h.HandleBet)
// a.fiber.Post("/win", h.HandleWin)
// a.fiber.Post("/cancel", h.HandleCancel)
// a.fiber.Post("/promoWin ", h.HandlePromoWin)
// a.fiber.Post("/tournamentWin ", h.HandleTournamentWin)
// a.fiber.Get("/popok/games", h.GetGameList)
// a.fiber.Get("/popok/games/recommend", a.authMiddleware, h.RecommendGames)
// groupV1.Post("/virtual-game/favorites", a.authMiddleware, h.AddFavorite)
// groupV1.Delete("/virtual-game/favorites/:gameID", a.authMiddleware, h.RemoveFavorite)
// groupV1.Get("/virtual-game/favorites", a.authMiddleware, h.ListFavorites)
// groupV1.Get("/orchestrator/virtual-game/provider-reports/asc", a.OnlyAdminAndAbove, h.ListVirtualGameProviderReportsAscHandler)
// groupV1.Get("/orchestrator/virtual-game/provider-reports/desc", a.OnlyAdminAndAbove, h.ListVirtualGameProviderReportsDescHandler)
// groupV1.Delete("/virtual-game/orchestrator/providers/:provideID", a.authMiddleware, h.RemoveProvider)
// groupV1.Get("/virtual-game/orchestrator/providers/:provideID", a.authMiddleware, h.GetProviderByID)
// groupV1.Get("/virtual-game/orchestrator/games", h.ListVirtualGames)
// groupV1.Get("/virtual-game/orchestrator/providers", h.ListProviders)
// groupV1.Patch("/virtual-game/orchestrator/providers/:provideID/status", a.authMiddleware, h.SetProviderEnabled)
//Issue Reporting Routes
// groupV1.Post("/issues", a.authMiddleware, h.CreateIssue) //anyone who has logged can report a
// groupV1.Get("/issues/customer/:customer_id", a.authMiddleware, a.OnlyAdminAndAbove, h.GetUserIssues)
@ -473,12 +179,4 @@ func (a *App) initAppRoutes() {
groupV1.Get("/settings/:key", a.authMiddleware, a.SuperAdminOnly, h.GetGlobalSettingByKey)
groupV1.Put("/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalSettingList)
tenant.Get("/settings", a.authMiddleware, a.OnlyAdminAndAbove, h.GetCompanySettingList)
tenant.Put("/settings", a.authMiddleware, a.OnlyAdminAndAbove, h.SaveCompanySettingList)
tenant.Delete("/settings/:key", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteCompanySetting)
tenant.Delete("/settings", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteAllCompanySetting)
// groupV1.Get("/stats/total/events", h.GetTotalEventStats)
// groupV1.Get("/stats/interval/events", h.GetTotalEventStatsByInterval)
}

View File

@ -10,7 +10,7 @@ coverage:
@mkdir -p coverage
@docker compose up -d test
@docker compose exec test sh -c "go test -coverprofile=coverage.out ./internal/... && go tool cover -func=coverage.out -o coverage/coverage.txt"
@docker cp $(shell docker ps -q -f "name=fortunebet-test-1"):/app/coverage ./ || true
@docker cp $(shell docker ps -q -f "name=yimaru-test-1"):/app/coverage ./ || true
@docker compose stop test
.PHONY: build
@ -46,45 +46,45 @@ postgres:
.PHONY: backup
backup:
@mkdir -p backup
@docker exec -t fortunebet-backend-postgres-1 pg_dump -U root --data-only --exclude-table=schema_migrations gh | gzip > backup/dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.gz
@docker exec -t yimaru-backend-postgres-1 pg_dump -U root --data-only --exclude-table=schema_migrations gh | gzip > backup/dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.gz
restore:
@echo "Restoring latest backup..."
@latest_file=$$(ls -t backup/dump_*.sql.gz | head -n 1); \
echo "Restoring from $$latest_file"; \
gunzip -c $$latest_file | docker exec -i fortunebet-backend-postgres-1 psql -U root -d gh
gunzip -c $$latest_file | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh
restore_file:
@echo "Restoring latest backup..."
gunzip -c $(file) | docker exec -i fortunebet-backend-postgres-1 psql -U root -d gh
gunzip -c $(file) | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh
.PHONY: seed_data
seed_data:
@echo "Waiting for PostgreSQL to be ready..."
@until docker exec fortunebet-backend-postgres-1 pg_isready -U root -d gh; do \
@until docker exec yimaru-backend-postgres-1 pg_isready -U root -d gh; do \
echo "PostgreSQL is not ready yet..."; \
sleep 1; \
done
@for file in db/data/*.sql; do \
echo "Seeding $$file..."; \
cat $$file | docker exec -i fortunebet-backend-postgres-1 psql -U root -d gh; \
cat $$file | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh; \
done
.PHONY: seed_dev_data
seed_dev_data:
@echo "Waiting for PostgreSQL to be ready..."
@until docker exec fortunebet-backend-postgres-1 pg_isready -U root -d gh; do \
@until docker exec yimaru-backend-postgres-1 pg_isready -U root -d gh; do \
echo "PostgreSQL is not ready yet..."; \
sleep 1; \
done
cat db/scripts/fix_autoincrement_desync.sql | docker exec -i fortunebet-backend-postgres-1 psql -U root -d gh;
cat db/scripts/fix_autoincrement_desync.sql | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh;
@for file in db/dev_data/*.sql; do \
if [ -f "$$file" ]; then \
echo "Seeding $$file..."; \
cat $$file | docker exec -i fortunebet-backend-postgres-1 psql -U root -d gh; \
cat $$file | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh; \
fi \
done
postgres_log:
docker logs fortunebet-backend-postgres-1
docker logs yimaru-backend-postgres-1
.PHONY: swagger
swagger:
@swag init -g cmd/main.go
@ -94,15 +94,15 @@ logs:
db-up: | logs
@mkdir -p logs
@docker compose up -d postgres migrate mongo
@docker logs fortunebet-backend-postgres-1 > logs/postgres.log 2>&1 &
@docker logs yimaru-backend-postgres-1 > logs/postgres.log 2>&1 &
.PHONY: db-down
db-down:
@docker compose down -v
# @docker volume rm fortunebet-backend_postgres_data
# @docker volume rm yimaru-backend_postgres_data
.PHONY: sqlc-gen
sqlc-gen:
@sqlc generate
app_log:
@mkdir -p app_logs
export_logs: | app_log
@docker exec fortunebet-mongo mongoexport --db=logdb --collection=applogs -u root -p secret --authenticationDatabase=admin --out - > app_logs/log_`date +%Y-%m-%d"_"%H_%M_%S`.json
@docker exec yimaru-mongo mongoexport --db=logdb --collection=applogs -u root -p secret --authenticationDatabase=admin --out - > app_logs/log_`date +%Y-%m-%d"_"%H_%M_%S`.json

108
makefile copy Normal file
View File

@ -0,0 +1,108 @@
include .env
.PHONY: test
test:
@docker compose up -d test
@docker compose exec test go test ./...
@docker compose stop test
.PHONY: coverage
coverage:
@mkdir -p coverage
@docker compose up -d test
@docker compose exec test sh -c "go test -coverprofile=coverage.out ./internal/... && go tool cover -func=coverage.out -o coverage/coverage.txt"
@docker cp $(shell docker ps -q -f "name=yimaru-test-1"):/app/coverage ./ || true
@docker compose stop test
.PHONY: build
build:
@docker compose build app
.PHONY: run
run:
@docker compose up
.PHONY: stop
stop:
@docker compose down
.PHONY: air
air:
@echo "Running air locally (not in Docker)"
@air -c .air.toml
.PHONY: migrations/new
migrations/new:
@echo 'Creating migration files for DB_URL'
@migrate create -seq -ext=.sql -dir=./db/migrations $(name)
.PHONY: migrations/up
migrations/up:
@echo 'Running up migrations...'
@docker compose up migrate
.PHONY: postgres
postgres:
@echo 'Running postgres db...'
docker compose -f docker-compose.yml exec postgres psql -U root -d gh
.PHONY: backup
backup:
@mkdir -p backup
@docker exec -t yimaru-backend-postgres-1 pg_dump -U root --data-only --exclude-table=schema_migrations gh | gzip > backup/dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.gz
restore:
@echo "Restoring latest backup..."
@latest_file=$$(ls -t backup/dump_*.sql.gz | head -n 1); \
echo "Restoring from $$latest_file"; \
gunzip -c $$latest_file | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh
restore_file:
@echo "Restoring latest backup..."
gunzip -c $(file) | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh
.PHONY: seed_data
seed_data:
@echo "Waiting for PostgreSQL to be ready..."
@until docker exec yimaru-backend-postgres-1 pg_isready -U root -d gh; do \
echo "PostgreSQL is not ready yet..."; \
sleep 1; \
done
@for file in db/data/*.sql; do \
echo "Seeding $$file..."; \
cat $$file | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh; \
done
.PHONY: seed_dev_data
seed_dev_data:
@echo "Waiting for PostgreSQL to be ready..."
@until docker exec yimaru-backend-postgres-1 pg_isready -U root -d gh; do \
echo "PostgreSQL is not ready yet..."; \
sleep 1; \
done
cat db/scripts/fix_autoincrement_desync.sql | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh;
@for file in db/dev_data/*.sql; do \
if [ -f "$$file" ]; then \
echo "Seeding $$file..."; \
cat $$file | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh; \
fi \
done
postgres_log:
docker logs yimaru-backend-postgres-1
.PHONY: swagger
swagger:
@swag init -g cmd/main.go
.PHONY: db-up
logs:
@mkdir -p logs
db-up: | logs
@mkdir -p logs
@docker compose up -d postgres migrate mongo
@docker logs yimaru-backend-postgres-1 > logs/postgres.log 2>&1 &
.PHONY: db-down
db-down:
@docker compose down -v
# @docker volume rm yimaru-backend_postgres_data
.PHONY: sqlc-gen
sqlc-gen:
@sqlc generate
app_log:
@mkdir -p app_logs
export_logs: | app_log
@docker exec yimaru-mongo mongoexport --db=logdb --collection=applogs -u root -p secret --authenticationDatabase=admin --out - > app_logs/log_`date +%Y-%m-%d"_"%H_%M_%S`.json