From 47d70b029fb20209064da9a6721aabba9d2a3764 Mon Sep 17 00:00:00 2001 From: Yared Yemane Date: Thu, 18 Dec 2025 18:06:26 +0300 Subject: [PATCH] afro SMS and partial ArifPay Payment Gateway integrations --- cmd/main.go | 95 +- db/data/001_initial_seed_data.sql | 2 +- db/migrations/000001_yimaru.down.sql | 7 +- db/migrations/000001_yimaru.up.sql | 2 +- db/migrations/000002_referal.down.sql | 17 - db/migrations/000002_referal.up.sql | 45 - db/migrations/000003_issue_reporting.down.sql | 2 - db/migrations/000003_issue_reporting.up.sql | 14 - db/query/auth.sql | 11 - db/query/issue_reporting.sql | 74 +- db/query/notification.sql | 169 +- db/query/settings.sql | 65 +- db/query/user.sql | 67 +- docs/docs.go | 3248 +---------------- docs/swagger.json | 3248 +---------------- docs/swagger.yaml | 2174 +---------- gen/db/auth.sql.go | 46 - gen/db/issue_reporting.sql.go | 197 + gen/db/notification.sql.go | 276 ++ gen/db/settings.sql.go | 92 + gen/db/user.sql.go | 122 +- go.mod | 9 +- go.sum | 152 - internal/config/config.go | 2 +- internal/domain/chapa.go | 390 -- internal/domain/direct_deposit.go | 39 - internal/domain/institutions.go | 28 - internal/domain/notification.go | 10 +- internal/domain/referal.go | 186 - internal/domain/role.go | 17 +- internal/domain/setting_list.go | 1336 +++---- internal/domain/settings.go | 70 +- internal/ports/notification.go | 10 +- internal/ports/settings.go | 13 +- internal/ports/user.go | 61 +- internal/repository/issue_reporting.go | 64 + internal/repository/notification.go | 476 +-- internal/repository/settings.go | 168 + internal/repository/user.go | 238 +- internal/services/currency/fetcher.go | 69 - internal/services/currency/service.go | 125 - internal/services/currency/worker.go | 55 - internal/services/issue_reporting/service.go | 92 + internal/services/messenger/email.go | 2 +- internal/services/messenger/sms.go | 28 +- internal/services/notification/service.go | 242 +- internal/services/referal/interface.go | 16 - internal/services/referal/service.go | 260 -- internal/services/settings/service.go | 48 +- internal/services/transaction/service.go | 46 +- internal/services/user/common.go | 4 +- internal/services/user/direct.go | 39 +- internal/services/user/register.go | 26 +- internal/services/user/reset.go | 14 +- internal/services/user/user.go | 25 +- internal/web_server/app.go | 88 +- internal/web_server/handlers/admin.go | 72 +- internal/web_server/handlers/auth_handler.go | 18 +- internal/web_server/handlers/cashier.go | 456 --- internal/web_server/handlers/currency.go | 66 +- internal/web_server/handlers/handlers.go | 18 +- internal/web_server/handlers/institutions.go | 226 +- internal/web_server/handlers/manager.go | 563 ++- .../handlers/notification_handler.go | 78 +- .../web_server/handlers/referal_handlers.go | 114 +- .../web_server/handlers/settings_handler.go | 170 +- .../handlers/transaction_approver.go | 382 +- internal/web_server/handlers/user.go | 194 +- internal/web_server/jwt/jwt.go | 57 +- internal/web_server/jwt/jwt_test.go | 1 - internal/web_server/middleware.go | 4 +- internal/web_server/routes.go | 316 +- makefile | 26 +- makefile copy | 108 + 74 files changed, 3507 insertions(+), 13783 deletions(-) delete mode 100644 db/migrations/000002_referal.down.sql delete mode 100644 db/migrations/000002_referal.up.sql delete mode 100644 db/migrations/000003_issue_reporting.down.sql delete mode 100644 db/migrations/000003_issue_reporting.up.sql create mode 100644 gen/db/issue_reporting.sql.go create mode 100644 gen/db/notification.sql.go create mode 100644 gen/db/settings.sql.go delete mode 100644 internal/domain/chapa.go delete mode 100644 internal/domain/direct_deposit.go delete mode 100644 internal/domain/institutions.go delete mode 100644 internal/domain/referal.go create mode 100644 internal/repository/issue_reporting.go create mode 100644 internal/repository/settings.go delete mode 100644 internal/services/currency/fetcher.go delete mode 100644 internal/services/currency/service.go delete mode 100644 internal/services/currency/worker.go create mode 100644 internal/services/issue_reporting/service.go delete mode 100644 internal/services/referal/interface.go delete mode 100644 internal/services/referal/service.go delete mode 100644 internal/web_server/handlers/cashier.go create mode 100644 makefile copy diff --git a/cmd/main.go b/cmd/main.go index 8b70fe8..245d0ff 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -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, ) diff --git a/db/data/001_initial_seed_data.sql b/db/data/001_initial_seed_data.sql index 0ac7167..a4a5ab1 100644 --- a/db/data/001_initial_seed_data.sql +++ b/db/data/001_initial_seed_data.sql @@ -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', diff --git a/db/migrations/000001_yimaru.down.sql b/db/migrations/000001_yimaru.down.sql index a29d002..4a6d42a 100644 --- a/db/migrations/000001_yimaru.down.sql +++ b/db/migrations/000001_yimaru.down.sql @@ -1,3 +1,8 @@ +-- ========================================= +-- Notifications +-- ========================================= +DROP TABLE IF EXISTS global_settings; + -- ========================================= -- Notifications -- ========================================= @@ -48,4 +53,4 @@ DROP TABLE IF EXISTS otps; -- ========================================= -- Users -- ========================================= -DROP TABLE IF EXISTS users; +DROP TABLE IF EXISTS users; \ No newline at end of file diff --git a/db/migrations/000001_yimaru.up.sql b/db/migrations/000001_yimaru.up.sql index f2a24ab..6c923a0 100644 --- a/db/migrations/000001_yimaru.up.sql +++ b/db/migrations/000001_yimaru.up.sql @@ -192,4 +192,4 @@ CREATE TABLE IF NOT EXISTS reported_issues ( metadata JSONB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); +); \ No newline at end of file diff --git a/db/migrations/000002_referal.down.sql b/db/migrations/000002_referal.down.sql deleted file mode 100644 index 911d3ed..0000000 --- a/db/migrations/000002_referal.down.sql +++ /dev/null @@ -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; diff --git a/db/migrations/000002_referal.up.sql b/db/migrations/000002_referal.up.sql deleted file mode 100644 index 566a7b4..0000000 --- a/db/migrations/000002_referal.up.sql +++ /dev/null @@ -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) -); diff --git a/db/migrations/000003_issue_reporting.down.sql b/db/migrations/000003_issue_reporting.down.sql deleted file mode 100644 index 59d3f24..0000000 --- a/db/migrations/000003_issue_reporting.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -DROP TABLE IF EXISTS reported_issues; - diff --git a/db/migrations/000003_issue_reporting.up.sql b/db/migrations/000003_issue_reporting.up.sql deleted file mode 100644 index 1b140f4..0000000 --- a/db/migrations/000003_issue_reporting.up.sql +++ /dev/null @@ -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 -); \ No newline at end of file diff --git a/db/query/auth.sql b/db/query/auth.sql index 43a207e..d08e76a 100644 --- a/db/query/auth.sql +++ b/db/query/auth.sql @@ -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); diff --git a/db/query/issue_reporting.sql b/db/query/issue_reporting.sql index 12263d5..1906ffe 100644 --- a/db/query/issue_reporting.sql +++ b/db/query/issue_reporting.sql @@ -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; \ No newline at end of file +-- 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; \ No newline at end of file diff --git a/db/query/notification.sql b/db/query/notification.sql index 1dbf5df..2c8b9b4 100644 --- a/db/query/notification.sql +++ b/db/query/notification.sql @@ -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(); \ No newline at end of file +-- 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; diff --git a/db/query/settings.sql b/db/query/settings.sql index bcac88e..1e2b440 100644 --- a/db/query/settings.sql +++ b/db/query/settings.sql @@ -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; \ No newline at end of file +-- 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; diff --git a/db/query/user.sql b/db/query/user.sql index 6224842..7fc1f42 100644 --- a/db/query/user.sql +++ b/db/query/user.sql @@ -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,29 +83,30 @@ SELECT id, suspended_at, organization_id FROM users -wHERE ( +WHERE ( role = $1 OR $1 IS NULL ) - AND ( + AND ( organization_id = $2 OR $2 IS NULL ) - AND ( + AND ( first_name ILIKE '%' || sqlc.narg('query') || '%' OR last_name ILIKE '%' || sqlc.narg('query') || '%' OR phone_number ILIKE '%' || sqlc.narg('query') || '%' OR sqlc.narg('query') IS NULL ) - AND ( + AND ( created_at > sqlc.narg('created_before') OR sqlc.narg('created_before') IS NULL ) - AND ( + AND ( 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, @@ -239,4 +220,10 @@ WHERE ( SELECT users.* FROM organizations JOIN users ON organizations.owner_id = users.id -WHERE organizations.id = $1; \ No newline at end of file +WHERE organizations.id = $1; +-- name: SuspendUser :exec +UPDATE users +SET suspended = $1, + suspended_at = $2, + updated_at = CURRENT_TIMESTAMP +WHERE id = $3; \ No newline at end of file diff --git a/docs/docs.go b/docs/docs.go index 1e91cf7..0bd3b47 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -79,7 +79,7 @@ const docTemplate = `{ } }, "post": { - "description": "Create transaction approver", + "description": "Create Admin", "consumes": [ "application/json" ], @@ -89,15 +89,15 @@ const docTemplate = `{ "tags": [ "admin" ], - "summary": "Create transaction approver", + "summary": "Create Admin", "parameters": [ { - "description": "Create transaction approver", + "description": "Create admin", "name": "manger", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/handlers.CreateTransactionApproverReq" + "$ref": "#/definitions/handlers.CreateAdminReq" } } ], @@ -129,41 +129,6 @@ const docTemplate = `{ } } }, - "/api/v1/admin-company": { - "get": { - "description": "Gets a single company by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Gets company by id", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetCompanyRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/admin/{id}": { "get": { "description": "Get a single admin by id", @@ -368,1703 +333,6 @@ const docTemplate = `{ } } }, - "/api/v1/banks": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "List all banks with pagination and filtering", - "parameters": [ - { - "type": "integer", - "description": "Filter by country ID", - "name": "country_id", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by active status", - "name": "is_active", - "in": "query" - }, - { - "type": "string", - "description": "Search term for bank name or code", - "name": "search", - "in": "query" - }, - { - "type": "integer", - "default": 1, - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "maximum": 100, - "type": "integer", - "default": 50, - "description": "Items per page", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.InstResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "Create a new bank", - "parameters": [ - { - "description": "Bank Info", - "name": "bank", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.Bank" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/domain.Bank" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/banks/{id}": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "Get a bank by ID", - "parameters": [ - { - "type": "integer", - "description": "Bank ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Bank" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "put": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "Update a bank", - "parameters": [ - { - "type": "integer", - "description": "Bank ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Bank Info", - "name": "bank", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.Bank" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Bank" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "delete": { - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "Delete a bank", - "parameters": [ - { - "type": "integer", - "description": "Bank ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "Deleted successfully", - "schema": { - "type": "string" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/branch": { - "get": { - "description": "Gets all branches", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets all branches", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Create a branch", - "parameters": [ - { - "description": "Creates branch", - "name": "createBranch", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBranchReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}": { - "get": { - "description": "Gets a single branch by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branch by id", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Updates a branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Updates a branch", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update Branch", - "name": "updateBranch", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBranchReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Delete the branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Delete the branch", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}/cashier": { - "get": { - "description": "Gets branch cashiers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branch cashiers", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.GetCashierRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}/operation": { - "get": { - "description": "Gets branch operations", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branch operations", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchOperationRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}/operation/{opID}": { - "delete": { - "description": "Delete the branch operation", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Delete the branch operation", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Branch Operation ID", - "name": "opID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}/return": { - "post": { - "description": "Unassign the branch wallet to company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Unassign the branch wallet to company", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branchCashier": { - "get": { - "description": "Gets branch for cahier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branch for cahier", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branchLocation": { - "get": { - "description": "Gets all branch locations", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets all branch locations", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchLocation" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/cashier/{id}": { - "get": { - "description": "Get a single cashier by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Get cashier by id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.UserProfileRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/cashiers": { - "get": { - "description": "Get all cashiers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Get all cashiers", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.GetCashierRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Create cashier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Create cashier", - "parameters": [ - { - "description": "Create cashier", - "name": "cashier", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateCashierReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/cashiers/{id}": { - "put": { - "description": "Update cashier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Update cashier", - "parameters": [ - { - "type": "integer", - "description": "Cashier ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update cashier", - "name": "cashier", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.updateCashierReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/company": { - "get": { - "description": "Gets all companies", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Gets all companies", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.GetCompanyRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Create a company", - "parameters": [ - { - "description": "Creates company", - "name": "createCompany", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateCompanyReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CompanyRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/company/{id}": { - "get": { - "description": "Gets a single company by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Gets company by id", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetCompanyRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Updates a company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Updates a company", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update Company", - "name": "updateCompany", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateCompanyReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CompanyRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Delete the company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Delete the company", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/company/{id}/branch": { - "get": { - "description": "Gets branches by company id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branches by company id", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/currencies": { - "get": { - "description": "Returns list of supported currencies", - "produces": [ - "application/json" - ], - "tags": [ - "Multi-Currency" - ], - "summary": "Get supported currencies", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "integer" - } - } - } - } - ] - } - } - } - } - }, - "/api/v1/currencies/convert": { - "get": { - "description": "Converts amount from one currency to another", - "produces": [ - "application/json" - ], - "tags": [ - "Multi-Currency" - ], - "summary": "Convert currency", - "parameters": [ - { - "type": "string", - "description": "Source currency code (e.g., USD)", - "name": "from", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "Target currency code (e.g., ETB)", - "name": "to", - "in": "query", - "required": true - }, - { - "type": "number", - "description": "Amount to convert", - "name": "amount", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "number" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/customer": { - "get": { - "description": "Get all Customers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get all Customers", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/customer/{id}": { - "get": { - "description": "Get a single customer by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get customer by id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Update Customers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Update Customers", - "parameters": [ - { - "description": "Update Customers", - "name": "Customers", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.updateCustomerReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/issues": { - "get": { - "description": "Admin endpoint to list all reported issues with pagination", - "produces": [ - "application/json" - ], - "tags": [ - "Issues" - ], - "summary": "Get all reported issues", - "parameters": [ - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ReportedIssue" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "post": { - "description": "Allows a customer to report a new issue related to the betting platform", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Issues" - ], - "summary": "Report an issue", - "parameters": [ - { - "description": "Issue to report", - "name": "issue", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ReportedIssue" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/domain.ReportedIssue" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/issues/user/{user_id}": { - "get": { - "description": "Returns all issues reported by a specific user", - "produces": [ - "application/json" - ], - "tags": [ - "Issues" - ], - "summary": "Get reported issues by a user", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "user_id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ReportedIssue" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/issues/{issue_id}": { - "delete": { - "description": "Admin endpoint to delete a reported issue", - "tags": [ - "Issues" - ], - "summary": "Delete a reported issue", - "parameters": [ - { - "type": "integer", - "description": "Issue ID", - "name": "issue_id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/issues/{issue_id}/status": { - "patch": { - "description": "Admin endpoint to update the status of a reported issue", - "consumes": [ - "application/json" - ], - "tags": [ - "Issues" - ], - "summary": "Update issue status", - "parameters": [ - { - "type": "integer", - "description": "Issue ID", - "name": "issue_id", - "in": "path", - "required": true - }, - { - "description": "New issue status (pending, in_progress, resolved, rejected)", - "name": "status", - "in": "body", - "required": true, - "schema": { - "type": "object", - "properties": { - "status": { - "type": "string" - } - } - } - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/logs": { "get": { "description": "Fetches application logs from MongoDB with pagination, level filtering, and search", @@ -2125,207 +393,7 @@ const docTemplate = `{ } } }, - "/api/v1/manager/{id}/branch": { - "get": { - "description": "Gets a branches by manager id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branches by manager id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/managers": { - "get": { - "description": "Get all Managers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "manager" - ], - "summary": "Get all Managers", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.ManagersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Create Manager", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "manager" - ], - "summary": "Create Manager", - "parameters": [ - { - "description": "Create manager", - "name": "manger", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateManagerReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/managers/{id}": { - "get": { - "description": "Get a single manager by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "manager" - ], - "summary": "Get manager by id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.ManagersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, "put": { "description": "Update Managers", "consumes": [ @@ -2377,137 +445,6 @@ const docTemplate = `{ } } }, - "/api/v1/operation": { - "post": { - "description": "Creates a operation", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Create a operation", - "parameters": [ - { - "description": "Creates operation", - "name": "createBranchOperation", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBranchOperationReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchOperationRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/search/branch": { - "get": { - "description": "Search branches by name or location", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Search branches", - "parameters": [ - { - "type": "string", - "description": "Search query", - "name": "q", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/search/company": { - "get": { - "description": "Gets all companies", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Gets all companies", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CompanyRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/super-login": { "post": { "description": "Login super-admin", @@ -2560,143 +497,6 @@ const docTemplate = `{ } } }, - "/api/v1/supportedOperation": { - "get": { - "description": "Gets all supported operations", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets all supported operations", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a supported operation", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Create a supported operation", - "parameters": [ - { - "description": "Creates supported operation", - "name": "createSupportedOperation", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateSupportedOperationReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.SupportedOperationRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/t-approver": { - "get": { - "description": "Get all Admins", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "Get all Admins", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.AdminRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/t-approver/{id}": { "get": { "description": "Get a single admin by id", @@ -2843,201 +643,6 @@ const docTemplate = `{ } } }, - "/api/v1/tenant/{tenant_slug}/customer": { - "get": { - "description": "Get all Customers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get all Customers", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/tenant/{tenant_slug}/customer/{id}": { - "get": { - "description": "Get a single customer by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get customer by id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Update Customers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Update Customers", - "parameters": [ - { - "description": "Update Customers", - "name": "Customers", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.updateCustomerReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/tenant/{tenant_slug}/referral/stats": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "description": "Retrieves referral statistics for the authenticated user", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "referral" - ], - "summary": "Get referral statistics", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ReferralStats" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/user/delete/{id}": { "delete": { "description": "Delete a user by their ID", @@ -3778,341 +1383,6 @@ const docTemplate = `{ } }, "definitions": { - "domain.Bank": { - "type": "object", - "properties": { - "acct_length": { - "type": "integer" - }, - "active": { - "type": "integer" - }, - "bank_logo": { - "description": "URL or base64", - "type": "string" - }, - "country_id": { - "type": "integer" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "is_24hrs": { - "description": "nullable", - "type": "integer" - }, - "is_active": { - "type": "integer" - }, - "is_mobilemoney": { - "description": "nullable", - "type": "integer" - }, - "is_rtgs": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "slug": { - "type": "string" - }, - "swift": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.BranchDetailRes": { - "type": "object", - "properties": { - "balance": { - "type": "number", - "example": 100.5 - }, - "branch_manager_id": { - "type": "integer", - "example": 1 - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "company_name": { - "type": "string", - "example": "fortune" - }, - "deducted_stake": { - "type": "number" - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": false - }, - "is_self_owned": { - "type": "boolean", - "example": false - }, - "is_wallet_active": { - "type": "boolean", - "example": false - }, - "location": { - "type": "string", - "example": "Addis Ababa" - }, - "manager_name": { - "type": "string", - "example": "John Smith" - }, - "manager_phone_number": { - "type": "string", - "example": "0911111111" - }, - "name": { - "type": "string", - "example": "4-kilo Branch" - }, - "number_of_unsettled": { - "type": "integer" - }, - "profit_percentage": { - "type": "number", - "example": 0.1 - }, - "stats_updated_at": { - "type": "string" - }, - "total_bets": { - "type": "integer" - }, - "total_cash_backs": { - "type": "number" - }, - "total_cash_out": { - "type": "number" - }, - "total_cashiers": { - "type": "integer" - }, - "total_stake": { - "type": "number" - }, - "total_unsettled_amount": { - "type": "number" - }, - "wallet_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.BranchLocation": { - "type": "object", - "properties": { - "key": { - "type": "string", - "example": "addis_ababa" - }, - "name": { - "type": "string", - "example": "Addis Ababa" - } - } - }, - "domain.BranchOperationRes": { - "type": "object", - "properties": { - "description": { - "type": "string", - "example": "Betting on sport events" - }, - "name": { - "type": "string", - "example": "SportsBook" - } - } - }, - "domain.BranchRes": { - "type": "object", - "properties": { - "branch_manager_id": { - "type": "integer", - "example": 1 - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": false - }, - "is_self_owned": { - "type": "boolean", - "example": false - }, - "location": { - "type": "string", - "example": "Addis Ababa" - }, - "name": { - "type": "string", - "example": "4-kilo Branch" - }, - "profit_percentage": { - "type": "number", - "example": 0.1 - }, - "wallet_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CompanyRes": { - "type": "object", - "properties": { - "admin_id": { - "type": "integer", - "example": 1 - }, - "deducted_percentage": { - "type": "number", - "example": 0.1 - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": true - }, - "name": { - "type": "string", - "example": "CompanyName" - }, - "slug": { - "type": "string", - "example": "slug" - }, - "wallet_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CreateBranchOperationReq": { - "type": "object", - "properties": { - "branch_id": { - "type": "integer", - "example": 1 - }, - "operation_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CreateBranchReq": { - "type": "object", - "required": [ - "branch_manager_id", - "location", - "name", - "operations" - ], - "properties": { - "branch_manager_id": { - "type": "integer", - "example": 1 - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "is_self_owned": { - "type": "boolean", - "example": false - }, - "location": { - "type": "string", - "maxLength": 100, - "minLength": 3, - "example": "Addis Ababa" - }, - "name": { - "type": "string", - "maxLength": 100, - "minLength": 3, - "example": "4-kilo Branch" - }, - "operations": { - "type": "array", - "items": { - "type": "integer" - } - }, - "profit_percentage": { - "type": "number", - "example": 0.1 - } - } - }, - "domain.CreateCompanyReq": { - "type": "object", - "properties": { - "admin_id": { - "type": "integer", - "example": 1 - }, - "deducted_percentage": { - "type": "number", - "example": 0.1 - }, - "is_active": { - "type": "boolean" - }, - "name": { - "type": "string", - "example": "CompanyName" - }, - "slug": { - "type": "string" - } - } - }, - "domain.CreateSupportedOperationReq": { - "type": "object", - "properties": { - "description": { - "type": "string", - "example": "Betting on sport events" - }, - "name": { - "type": "string", - "example": "SportsBook" - } - } - }, "domain.ErrorResponse": { "type": "object", "properties": { @@ -4124,123 +1394,6 @@ const docTemplate = `{ } } }, - "domain.GetCompanyRes": { - "type": "object", - "properties": { - "admin_first_name": { - "type": "string", - "example": "John" - }, - "admin_id": { - "type": "integer", - "example": 1 - }, - "admin_last_name": { - "type": "string", - "example": "Doe" - }, - "admin_phone_number": { - "type": "string", - "example": "1234567890" - }, - "balance": { - "type": "number", - "example": 1 - }, - "deducted_percentage": { - "type": "number", - "example": 0.1 - }, - "deducted_stake": { - "type": "number" - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": false - }, - "is_wallet_active": { - "type": "boolean", - "example": false - }, - "name": { - "type": "string", - "example": "CompanyName" - }, - "number_of_unsettled": { - "type": "integer" - }, - "slug": { - "type": "string", - "example": "slug" - }, - "stats_updated_at": { - "type": "string" - }, - "total_admins": { - "type": "integer" - }, - "total_approvers": { - "type": "integer" - }, - "total_bets": { - "type": "integer" - }, - "total_branches": { - "type": "integer" - }, - "total_cash_backs": { - "type": "number" - }, - "total_cash_out": { - "type": "number" - }, - "total_cashiers": { - "type": "integer" - }, - "total_customers": { - "type": "integer" - }, - "total_managers": { - "type": "integer" - }, - "total_stake": { - "type": "number" - }, - "total_unsettled_amount": { - "type": "number" - }, - "wallet_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.InstResponse": { - "type": "object", - "properties": { - "data": { - "description": "Changed to interface{} for flexibility" - }, - "message": { - "type": "string" - }, - "pagination": { - "description": "Made pointer and optional", - "allOf": [ - { - "$ref": "#/definitions/domain.Pagination" - } - ] - }, - "status": { - "type": "string" - } - } - }, "domain.LogEntry": { "type": "object", "properties": { @@ -4305,129 +1458,23 @@ const docTemplate = `{ } } }, - "domain.ReferralStats": { - "type": "object", - "properties": { - "totalReferrals": { - "type": "integer" - }, - "totalRewardEarned": { - "type": "integer" - } - } - }, - "domain.ReportedIssue": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "issue_type": { - "type": "string" - }, - "metadata": { - "type": "object", - "additionalProperties": true - }, - "status": { - "type": "string" - }, - "subject": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "user_id": { - "type": "integer" - }, - "user_role": { - "$ref": "#/definitions/domain.Role" - } - } - }, - "domain.Response": { - "type": "object", - "properties": { - "data": {}, - "message": { - "type": "string" - }, - "metadata": {}, - "status_code": { - "type": "integer" - }, - "success": { - "type": "boolean" - } - } - }, "domain.Role": { "type": "string", "enum": [ "super_admin", "admin", - "branch_manager", - "customer", - "cashier", - "transaction_approver" + "student", + "instructor", + "support" ], "x-enum-varnames": [ "RoleSuperAdmin", "RoleAdmin", - "RoleBranchManager", - "RoleCustomer", - "RoleCashier", - "RoleTransactionApprover" + "RoleStudent", + "RoleInstructor", + "RoleSupport" ] }, - "domain.SupportedOperationRes": { - "type": "object", - "properties": { - "description": { - "type": "string", - "example": "Betting on sport events" - }, - "id": { - "type": "integer", - "example": 1 - }, - "name": { - "type": "string", - "example": "SportsBook" - } - } - }, - "domain.UpdateCompanyReq": { - "type": "object", - "properties": { - "admin_id": { - "type": "integer", - "example": 1 - }, - "deducted_percentage": { - "type": "number", - "example": 0.1 - }, - "is_active": { - "type": "boolean", - "example": true - }, - "name": { - "type": "string", - "example": "CompanyName" - }, - "slug": { - "type": "string" - } - } - }, "handlers.AdminProfileRes": { "type": "object", "properties": { @@ -4569,97 +1616,6 @@ const docTemplate = `{ } } }, - "handlers.CreateCashierReq": { - "type": "object", - "properties": { - "branch_id": { - "type": "integer", - "example": 1 - }, - "email": { - "type": "string", - "example": "john.doe@example.com" - }, - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "password": { - "type": "string", - "example": "password123" - }, - "phone_number": { - "type": "string", - "example": "1234567890" - }, - "suspended": { - "type": "boolean", - "example": false - } - } - }, - "handlers.CreateManagerReq": { - "type": "object", - "properties": { - "company_id": { - "type": "integer", - "example": 1 - }, - "email": { - "type": "string", - "example": "john.doe@example.com" - }, - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "password": { - "type": "string", - "example": "password123" - }, - "phone_number": { - "type": "string", - "example": "1234567890" - } - } - }, - "handlers.CreateTransactionApproverReq": { - "type": "object", - "properties": { - "company_id": { - "type": "integer", - "example": 1 - }, - "email": { - "type": "string", - "example": "john.doe@example.com" - }, - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "password": { - "type": "string", - "example": "password123" - }, - "phone_number": { - "type": "string", - "example": "1234567890" - } - } - }, "handlers.CustomerProfileRes": { "type": "object", "properties": { @@ -4707,112 +1663,6 @@ const docTemplate = `{ } } }, - "handlers.CustomersRes": { - "type": "object", - "properties": { - "company_id": { - "type": "integer" - }, - "company_name": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "first_name": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_login": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "phone_number": { - "type": "string" - }, - "phone_verified": { - "type": "boolean" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "suspended": { - "type": "boolean" - }, - "suspended_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "handlers.GetCashierRes": { - "type": "object", - "properties": { - "branch_id": { - "type": "integer" - }, - "branch_location": { - "type": "string" - }, - "branch_name": { - "type": "string" - }, - "branch_wallet": { - "type": "integer" - }, - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "first_name": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_login": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "phone_number": { - "type": "string" - }, - "phone_verified": { - "type": "boolean" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "suspended": { - "type": "boolean" - }, - "suspended_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, "handlers.LoginAdminRes": { "type": "object", "properties": { @@ -4827,50 +1677,6 @@ const docTemplate = `{ } } }, - "handlers.ManagersRes": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "first_name": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_login": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "phone_number": { - "type": "string" - }, - "phone_verified": { - "type": "boolean" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "suspended": { - "type": "boolean" - }, - "suspended_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, "handlers.RegisterCodeReq": { "type": "object", "properties": { @@ -5145,40 +1951,6 @@ const docTemplate = `{ } } }, - "handlers.updateCashierReq": { - "type": "object", - "properties": { - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "suspended": { - "type": "boolean", - "example": false - } - } - }, - "handlers.updateCustomerReq": { - "type": "object", - "properties": { - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "suspended": { - "type": "boolean", - "example": false - } - } - }, "handlers.updateManagerReq": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index a4fe6e6..81c2588 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -71,7 +71,7 @@ } }, "post": { - "description": "Create transaction approver", + "description": "Create Admin", "consumes": [ "application/json" ], @@ -81,15 +81,15 @@ "tags": [ "admin" ], - "summary": "Create transaction approver", + "summary": "Create Admin", "parameters": [ { - "description": "Create transaction approver", + "description": "Create admin", "name": "manger", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/handlers.CreateTransactionApproverReq" + "$ref": "#/definitions/handlers.CreateAdminReq" } } ], @@ -121,41 +121,6 @@ } } }, - "/api/v1/admin-company": { - "get": { - "description": "Gets a single company by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Gets company by id", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetCompanyRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/admin/{id}": { "get": { "description": "Get a single admin by id", @@ -360,1703 +325,6 @@ } } }, - "/api/v1/banks": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "List all banks with pagination and filtering", - "parameters": [ - { - "type": "integer", - "description": "Filter by country ID", - "name": "country_id", - "in": "query" - }, - { - "type": "boolean", - "description": "Filter by active status", - "name": "is_active", - "in": "query" - }, - { - "type": "string", - "description": "Search term for bank name or code", - "name": "search", - "in": "query" - }, - { - "type": "integer", - "default": 1, - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "maximum": 100, - "type": "integer", - "default": 50, - "description": "Items per page", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.InstResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "Create a new bank", - "parameters": [ - { - "description": "Bank Info", - "name": "bank", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.Bank" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/domain.Bank" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/banks/{id}": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "Get a bank by ID", - "parameters": [ - { - "type": "integer", - "description": "Bank ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Bank" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "put": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "Update a bank", - "parameters": [ - { - "type": "integer", - "description": "Bank ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Bank Info", - "name": "bank", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.Bank" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Bank" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "delete": { - "produces": [ - "application/json" - ], - "tags": [ - "Institutions - Banks" - ], - "summary": "Delete a bank", - "parameters": [ - { - "type": "integer", - "description": "Bank ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "Deleted successfully", - "schema": { - "type": "string" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/branch": { - "get": { - "description": "Gets all branches", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets all branches", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Create a branch", - "parameters": [ - { - "description": "Creates branch", - "name": "createBranch", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBranchReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}": { - "get": { - "description": "Gets a single branch by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branch by id", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Updates a branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Updates a branch", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update Branch", - "name": "updateBranch", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBranchReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Delete the branch", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Delete the branch", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}/cashier": { - "get": { - "description": "Gets branch cashiers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branch cashiers", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.GetCashierRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}/operation": { - "get": { - "description": "Gets branch operations", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branch operations", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchOperationRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}/operation/{opID}": { - "delete": { - "description": "Delete the branch operation", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Delete the branch operation", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Branch Operation ID", - "name": "opID", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branch/{id}/return": { - "post": { - "description": "Unassign the branch wallet to company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Unassign the branch wallet to company", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branchCashier": { - "get": { - "description": "Gets branch for cahier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branch for cahier", - "parameters": [ - { - "type": "integer", - "description": "Branch ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/branchLocation": { - "get": { - "description": "Gets all branch locations", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets all branch locations", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchLocation" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/cashier/{id}": { - "get": { - "description": "Get a single cashier by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Get cashier by id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.UserProfileRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/cashiers": { - "get": { - "description": "Get all cashiers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Get all cashiers", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/handlers.GetCashierRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Create cashier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Create cashier", - "parameters": [ - { - "description": "Create cashier", - "name": "cashier", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateCashierReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/cashiers/{id}": { - "put": { - "description": "Update cashier", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "cashier" - ], - "summary": "Update cashier", - "parameters": [ - { - "type": "integer", - "description": "Cashier ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update cashier", - "name": "cashier", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.updateCashierReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/company": { - "get": { - "description": "Gets all companies", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Gets all companies", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.GetCompanyRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Create a company", - "parameters": [ - { - "description": "Creates company", - "name": "createCompany", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateCompanyReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CompanyRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/company/{id}": { - "get": { - "description": "Gets a single company by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Gets company by id", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetCompanyRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Updates a company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Updates a company", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Update Company", - "name": "updateCompany", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateCompanyReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CompanyRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "delete": { - "description": "Delete the company", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Delete the company", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/company/{id}/branch": { - "get": { - "description": "Gets branches by company id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branches by company id", - "parameters": [ - { - "type": "integer", - "description": "Company ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/currencies": { - "get": { - "description": "Returns list of supported currencies", - "produces": [ - "application/json" - ], - "tags": [ - "Multi-Currency" - ], - "summary": "Get supported currencies", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "type": "integer" - } - } - } - } - ] - } - } - } - } - }, - "/api/v1/currencies/convert": { - "get": { - "description": "Converts amount from one currency to another", - "produces": [ - "application/json" - ], - "tags": [ - "Multi-Currency" - ], - "summary": "Convert currency", - "parameters": [ - { - "type": "string", - "description": "Source currency code (e.g., USD)", - "name": "from", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "Target currency code (e.g., ETB)", - "name": "to", - "in": "query", - "required": true - }, - { - "type": "number", - "description": "Amount to convert", - "name": "amount", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "number" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/customer": { - "get": { - "description": "Get all Customers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get all Customers", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/customer/{id}": { - "get": { - "description": "Get a single customer by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get customer by id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Update Customers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Update Customers", - "parameters": [ - { - "description": "Update Customers", - "name": "Customers", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.updateCustomerReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/issues": { - "get": { - "description": "Admin endpoint to list all reported issues with pagination", - "produces": [ - "application/json" - ], - "tags": [ - "Issues" - ], - "summary": "Get all reported issues", - "parameters": [ - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ReportedIssue" - } - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - }, - "post": { - "description": "Allows a customer to report a new issue related to the betting platform", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Issues" - ], - "summary": "Report an issue", - "parameters": [ - { - "description": "Issue to report", - "name": "issue", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ReportedIssue" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/domain.ReportedIssue" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/issues/user/{user_id}": { - "get": { - "description": "Returns all issues reported by a specific user", - "produces": [ - "application/json" - ], - "tags": [ - "Issues" - ], - "summary": "Get reported issues by a user", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "user_id", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "Limit", - "name": "limit", - "in": "query" - }, - { - "type": "integer", - "description": "Offset", - "name": "offset", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ReportedIssue" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/issues/{issue_id}": { - "delete": { - "description": "Admin endpoint to delete a reported issue", - "tags": [ - "Issues" - ], - "summary": "Delete a reported issue", - "parameters": [ - { - "type": "integer", - "description": "Issue ID", - "name": "issue_id", - "in": "path", - "required": true - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/issues/{issue_id}/status": { - "patch": { - "description": "Admin endpoint to update the status of a reported issue", - "consumes": [ - "application/json" - ], - "tags": [ - "Issues" - ], - "summary": "Update issue status", - "parameters": [ - { - "type": "integer", - "description": "Issue ID", - "name": "issue_id", - "in": "path", - "required": true - }, - { - "description": "New issue status (pending, in_progress, resolved, rejected)", - "name": "status", - "in": "body", - "required": true, - "schema": { - "type": "object", - "properties": { - "status": { - "type": "string" - } - } - } - } - ], - "responses": { - "204": { - "description": "No Content" - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/logs": { "get": { "description": "Fetches application logs from MongoDB with pagination, level filtering, and search", @@ -2117,207 +385,7 @@ } } }, - "/api/v1/manager/{id}/branch": { - "get": { - "description": "Gets a branches by manager id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets branches by manager id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/managers": { - "get": { - "description": "Get all Managers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "manager" - ], - "summary": "Get all Managers", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.ManagersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Create Manager", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "manager" - ], - "summary": "Create Manager", - "parameters": [ - { - "description": "Create manager", - "name": "manger", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.CreateManagerReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/managers/{id}": { - "get": { - "description": "Get a single manager by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "manager" - ], - "summary": "Get manager by id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.ManagersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, "put": { "description": "Update Managers", "consumes": [ @@ -2369,137 +437,6 @@ } } }, - "/api/v1/operation": { - "post": { - "description": "Creates a operation", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Create a operation", - "parameters": [ - { - "description": "Creates operation", - "name": "createBranchOperation", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateBranchOperationReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.BranchOperationRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/search/branch": { - "get": { - "description": "Search branches by name or location", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Search branches", - "parameters": [ - { - "type": "string", - "description": "Search query", - "name": "q", - "in": "query", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/search/company": { - "get": { - "description": "Gets all companies", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "company" - ], - "summary": "Gets all companies", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.CompanyRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/super-login": { "post": { "description": "Login super-admin", @@ -2552,143 +489,6 @@ } } }, - "/api/v1/supportedOperation": { - "get": { - "description": "Gets all supported operations", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Gets all supported operations", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.BranchDetailRes" - } - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "post": { - "description": "Creates a supported operation", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "branch" - ], - "summary": "Create a supported operation", - "parameters": [ - { - "description": "Creates supported operation", - "name": "createSupportedOperation", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateSupportedOperationReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.SupportedOperationRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/t-approver": { - "get": { - "description": "Get all Admins", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "admin" - ], - "summary": "Get all Admins", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.AdminRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/t-approver/{id}": { "get": { "description": "Get a single admin by id", @@ -2835,201 +635,6 @@ } } }, - "/api/v1/tenant/{tenant_slug}/customer": { - "get": { - "description": "Get all Customers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get all Customers", - "parameters": [ - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/tenant/{tenant_slug}/customer/{id}": { - "get": { - "description": "Get a single customer by id", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Get customer by id", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handlers.CustomersRes" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - }, - "put": { - "description": "Update Customers", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "customer" - ], - "summary": "Update Customers", - "parameters": [ - { - "description": "Update Customers", - "name": "Customers", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/handlers.updateCustomerReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, - "/api/v1/tenant/{tenant_slug}/referral/stats": { - "get": { - "security": [ - { - "Bearer": [] - } - ], - "description": "Retrieves referral statistics for the authenticated user", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "referral" - ], - "summary": "Get referral statistics", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.ReferralStats" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } - }, "/api/v1/user/delete/{id}": { "delete": { "description": "Delete a user by their ID", @@ -3770,341 +1375,6 @@ } }, "definitions": { - "domain.Bank": { - "type": "object", - "properties": { - "acct_length": { - "type": "integer" - }, - "active": { - "type": "integer" - }, - "bank_logo": { - "description": "URL or base64", - "type": "string" - }, - "country_id": { - "type": "integer" - }, - "created_at": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "is_24hrs": { - "description": "nullable", - "type": "integer" - }, - "is_active": { - "type": "integer" - }, - "is_mobilemoney": { - "description": "nullable", - "type": "integer" - }, - "is_rtgs": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "slug": { - "type": "string" - }, - "swift": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "domain.BranchDetailRes": { - "type": "object", - "properties": { - "balance": { - "type": "number", - "example": 100.5 - }, - "branch_manager_id": { - "type": "integer", - "example": 1 - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "company_name": { - "type": "string", - "example": "fortune" - }, - "deducted_stake": { - "type": "number" - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": false - }, - "is_self_owned": { - "type": "boolean", - "example": false - }, - "is_wallet_active": { - "type": "boolean", - "example": false - }, - "location": { - "type": "string", - "example": "Addis Ababa" - }, - "manager_name": { - "type": "string", - "example": "John Smith" - }, - "manager_phone_number": { - "type": "string", - "example": "0911111111" - }, - "name": { - "type": "string", - "example": "4-kilo Branch" - }, - "number_of_unsettled": { - "type": "integer" - }, - "profit_percentage": { - "type": "number", - "example": 0.1 - }, - "stats_updated_at": { - "type": "string" - }, - "total_bets": { - "type": "integer" - }, - "total_cash_backs": { - "type": "number" - }, - "total_cash_out": { - "type": "number" - }, - "total_cashiers": { - "type": "integer" - }, - "total_stake": { - "type": "number" - }, - "total_unsettled_amount": { - "type": "number" - }, - "wallet_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.BranchLocation": { - "type": "object", - "properties": { - "key": { - "type": "string", - "example": "addis_ababa" - }, - "name": { - "type": "string", - "example": "Addis Ababa" - } - } - }, - "domain.BranchOperationRes": { - "type": "object", - "properties": { - "description": { - "type": "string", - "example": "Betting on sport events" - }, - "name": { - "type": "string", - "example": "SportsBook" - } - } - }, - "domain.BranchRes": { - "type": "object", - "properties": { - "branch_manager_id": { - "type": "integer", - "example": 1 - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": false - }, - "is_self_owned": { - "type": "boolean", - "example": false - }, - "location": { - "type": "string", - "example": "Addis Ababa" - }, - "name": { - "type": "string", - "example": "4-kilo Branch" - }, - "profit_percentage": { - "type": "number", - "example": 0.1 - }, - "wallet_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CompanyRes": { - "type": "object", - "properties": { - "admin_id": { - "type": "integer", - "example": 1 - }, - "deducted_percentage": { - "type": "number", - "example": 0.1 - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": true - }, - "name": { - "type": "string", - "example": "CompanyName" - }, - "slug": { - "type": "string", - "example": "slug" - }, - "wallet_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CreateBranchOperationReq": { - "type": "object", - "properties": { - "branch_id": { - "type": "integer", - "example": 1 - }, - "operation_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.CreateBranchReq": { - "type": "object", - "required": [ - "branch_manager_id", - "location", - "name", - "operations" - ], - "properties": { - "branch_manager_id": { - "type": "integer", - "example": 1 - }, - "company_id": { - "type": "integer", - "example": 1 - }, - "is_self_owned": { - "type": "boolean", - "example": false - }, - "location": { - "type": "string", - "maxLength": 100, - "minLength": 3, - "example": "Addis Ababa" - }, - "name": { - "type": "string", - "maxLength": 100, - "minLength": 3, - "example": "4-kilo Branch" - }, - "operations": { - "type": "array", - "items": { - "type": "integer" - } - }, - "profit_percentage": { - "type": "number", - "example": 0.1 - } - } - }, - "domain.CreateCompanyReq": { - "type": "object", - "properties": { - "admin_id": { - "type": "integer", - "example": 1 - }, - "deducted_percentage": { - "type": "number", - "example": 0.1 - }, - "is_active": { - "type": "boolean" - }, - "name": { - "type": "string", - "example": "CompanyName" - }, - "slug": { - "type": "string" - } - } - }, - "domain.CreateSupportedOperationReq": { - "type": "object", - "properties": { - "description": { - "type": "string", - "example": "Betting on sport events" - }, - "name": { - "type": "string", - "example": "SportsBook" - } - } - }, "domain.ErrorResponse": { "type": "object", "properties": { @@ -4116,123 +1386,6 @@ } } }, - "domain.GetCompanyRes": { - "type": "object", - "properties": { - "admin_first_name": { - "type": "string", - "example": "John" - }, - "admin_id": { - "type": "integer", - "example": 1 - }, - "admin_last_name": { - "type": "string", - "example": "Doe" - }, - "admin_phone_number": { - "type": "string", - "example": "1234567890" - }, - "balance": { - "type": "number", - "example": 1 - }, - "deducted_percentage": { - "type": "number", - "example": 0.1 - }, - "deducted_stake": { - "type": "number" - }, - "id": { - "type": "integer", - "example": 1 - }, - "is_active": { - "type": "boolean", - "example": false - }, - "is_wallet_active": { - "type": "boolean", - "example": false - }, - "name": { - "type": "string", - "example": "CompanyName" - }, - "number_of_unsettled": { - "type": "integer" - }, - "slug": { - "type": "string", - "example": "slug" - }, - "stats_updated_at": { - "type": "string" - }, - "total_admins": { - "type": "integer" - }, - "total_approvers": { - "type": "integer" - }, - "total_bets": { - "type": "integer" - }, - "total_branches": { - "type": "integer" - }, - "total_cash_backs": { - "type": "number" - }, - "total_cash_out": { - "type": "number" - }, - "total_cashiers": { - "type": "integer" - }, - "total_customers": { - "type": "integer" - }, - "total_managers": { - "type": "integer" - }, - "total_stake": { - "type": "number" - }, - "total_unsettled_amount": { - "type": "number" - }, - "wallet_id": { - "type": "integer", - "example": 1 - } - } - }, - "domain.InstResponse": { - "type": "object", - "properties": { - "data": { - "description": "Changed to interface{} for flexibility" - }, - "message": { - "type": "string" - }, - "pagination": { - "description": "Made pointer and optional", - "allOf": [ - { - "$ref": "#/definitions/domain.Pagination" - } - ] - }, - "status": { - "type": "string" - } - } - }, "domain.LogEntry": { "type": "object", "properties": { @@ -4297,129 +1450,23 @@ } } }, - "domain.ReferralStats": { - "type": "object", - "properties": { - "totalReferrals": { - "type": "integer" - }, - "totalRewardEarned": { - "type": "integer" - } - } - }, - "domain.ReportedIssue": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "issue_type": { - "type": "string" - }, - "metadata": { - "type": "object", - "additionalProperties": true - }, - "status": { - "type": "string" - }, - "subject": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "user_id": { - "type": "integer" - }, - "user_role": { - "$ref": "#/definitions/domain.Role" - } - } - }, - "domain.Response": { - "type": "object", - "properties": { - "data": {}, - "message": { - "type": "string" - }, - "metadata": {}, - "status_code": { - "type": "integer" - }, - "success": { - "type": "boolean" - } - } - }, "domain.Role": { "type": "string", "enum": [ "super_admin", "admin", - "branch_manager", - "customer", - "cashier", - "transaction_approver" + "student", + "instructor", + "support" ], "x-enum-varnames": [ "RoleSuperAdmin", "RoleAdmin", - "RoleBranchManager", - "RoleCustomer", - "RoleCashier", - "RoleTransactionApprover" + "RoleStudent", + "RoleInstructor", + "RoleSupport" ] }, - "domain.SupportedOperationRes": { - "type": "object", - "properties": { - "description": { - "type": "string", - "example": "Betting on sport events" - }, - "id": { - "type": "integer", - "example": 1 - }, - "name": { - "type": "string", - "example": "SportsBook" - } - } - }, - "domain.UpdateCompanyReq": { - "type": "object", - "properties": { - "admin_id": { - "type": "integer", - "example": 1 - }, - "deducted_percentage": { - "type": "number", - "example": 0.1 - }, - "is_active": { - "type": "boolean", - "example": true - }, - "name": { - "type": "string", - "example": "CompanyName" - }, - "slug": { - "type": "string" - } - } - }, "handlers.AdminProfileRes": { "type": "object", "properties": { @@ -4561,97 +1608,6 @@ } } }, - "handlers.CreateCashierReq": { - "type": "object", - "properties": { - "branch_id": { - "type": "integer", - "example": 1 - }, - "email": { - "type": "string", - "example": "john.doe@example.com" - }, - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "password": { - "type": "string", - "example": "password123" - }, - "phone_number": { - "type": "string", - "example": "1234567890" - }, - "suspended": { - "type": "boolean", - "example": false - } - } - }, - "handlers.CreateManagerReq": { - "type": "object", - "properties": { - "company_id": { - "type": "integer", - "example": 1 - }, - "email": { - "type": "string", - "example": "john.doe@example.com" - }, - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "password": { - "type": "string", - "example": "password123" - }, - "phone_number": { - "type": "string", - "example": "1234567890" - } - } - }, - "handlers.CreateTransactionApproverReq": { - "type": "object", - "properties": { - "company_id": { - "type": "integer", - "example": 1 - }, - "email": { - "type": "string", - "example": "john.doe@example.com" - }, - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "password": { - "type": "string", - "example": "password123" - }, - "phone_number": { - "type": "string", - "example": "1234567890" - } - } - }, "handlers.CustomerProfileRes": { "type": "object", "properties": { @@ -4699,112 +1655,6 @@ } } }, - "handlers.CustomersRes": { - "type": "object", - "properties": { - "company_id": { - "type": "integer" - }, - "company_name": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "first_name": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_login": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "phone_number": { - "type": "string" - }, - "phone_verified": { - "type": "boolean" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "suspended": { - "type": "boolean" - }, - "suspended_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, - "handlers.GetCashierRes": { - "type": "object", - "properties": { - "branch_id": { - "type": "integer" - }, - "branch_location": { - "type": "string" - }, - "branch_name": { - "type": "string" - }, - "branch_wallet": { - "type": "integer" - }, - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "first_name": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_login": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "phone_number": { - "type": "string" - }, - "phone_verified": { - "type": "boolean" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "suspended": { - "type": "boolean" - }, - "suspended_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, "handlers.LoginAdminRes": { "type": "object", "properties": { @@ -4819,50 +1669,6 @@ } } }, - "handlers.ManagersRes": { - "type": "object", - "properties": { - "created_at": { - "type": "string" - }, - "email": { - "type": "string" - }, - "email_verified": { - "type": "boolean" - }, - "first_name": { - "type": "string" - }, - "id": { - "type": "integer" - }, - "last_login": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "phone_number": { - "type": "string" - }, - "phone_verified": { - "type": "boolean" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "suspended": { - "type": "boolean" - }, - "suspended_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - } - }, "handlers.RegisterCodeReq": { "type": "object", "properties": { @@ -5137,40 +1943,6 @@ } } }, - "handlers.updateCashierReq": { - "type": "object", - "properties": { - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "suspended": { - "type": "boolean", - "example": false - } - } - }, - "handlers.updateCustomerReq": { - "type": "object", - "properties": { - "first_name": { - "type": "string", - "example": "John" - }, - "last_name": { - "type": "string", - "example": "Doe" - }, - "suspended": { - "type": "boolean", - "example": false - } - } - }, "handlers.updateManagerReq": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index c279a32..8805e3e 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,243 +1,4 @@ definitions: - domain.Bank: - properties: - acct_length: - type: integer - active: - type: integer - bank_logo: - description: URL or base64 - type: string - country_id: - type: integer - created_at: - type: string - currency: - type: string - id: - type: integer - is_24hrs: - description: nullable - type: integer - is_active: - type: integer - is_mobilemoney: - description: nullable - type: integer - is_rtgs: - type: integer - name: - type: string - slug: - type: string - swift: - type: string - updated_at: - type: string - type: object - domain.BranchDetailRes: - properties: - balance: - example: 100.5 - type: number - branch_manager_id: - example: 1 - type: integer - company_id: - example: 1 - type: integer - company_name: - example: fortune - type: string - deducted_stake: - type: number - id: - example: 1 - type: integer - is_active: - example: false - type: boolean - is_self_owned: - example: false - type: boolean - is_wallet_active: - example: false - type: boolean - location: - example: Addis Ababa - type: string - manager_name: - example: John Smith - type: string - manager_phone_number: - example: "0911111111" - type: string - name: - example: 4-kilo Branch - type: string - number_of_unsettled: - type: integer - profit_percentage: - example: 0.1 - type: number - stats_updated_at: - type: string - total_bets: - type: integer - total_cash_backs: - type: number - total_cash_out: - type: number - total_cashiers: - type: integer - total_stake: - type: number - total_unsettled_amount: - type: number - wallet_id: - example: 1 - type: integer - type: object - domain.BranchLocation: - properties: - key: - example: addis_ababa - type: string - name: - example: Addis Ababa - type: string - type: object - domain.BranchOperationRes: - properties: - description: - example: Betting on sport events - type: string - name: - example: SportsBook - type: string - type: object - domain.BranchRes: - properties: - branch_manager_id: - example: 1 - type: integer - company_id: - example: 1 - type: integer - id: - example: 1 - type: integer - is_active: - example: false - type: boolean - is_self_owned: - example: false - type: boolean - location: - example: Addis Ababa - type: string - name: - example: 4-kilo Branch - type: string - profit_percentage: - example: 0.1 - type: number - wallet_id: - example: 1 - type: integer - type: object - domain.CompanyRes: - properties: - admin_id: - example: 1 - type: integer - deducted_percentage: - example: 0.1 - type: number - id: - example: 1 - type: integer - is_active: - example: true - type: boolean - name: - example: CompanyName - type: string - slug: - example: slug - type: string - wallet_id: - example: 1 - type: integer - type: object - domain.CreateBranchOperationReq: - properties: - branch_id: - example: 1 - type: integer - operation_id: - example: 1 - type: integer - type: object - domain.CreateBranchReq: - properties: - branch_manager_id: - example: 1 - type: integer - company_id: - example: 1 - type: integer - is_self_owned: - example: false - type: boolean - location: - example: Addis Ababa - maxLength: 100 - minLength: 3 - type: string - name: - example: 4-kilo Branch - maxLength: 100 - minLength: 3 - type: string - operations: - items: - type: integer - type: array - profit_percentage: - example: 0.1 - type: number - required: - - branch_manager_id - - location - - name - - operations - type: object - domain.CreateCompanyReq: - properties: - admin_id: - example: 1 - type: integer - deducted_percentage: - example: 0.1 - type: number - is_active: - type: boolean - name: - example: CompanyName - type: string - slug: - type: string - type: object - domain.CreateSupportedOperationReq: - properties: - description: - example: Betting on sport events - type: string - name: - example: SportsBook - type: string - type: object domain.ErrorResponse: properties: error: @@ -245,86 +6,6 @@ definitions: message: type: string type: object - domain.GetCompanyRes: - properties: - admin_first_name: - example: John - type: string - admin_id: - example: 1 - type: integer - admin_last_name: - example: Doe - type: string - admin_phone_number: - example: "1234567890" - type: string - balance: - example: 1 - type: number - deducted_percentage: - example: 0.1 - type: number - deducted_stake: - type: number - id: - example: 1 - type: integer - is_active: - example: false - type: boolean - is_wallet_active: - example: false - type: boolean - name: - example: CompanyName - type: string - number_of_unsettled: - type: integer - slug: - example: slug - type: string - stats_updated_at: - type: string - total_admins: - type: integer - total_approvers: - type: integer - total_bets: - type: integer - total_branches: - type: integer - total_cash_backs: - type: number - total_cash_out: - type: number - total_cashiers: - type: integer - total_customers: - type: integer - total_managers: - type: integer - total_stake: - type: number - total_unsettled_amount: - type: number - wallet_id: - example: 1 - type: integer - type: object - domain.InstResponse: - properties: - data: - description: Changed to interface{} for flexibility - message: - type: string - pagination: - allOf: - - $ref: '#/definitions/domain.Pagination' - description: Made pointer and optional - status: - type: string - type: object domain.LogEntry: properties: caller: @@ -367,93 +48,20 @@ definitions: total_pages: type: integer type: object - domain.ReferralStats: - properties: - totalReferrals: - type: integer - totalRewardEarned: - type: integer - type: object - domain.ReportedIssue: - properties: - created_at: - type: string - description: - type: string - id: - type: integer - issue_type: - type: string - metadata: - additionalProperties: true - type: object - status: - type: string - subject: - type: string - updated_at: - type: string - user_id: - type: integer - user_role: - $ref: '#/definitions/domain.Role' - type: object - domain.Response: - properties: - data: {} - message: - type: string - metadata: {} - status_code: - type: integer - success: - type: boolean - type: object domain.Role: enum: - super_admin - admin - - branch_manager - - customer - - cashier - - transaction_approver + - student + - instructor + - support type: string x-enum-varnames: - RoleSuperAdmin - RoleAdmin - - RoleBranchManager - - RoleCustomer - - RoleCashier - - RoleTransactionApprover - domain.SupportedOperationRes: - properties: - description: - example: Betting on sport events - type: string - id: - example: 1 - type: integer - name: - example: SportsBook - type: string - type: object - domain.UpdateCompanyReq: - properties: - admin_id: - example: 1 - type: integer - deducted_percentage: - example: 0.1 - type: number - is_active: - example: true - type: boolean - name: - example: CompanyName - type: string - slug: - type: string - type: object + - RoleStudent + - RoleInstructor + - RoleSupport handlers.AdminProfileRes: properties: created_at: @@ -549,72 +157,6 @@ definitions: example: "1234567890" type: string type: object - handlers.CreateCashierReq: - properties: - branch_id: - example: 1 - type: integer - email: - example: john.doe@example.com - type: string - first_name: - example: John - type: string - last_name: - example: Doe - type: string - password: - example: password123 - type: string - phone_number: - example: "1234567890" - type: string - suspended: - example: false - type: boolean - type: object - handlers.CreateManagerReq: - properties: - company_id: - example: 1 - type: integer - email: - example: john.doe@example.com - type: string - first_name: - example: John - type: string - last_name: - example: Doe - type: string - password: - example: password123 - type: string - phone_number: - example: "1234567890" - type: string - type: object - handlers.CreateTransactionApproverReq: - properties: - company_id: - example: 1 - type: integer - email: - example: john.doe@example.com - type: string - first_name: - example: John - type: string - last_name: - example: Doe - type: string - password: - example: password123 - type: string - phone_number: - example: "1234567890" - type: string - type: object handlers.CustomerProfileRes: properties: created_at: @@ -646,76 +188,6 @@ definitions: updated_at: type: string type: object - handlers.CustomersRes: - properties: - company_id: - type: integer - company_name: - type: string - created_at: - type: string - email: - type: string - email_verified: - type: boolean - first_name: - type: string - id: - type: integer - last_login: - type: string - last_name: - type: string - phone_number: - type: string - phone_verified: - type: boolean - role: - $ref: '#/definitions/domain.Role' - suspended: - type: boolean - suspended_at: - type: string - updated_at: - type: string - type: object - handlers.GetCashierRes: - properties: - branch_id: - type: integer - branch_location: - type: string - branch_name: - type: string - branch_wallet: - type: integer - created_at: - type: string - email: - type: string - email_verified: - type: boolean - first_name: - type: string - id: - type: integer - last_login: - type: string - last_name: - type: string - phone_number: - type: string - phone_verified: - type: boolean - role: - $ref: '#/definitions/domain.Role' - suspended: - type: boolean - suspended_at: - type: string - updated_at: - type: string - type: object handlers.LoginAdminRes: properties: access_token: @@ -725,35 +197,6 @@ definitions: role: type: string type: object - handlers.ManagersRes: - properties: - created_at: - type: string - email: - type: string - email_verified: - type: boolean - first_name: - type: string - id: - type: integer - last_login: - type: string - last_name: - type: string - phone_number: - type: string - phone_verified: - type: boolean - role: - $ref: '#/definitions/domain.Role' - suspended: - type: boolean - suspended_at: - type: string - updated_at: - type: string - type: object handlers.RegisterCodeReq: properties: email: @@ -943,30 +386,6 @@ definitions: example: false type: boolean type: object - handlers.updateCashierReq: - properties: - first_name: - example: John - type: string - last_name: - example: Doe - type: string - suspended: - example: false - type: boolean - type: object - handlers.updateCustomerReq: - properties: - first_name: - example: John - type: string - last_name: - example: Doe - type: string - suspended: - example: false - type: boolean - type: object handlers.updateManagerReq: properties: company_id: @@ -1355,14 +774,14 @@ paths: post: consumes: - application/json - description: Create transaction approver + description: Create Admin parameters: - - description: Create transaction approver + - description: Create admin in: body name: manger required: true schema: - $ref: '#/definitions/handlers.CreateTransactionApproverReq' + $ref: '#/definitions/handlers.CreateAdminReq' produces: - application/json responses: @@ -1382,32 +801,9 @@ paths: description: Internal Server Error schema: $ref: '#/definitions/response.APIResponse' - summary: Create transaction approver + summary: Create Admin tags: - admin - /api/v1/admin-company: - get: - consumes: - - application/json - description: Gets a single company by id - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.GetCompanyRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets company by id - tags: - - company /api/v1/admin/{id}: get: consumes: @@ -1542,1121 +938,6 @@ paths: summary: Refresh token tags: - auth - /api/v1/banks: - get: - parameters: - - description: Filter by country ID - in: query - name: country_id - type: integer - - description: Filter by active status - in: query - name: is_active - type: boolean - - description: Search term for bank name or code - in: query - name: search - type: string - - default: 1 - description: Page number - in: query - name: page - type: integer - - default: 50 - description: Items per page - in: query - maximum: 100 - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.InstResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: List all banks with pagination and filtering - tags: - - Institutions - Banks - post: - consumes: - - application/json - parameters: - - description: Bank Info - in: body - name: bank - required: true - schema: - $ref: '#/definitions/domain.Bank' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/domain.Bank' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Create a new bank - tags: - - Institutions - Banks - /api/v1/banks/{id}: - delete: - parameters: - - description: Bank ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "204": - description: Deleted successfully - schema: - type: string - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "404": - description: Not Found - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Delete a bank - tags: - - Institutions - Banks - get: - parameters: - - description: Bank ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Bank' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "404": - description: Not Found - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get a bank by ID - tags: - - Institutions - Banks - put: - consumes: - - application/json - parameters: - - description: Bank ID - in: path - name: id - required: true - type: integer - - description: Bank Info - in: body - name: bank - required: true - schema: - $ref: '#/definitions/domain.Bank' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Bank' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "404": - description: Not Found - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Update a bank - tags: - - Institutions - Banks - /api/v1/branch: - get: - consumes: - - application/json - description: Gets all branches - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BranchDetailRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all branches - tags: - - branch - post: - consumes: - - application/json - description: Creates a branch - parameters: - - description: Creates branch - in: body - name: createBranch - required: true - schema: - $ref: '#/definitions/domain.CreateBranchReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BranchRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create a branch - tags: - - branch - /api/v1/branch/{id}: - delete: - consumes: - - application/json - description: Delete the branch - parameters: - - description: Branch ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Delete the branch - tags: - - branch - get: - consumes: - - application/json - description: Gets a single branch by id - parameters: - - description: Branch ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BranchDetailRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets branch by id - tags: - - branch - put: - consumes: - - application/json - description: Updates a branch - parameters: - - description: Branch ID - in: path - name: id - required: true - type: integer - - description: Update Branch - in: body - name: updateBranch - required: true - schema: - $ref: '#/definitions/domain.CreateBranchReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BranchRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Updates a branch - tags: - - branch - /api/v1/branch/{id}/cashier: - get: - consumes: - - application/json - description: Gets branch cashiers - parameters: - - description: Branch ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/handlers.GetCashierRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets branch cashiers - tags: - - branch - /api/v1/branch/{id}/operation: - get: - consumes: - - application/json - description: Gets branch operations - parameters: - - description: Branch ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BranchOperationRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets branch operations - tags: - - branch - /api/v1/branch/{id}/operation/{opID}: - delete: - consumes: - - application/json - description: Delete the branch operation - parameters: - - description: Branch ID - in: path - name: id - required: true - type: integer - - description: Branch Operation ID - in: path - name: opID - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Delete the branch operation - tags: - - branch - /api/v1/branch/{id}/return: - post: - consumes: - - application/json - description: Unassign the branch wallet to company - parameters: - - description: Branch ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BranchDetailRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Unassign the branch wallet to company - tags: - - branch - /api/v1/branchCashier: - get: - consumes: - - application/json - description: Gets branch for cahier - parameters: - - description: Branch ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BranchDetailRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets branch for cahier - tags: - - branch - /api/v1/branchLocation: - get: - consumes: - - application/json - description: Gets all branch locations - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BranchLocation' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all branch locations - tags: - - branch - /api/v1/cashier/{id}: - get: - consumes: - - application/json - description: Get a single cashier by id - parameters: - - description: User ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.UserProfileRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get cashier by id - tags: - - cashier - /api/v1/cashiers: - get: - consumes: - - application/json - description: Get all cashiers - parameters: - - description: Page number - in: query - name: page - type: integer - - description: Page size - in: query - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/handlers.GetCashierRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all cashiers - tags: - - cashier - post: - consumes: - - application/json - description: Create cashier - parameters: - - description: Create cashier - in: body - name: cashier - required: true - schema: - $ref: '#/definitions/handlers.CreateCashierReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create cashier - tags: - - cashier - /api/v1/cashiers/{id}: - put: - consumes: - - application/json - description: Update cashier - parameters: - - description: Cashier ID - in: path - name: id - required: true - type: integer - - description: Update cashier - in: body - name: cashier - required: true - schema: - $ref: '#/definitions/handlers.updateCashierReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Update cashier - tags: - - cashier - /api/v1/company: - get: - consumes: - - application/json - description: Gets all companies - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.GetCompanyRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all companies - tags: - - company - post: - consumes: - - application/json - description: Creates a company - parameters: - - description: Creates company - in: body - name: createCompany - required: true - schema: - $ref: '#/definitions/domain.CreateCompanyReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.CompanyRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create a company - tags: - - company - /api/v1/company/{id}: - delete: - consumes: - - application/json - description: Delete the company - parameters: - - description: Company ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Delete the company - tags: - - company - get: - consumes: - - application/json - description: Gets a single company by id - parameters: - - description: Company ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.GetCompanyRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets company by id - tags: - - company - put: - consumes: - - application/json - description: Updates a company - parameters: - - description: Company ID - in: path - name: id - required: true - type: integer - - description: Update Company - in: body - name: updateCompany - required: true - schema: - $ref: '#/definitions/domain.UpdateCompanyReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.CompanyRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Updates a company - tags: - - company - /api/v1/company/{id}/branch: - get: - consumes: - - application/json - description: Gets branches by company id - parameters: - - description: Company ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BranchDetailRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets branches by company id - tags: - - branch - /api/v1/currencies: - get: - description: Returns list of supported currencies - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - type: integer - type: array - type: object - summary: Get supported currencies - tags: - - Multi-Currency - /api/v1/currencies/convert: - get: - description: Converts amount from one currency to another - parameters: - - description: Source currency code (e.g., USD) - in: query - name: from - required: true - type: string - - description: Target currency code (e.g., ETB) - in: query - name: to - required: true - type: string - - description: Amount to convert - in: query - name: amount - required: true - type: number - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - type: number - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Convert currency - tags: - - Multi-Currency - /api/v1/customer: - get: - consumes: - - application/json - description: Get all Customers - parameters: - - description: Page number - in: query - name: page - type: integer - - description: Page size - in: query - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.CustomersRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all Customers - tags: - - customer - /api/v1/customer/{id}: - get: - consumes: - - application/json - description: Get a single customer by id - parameters: - - description: User ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.CustomersRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get customer by id - tags: - - customer - put: - consumes: - - application/json - description: Update Customers - parameters: - - description: Update Customers - in: body - name: Customers - required: true - schema: - $ref: '#/definitions/handlers.updateCustomerReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Update Customers - tags: - - customer - /api/v1/issues: - get: - description: Admin endpoint to list all reported issues with pagination - parameters: - - description: Limit - in: query - name: limit - type: integer - - description: Offset - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.ReportedIssue' - type: array - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get all reported issues - tags: - - Issues - post: - consumes: - - application/json - description: Allows a customer to report a new issue related to the betting - platform - parameters: - - description: Issue to report - in: body - name: issue - required: true - schema: - $ref: '#/definitions/domain.ReportedIssue' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/domain.ReportedIssue' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Report an issue - tags: - - Issues - /api/v1/issues/{issue_id}: - delete: - description: Admin endpoint to delete a reported issue - parameters: - - description: Issue ID - in: path - name: issue_id - required: true - type: integer - responses: - "204": - description: No Content - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Delete a reported issue - tags: - - Issues - /api/v1/issues/{issue_id}/status: - patch: - consumes: - - application/json - description: Admin endpoint to update the status of a reported issue - parameters: - - description: Issue ID - in: path - name: issue_id - required: true - type: integer - - description: New issue status (pending, in_progress, resolved, rejected) - in: body - name: status - required: true - schema: - properties: - status: - type: string - type: object - responses: - "204": - description: No Content - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Update issue status - tags: - - Issues - /api/v1/issues/user/{user_id}: - get: - description: Returns all issues reported by a specific user - parameters: - - description: User ID - in: path - name: user_id - required: true - type: integer - - description: Limit - in: query - name: limit - type: integer - - description: Offset - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.ReportedIssue' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Get reported issues by a user - tags: - - Issues /api/v1/logs: get: description: Fetches application logs from MongoDB with pagination, level filtering, @@ -2699,139 +980,7 @@ paths: summary: Retrieve application logs with filtering and pagination tags: - Logs - /api/v1/manager/{id}/branch: - get: - consumes: - - application/json - description: Gets a branches by manager id - parameters: - - description: User ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BranchDetailRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets branches by manager id - tags: - - branch - /api/v1/managers: - get: - consumes: - - application/json - description: Get all Managers - parameters: - - description: Page number - in: query - name: page - type: integer - - description: Page size - in: query - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.ManagersRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all Managers - tags: - - manager - post: - consumes: - - application/json - description: Create Manager - parameters: - - description: Create manager - in: body - name: manger - required: true - schema: - $ref: '#/definitions/handlers.CreateManagerReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create Manager - tags: - - manager /api/v1/managers/{id}: - get: - consumes: - - application/json - description: Get a single manager by id - parameters: - - description: User ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.ManagersRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get manager by id - tags: - - manager put: consumes: - application/json @@ -2865,92 +1014,6 @@ paths: summary: Update Managers tags: - manager - /api/v1/operation: - post: - consumes: - - application/json - description: Creates a operation - parameters: - - description: Creates operation - in: body - name: createBranchOperation - required: true - schema: - $ref: '#/definitions/domain.CreateBranchOperationReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.BranchOperationRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create a operation - tags: - - branch - /api/v1/search/branch: - get: - consumes: - - application/json - description: Search branches by name or location - parameters: - - description: Search query - in: query - name: q - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BranchDetailRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Search branches - tags: - - branch - /api/v1/search/company: - get: - consumes: - - application/json - description: Gets all companies - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.CompanyRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all companies - tags: - - company /api/v1/super-login: post: consumes: @@ -2985,96 +1048,6 @@ paths: summary: Login super-admin tags: - auth - /api/v1/supportedOperation: - get: - consumes: - - application/json - description: Gets all supported operations - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/domain.BranchDetailRes' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Gets all supported operations - tags: - - branch - post: - consumes: - - application/json - description: Creates a supported operation - parameters: - - description: Creates supported operation - in: body - name: createSupportedOperation - required: true - schema: - $ref: '#/definitions/domain.CreateSupportedOperationReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.SupportedOperationRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Create a supported operation - tags: - - branch - /api/v1/t-approver: - get: - consumes: - - application/json - description: Get all Admins - parameters: - - description: Page number - in: query - name: page - type: integer - - description: Page size - in: query - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.AdminRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all Admins - tags: - - admin /api/v1/t-approver/{id}: get: consumes: @@ -3171,133 +1144,6 @@ paths: summary: Check if phone number or email exist tags: - user - /api/v1/tenant/{tenant_slug}/customer: - get: - consumes: - - application/json - description: Get all Customers - parameters: - - description: Page number - in: query - name: page - type: integer - - description: Page size - in: query - name: page_size - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.CustomersRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all Customers - tags: - - customer - /api/v1/tenant/{tenant_slug}/customer/{id}: - get: - consumes: - - application/json - description: Get a single customer by id - parameters: - - description: User ID - in: path - name: id - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handlers.CustomersRes' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get customer by id - tags: - - customer - put: - consumes: - - application/json - description: Update Customers - parameters: - - description: Update Customers - in: body - name: Customers - required: true - schema: - $ref: '#/definitions/handlers.updateCustomerReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Update Customers - tags: - - customer - /api/v1/tenant/{tenant_slug}/referral/stats: - get: - consumes: - - application/json - description: Retrieves referral statistics for the authenticated user - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.ReferralStats' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - security: - - Bearer: [] - summary: Get referral statistics - tags: - - referral /api/v1/user/delete/{id}: delete: consumes: diff --git a/gen/db/auth.sql.go b/gen/db/auth.sql.go index d916ed8..c4bae11 100644 --- a/gen/db/auth.sql.go +++ b/gen/db/auth.sql.go @@ -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 diff --git a/gen/db/issue_reporting.sql.go b/gen/db/issue_reporting.sql.go new file mode 100644 index 0000000..7fcb4af --- /dev/null +++ b/gen/db/issue_reporting.sql.go @@ -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 +} diff --git a/gen/db/notification.sql.go b/gen/db/notification.sql.go new file mode 100644 index 0000000..95c851e --- /dev/null +++ b/gen/db/notification.sql.go @@ -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 +} diff --git a/gen/db/settings.sql.go b/gen/db/settings.sql.go new file mode 100644 index 0000000..827a9ec --- /dev/null +++ b/gen/db/settings.sql.go @@ -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 +} diff --git a/gen/db/user.sql.go b/gen/db/user.sql.go index 0c1f9c2..9637e29 100644 --- a/gen/db/user.sql.go +++ b/gen/db/user.sql.go @@ -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,29 +226,30 @@ SELECT id, suspended_at, organization_id FROM users -wHERE ( +WHERE ( role = $1 OR $1 IS NULL ) - AND ( + AND ( organization_id = $2 OR $2 IS NULL ) - AND ( + AND ( first_name ILIKE '%' || $3 || '%' OR last_name ILIKE '%' || $3 || '%' OR phone_number ILIKE '%' || $3 || '%' OR $3 IS NULL ) - AND ( + AND ( created_at > $4 OR $4 IS NULL ) - AND ( + AND ( 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, diff --git a/go.mod b/go.mod index 9f374c4..135888f 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index ce3fe7d..6f7e85b 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/config/config.go b/internal/config/config.go index fdf4bb2..21d5f7e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -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") diff --git a/internal/domain/chapa.go b/internal/domain/chapa.go deleted file mode 100644 index ef749d9..0000000 --- a/internal/domain/chapa.go +++ /dev/null @@ -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"` -} diff --git a/internal/domain/direct_deposit.go b/internal/domain/direct_deposit.go deleted file mode 100644 index 7ee2408..0000000 --- a/internal/domain/direct_deposit.go +++ /dev/null @@ -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 -} diff --git a/internal/domain/institutions.go b/internal/domain/institutions.go deleted file mode 100644 index 431f64e..0000000 --- a/internal/domain/institutions.go +++ /dev/null @@ -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 -} diff --git a/internal/domain/notification.go b/internal/domain/notification.go index 918a47e..e1de9d2 100644 --- a/internal/domain/notification.go +++ b/internal/domain/notification.go @@ -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 "" diff --git a/internal/domain/referal.go b/internal/domain/referal.go deleted file mode 100644 index e4ed32d..0000000 --- a/internal/domain/referal.go +++ /dev/null @@ -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(), -// } -// } diff --git a/internal/domain/role.go b/internal/domain/role.go index f1ddfc4..a974dc2 100644 --- a/internal/domain/role.go +++ b/internal/domain/role.go @@ -3,19 +3,22 @@ package domain 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" + RoleSuperAdmin Role = "super_admin" + RoleAdmin Role = "admin" + 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) +} diff --git a/internal/domain/setting_list.go b/internal/domain/setting_list.go index 5011660..b82085a 100644 --- a/internal/domain/setting_list.go +++ b/internal/domain/setting_list.go @@ -1,673 +1,673 @@ package domain -// import ( -// "errors" -// "fmt" -// "strconv" -// "strings" -// "time" - -// dbgen "Yimaru-Backend/gen/db" -// "go.uber.org/zap" -// ) - -// var ( -// ErrSettingNotFound = errors.New("cannot find setting in list") -// ) - -// type SettingList struct { -// SMSProvider SMSProvider `json:"sms_provider"` -// MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"` -// MaxUnsettledBets int64 `json:"max_unsettled_bets"` -// BetAmountLimit Currency `json:"bet_amount_limit"` -// DailyTicketPerIP int64 `json:"daily_ticket_limit"` -// TotalWinningLimit Currency `json:"total_winning_limit"` -// TotalWinningNotify Currency `json:"total_winning_notify"` -// AmountForBetReferral Currency `json:"amount_for_bet_referral"` -// CashbackAmountCap Currency `json:"cashback_amount_cap"` -// DefaultWinningLimit int64 `json:"default_winning_limit"` -// ReferralRewardAmount Currency `json:"referral_reward_amount"` -// CashbackPercentage float32 `json:"cashback_percentage"` -// DefaultMaxReferrals int64 `json:"default_max_referrals"` -// MinimumBetAmount Currency `json:"minimum_bet_amount"` -// BetDuplicateLimit int64 `json:"bet_duplicate_limit"` -// SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"` -// SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"` -// WelcomeBonusActive bool `json:"welcome_bonus_active"` -// WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"` -// WelcomeBonusCap Currency `json:"welcome_bonus_cap"` -// WelcomeBonusCount int64 `json:"welcome_bonus_count"` -// WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"` -// } - -// // Default Global Settings added to the database if the setting isn't found -// // This is already configured in the seed_data.sql, but just added it here too -// // in case of database to code drift -// func NewDefaultSettingList() SettingList { -// return SettingList{ -// SMSProvider: AfroMessage, //Provider that will be used for sending sms -// MaxNumberOfOutcomes: 30, //Maximum number of outcomes for a single bet -// MaxUnsettledBets: 100, //Maximum number of unsettled bets before system disabled bet service -// BetAmountLimit: 10000000, //Maximum amount that can be bet (100,000.00 Birr Limit) -// DailyTicketPerIP: 50, //Daily Number of Tickets That can be cut -// TotalWinningLimit: 100000000, //Winning Limit (1,000,000.00 Birr) -// TotalWinningNotify: 10000000, //Notify if user wins (100,000.00+ Birr) -// AmountForBetReferral: 1000000, //Reward for bet referral (only for betting) (10,000.00 Birr) -// CashbackAmountCap: 10.00, //Cashback amount limit (10 Birr) -// DefaultWinningLimit: 5000000, //Birr Limit on single event (50,000.00) -// ReferralRewardAmount: 10000, //Reward for user referral (100.00 Birr) -// CashbackPercentage: 0.2, //Cashback Percent (20%) -// DefaultMaxReferrals: 15, //Max number of user referrals (15) -// MinimumBetAmount: 100, //Minimum Bet Amount (1 Birr) -// BetDuplicateLimit: 5, //Maximum number of duplicate bets (5) -// SendEmailOnBetFinish: true, //Whether to send email to user when he wins bet -// SendSMSOnBetFinish: false, //Whether to send sms to user when he wins bet -// WelcomeBonusActive: false, //Is Welcome Bonus in effect -// WelcomeBonusMultiplier: 1.5, //Welcome Bonus Multiplier -// WelcomeBonusCap: 100000, //Welcome Bonus Limit -// WelcomeBonusCount: 3, //Maximum number of welcome bonuses given -// WelcomeBonusExpire: 10, //Welcome Bonus Expiry -// } -// } - -// func (s SettingList) ToSettingArray() []Setting { -// return []Setting{ -// {"sms_provider", string(s.SMSProvider), time.Now()}, -// {"max_number_of_outcomes", strconv.FormatInt(s.MaxNumberOfOutcomes, 10), time.Now()}, -// {"max_unsettled_bets", strconv.FormatInt(s.MaxUnsettledBets, 10), time.Now()}, -// {"bet_amount_limit", strconv.FormatInt(int64(s.BetAmountLimit), 10), time.Now()}, -// {"daily_ticket_limit", strconv.FormatInt(s.DailyTicketPerIP, 10), time.Now()}, -// {"total_winnings_limit", strconv.FormatInt(int64(s.TotalWinningLimit), 10), time.Now()}, -// {"total_winnings_notify", strconv.FormatInt(int64(s.TotalWinningNotify), 10), time.Now()}, -// {"amount_for_bet_referral", strconv.FormatInt(int64(s.AmountForBetReferral), 10), time.Now()}, -// {"cashback_amount_cap", strconv.FormatInt(int64(s.CashbackAmountCap), 10), time.Now()}, -// {"default_winning_limit", strconv.FormatInt(s.DefaultWinningLimit, 10), time.Now()}, -// {"referral_reward_amount", strconv.FormatInt(int64(s.ReferralRewardAmount), 10), time.Now()}, -// {"cashback_percentage", strconv.FormatFloat(float64(s.CashbackPercentage), 'f', -1, 32), time.Now()}, -// {"default_max_referrals", strconv.FormatInt(s.DefaultMaxReferrals, 10), time.Now()}, -// {"minimum_bet_amount", strconv.FormatInt(int64(s.MinimumBetAmount), 10), time.Now()}, -// {"bet_duplicate_limit", strconv.FormatInt(s.BetDuplicateLimit, 10), time.Now()}, -// {"send_email_on_bet_finish", strconv.FormatBool(s.SendEmailOnBetFinish), time.Now()}, -// {"send_sms_on_bet_finish", strconv.FormatBool(s.SendSMSOnBetFinish), time.Now()}, -// {"welcome_bonus_active", strconv.FormatBool(s.WelcomeBonusActive), time.Now()}, -// {"welcome_bonus_multiplier", strconv.FormatFloat(float64(s.WelcomeBonusMultiplier), 'f', -1, 32), time.Now()}, -// {"welcome_bonus_cap", strconv.FormatInt(int64(s.WelcomeBonusCap), 10), time.Now()}, -// {"welcome_bonus_count", strconv.FormatInt(s.WelcomeBonusCount, 10), time.Now()}, -// {"welcome_bonus_expiry", strconv.FormatInt(s.WelcomeBonusExpire, 10), time.Now()}, -// } -// } - -// type SettingListRes struct { -// SMSProvider SMSProvider `json:"sms_provider"` -// MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"` -// MaxUnsettledBets int64 `json:"max_unsettled_bets"` -// BetAmountLimit float32 `json:"bet_amount_limit"` -// DailyTicketPerIP int64 `json:"daily_ticket_limit"` -// TotalWinningLimit float32 `json:"total_winning_limit"` -// TotalWinningNotify float32 `json:"total_winning_notify"` -// AmountForBetReferral float32 `json:"amount_for_bet_referral"` -// CashbackAmountCap float32 `json:"cashback_amount_cap"` -// DefaultWinningLimit int64 `json:"default_winning_limit"` -// ReferralRewardAmount float32 `json:"referral_reward_amount"` -// CashbackPercentage float32 `json:"cashback_percentage"` -// DefaultMaxReferrals int64 `json:"default_max_referrals"` -// MinimumBetAmount float32 `json:"minimum_bet_amount"` -// BetDuplicateLimit int64 `json:"bet_duplicate_limit"` -// SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"` -// SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"` -// WelcomeBonusActive bool `json:"welcome_bonus_active"` -// WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"` -// WelcomeBonusCap float32 `json:"welcome_bonus_cap"` -// WelcomeBonusCount int64 `json:"welcome_bonus_count"` -// WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"` -// } - -// func ConvertSettingListRes(settings SettingList) SettingListRes { -// return SettingListRes{ -// SMSProvider: settings.SMSProvider, -// MaxNumberOfOutcomes: settings.MaxNumberOfOutcomes, -// MaxUnsettledBets: settings.MaxUnsettledBets, -// BetAmountLimit: settings.BetAmountLimit.Float32(), -// DailyTicketPerIP: settings.DailyTicketPerIP, -// TotalWinningLimit: settings.TotalWinningLimit.Float32(), -// TotalWinningNotify: settings.TotalWinningNotify.Float32(), -// AmountForBetReferral: settings.AmountForBetReferral.Float32(), -// CashbackAmountCap: settings.CashbackAmountCap.Float32(), -// DefaultWinningLimit: settings.DefaultWinningLimit, -// ReferralRewardAmount: settings.ReferralRewardAmount.Float32(), -// CashbackPercentage: settings.CashbackPercentage, -// DefaultMaxReferrals: settings.DefaultMaxReferrals, -// MinimumBetAmount: settings.MinimumBetAmount.Float32(), -// BetDuplicateLimit: settings.BetDuplicateLimit, -// SendEmailOnBetFinish: settings.SendEmailOnBetFinish, -// SendSMSOnBetFinish: settings.SendSMSOnBetFinish, -// WelcomeBonusActive: settings.WelcomeBonusActive, -// WelcomeBonusMultiplier: settings.WelcomeBonusMultiplier, -// WelcomeBonusCap: settings.WelcomeBonusCap.Float32(), -// WelcomeBonusCount: settings.WelcomeBonusCount, -// WelcomeBonusExpire: settings.WelcomeBonusExpire, -// } -// } - -// type SaveSettingListReq struct { -// SMSProvider *string `json:"sms_provider,omitempty"` -// MaxNumberOfOutcomes *int64 `json:"max_number_of_outcomes,omitempty"` -// MaxUnsettledBets *int64 `json:"max_unsettled_bets,omitempty"` -// BetAmountLimit *float32 `json:"bet_amount_limit,omitempty"` -// DailyTicketPerIP *int64 `json:"daily_ticket_limit,omitempty"` -// TotalWinningLimit *float32 `json:"total_winning_limit,omitempty"` -// TotalWinningNotify *float32 `json:"total_winning_notify,omitempty"` -// AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"` -// CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"` -// DefaultWinningLimit *int64 `json:"default_winning_limit,omitempty"` -// ReferralRewardAmount *float32 `json:"referral_reward_amount,omitempty"` -// CashbackPercentage *float32 `json:"cashback_percentage,omitempty"` -// DefaultMaxReferrals *int64 `json:"default_max_referrals,omitempty"` -// MinimumBetAmount *float32 `json:"minimum_bet_amount,omitempty"` -// BetDuplicateLimit *int64 `json:"bet_duplicate_limit,omitempty"` -// SendEmailOnBetFinish *bool `json:"send_email_on_bet_finish,omitempty"` -// SendSMSOnBetFinish *bool `json:"send_sms_on_bet_finish,omitempty"` -// WelcomeBonusActive *bool `json:"welcome_bonus_active,omitempty"` -// WelcomeBonusMultiplier *float32 `json:"welcome_bonus_multiplier,omitempty"` -// WelcomeBonusCap *float32 `json:"welcome_bonus_cap,omitempty"` -// WelcomeBonusCount *int64 `json:"welcome_bonus_count,omitempty"` -// WelcomeBonusExpire *int64 `json:"welcome_bonus_expiry,omitempty"` -// } - -// type ValidSettingList struct { -// SMSProvider ValidString -// MaxNumberOfOutcomes ValidInt64 -// MaxUnsettledBets ValidInt64 -// BetAmountLimit ValidCurrency -// DailyTicketPerIP ValidInt64 -// TotalWinningLimit ValidCurrency -// TotalWinningNotify ValidCurrency -// AmountForBetReferral ValidCurrency -// CashbackAmountCap ValidCurrency -// DefaultWinningLimit ValidInt64 -// ReferralRewardAmount ValidCurrency -// CashbackPercentage ValidFloat32 -// DefaultMaxReferrals ValidInt64 -// MinimumBetAmount ValidCurrency -// BetDuplicateLimit ValidInt64 -// SendEmailOnBetFinish ValidBool -// SendSMSOnBetFinish ValidBool -// WelcomeBonusActive ValidBool -// WelcomeBonusMultiplier ValidFloat32 -// WelcomeBonusCap ValidCurrency -// WelcomeBonusCount ValidInt64 -// WelcomeBonusExpire ValidInt64 -// } - -// func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList { -// return ValidSettingList{ -// SMSProvider: ConvertStringPtr(settings.SMSProvider), -// MaxNumberOfOutcomes: ConvertInt64Ptr(settings.MaxNumberOfOutcomes), -// MaxUnsettledBets: ConvertInt64Ptr(settings.MaxUnsettledBets), -// BetAmountLimit: ConvertFloat32PtrToCurrency(settings.BetAmountLimit), -// DailyTicketPerIP: ConvertInt64Ptr(settings.DailyTicketPerIP), -// TotalWinningLimit: ConvertFloat32PtrToCurrency(settings.TotalWinningLimit), -// TotalWinningNotify: ConvertFloat32PtrToCurrency(settings.TotalWinningNotify), -// AmountForBetReferral: ConvertFloat32PtrToCurrency(settings.AmountForBetReferral), -// CashbackAmountCap: ConvertFloat32PtrToCurrency(settings.CashbackAmountCap), -// DefaultWinningLimit: ConvertInt64Ptr(settings.DefaultWinningLimit), -// ReferralRewardAmount: ConvertFloat32PtrToCurrency(settings.ReferralRewardAmount), -// CashbackPercentage: ConvertFloat32Ptr(settings.CashbackPercentage), -// DefaultMaxReferrals: ConvertInt64Ptr(settings.DefaultMaxReferrals), -// MinimumBetAmount: ConvertFloat32PtrToCurrency(settings.MinimumBetAmount), -// BetDuplicateLimit: ConvertInt64Ptr(settings.BetDuplicateLimit), -// SendEmailOnBetFinish: ConvertBoolPtr(settings.SendEmailOnBetFinish), -// SendSMSOnBetFinish: ConvertBoolPtr(settings.SendSMSOnBetFinish), -// WelcomeBonusActive: ConvertBoolPtr(settings.WelcomeBonusActive), -// WelcomeBonusMultiplier: ConvertFloat32Ptr(settings.WelcomeBonusMultiplier), -// WelcomeBonusCap: ConvertFloat32PtrToCurrency(settings.WelcomeBonusCap), -// WelcomeBonusCount: ConvertInt64Ptr(settings.WelcomeBonusCount), -// WelcomeBonusExpire: ConvertInt64Ptr(settings.WelcomeBonusExpire), -// } -// } - -// // Always make sure to run the validation before converting this -// func (vsl *ValidSettingList) ToSettingList() SettingList { -// return SettingList{ -// SMSProvider: SMSProvider(vsl.SMSProvider.Value), -// MaxNumberOfOutcomes: vsl.MaxNumberOfOutcomes.Value, -// MaxUnsettledBets: vsl.MaxUnsettledBets.Value, -// BetAmountLimit: vsl.BetAmountLimit.Value, -// DailyTicketPerIP: vsl.DailyTicketPerIP.Value, -// TotalWinningLimit: vsl.TotalWinningLimit.Value, -// TotalWinningNotify: vsl.TotalWinningNotify.Value, -// AmountForBetReferral: vsl.AmountForBetReferral.Value, -// CashbackAmountCap: vsl.CashbackAmountCap.Value, -// DefaultWinningLimit: vsl.DefaultWinningLimit.Value, -// ReferralRewardAmount: vsl.ReferralRewardAmount.Value, -// CashbackPercentage: vsl.CashbackPercentage.Value, -// DefaultMaxReferrals: vsl.DefaultMaxReferrals.Value, -// MinimumBetAmount: vsl.MinimumBetAmount.Value, -// BetDuplicateLimit: vsl.BetDuplicateLimit.Value, -// SendEmailOnBetFinish: vsl.SendEmailOnBetFinish.Value, -// SendSMSOnBetFinish: vsl.SendSMSOnBetFinish.Value, -// WelcomeBonusActive: vsl.WelcomeBonusActive.Value, -// WelcomeBonusMultiplier: vsl.WelcomeBonusMultiplier.Value, -// WelcomeBonusCap: vsl.WelcomeBonusCap.Value, -// WelcomeBonusCount: vsl.WelcomeBonusCount.Value, -// WelcomeBonusExpire: vsl.WelcomeBonusExpire.Value, -// } -// } - -// // Custom Validations for non-generic types -// func (vsl *ValidSettingList) CustomValidationSettings() error { -// if !SMSProvider(vsl.SMSProvider.Value).IsValid() { -// return fmt.Errorf("sms provider invalid") -// } -// return nil -// } - -// func (vsl *ValidSettingList) GetInt64SettingsMap() map[string]*ValidInt64 { -// return map[string]*ValidInt64{ -// "max_number_of_outcomes": &vsl.MaxNumberOfOutcomes, -// "max_unsettled_bets": &vsl.MaxUnsettledBets, -// "daily_ticket_limit": &vsl.DailyTicketPerIP, -// "default_winning_limit": &vsl.DefaultWinningLimit, -// "default_max_referrals": &vsl.DefaultMaxReferrals, -// "bet_duplicate_limit": &vsl.BetDuplicateLimit, -// "welcome_bonus_count": &vsl.WelcomeBonusCount, -// "welcome_bonus_expiry": &vsl.WelcomeBonusExpire, -// } -// } - -// func (vsl *ValidSettingList) GetCurrencySettingsMap() map[string]*ValidCurrency { -// return map[string]*ValidCurrency{ -// "bet_amount_limit": &vsl.BetAmountLimit, -// "total_winnings_limit": &vsl.TotalWinningLimit, -// "total_winnings_notify": &vsl.TotalWinningNotify, -// "amount_for_bet_referral": &vsl.AmountForBetReferral, -// "cashback_amount_cap": &vsl.CashbackAmountCap, -// "referral_reward_amount": &vsl.ReferralRewardAmount, -// "minimum_bet_amount": &vsl.MinimumBetAmount, -// "welcome_bonus_cap": &vsl.WelcomeBonusCap, -// } -// } - -// func (vsl *ValidSettingList) GetStringSettingsMap() map[string]*ValidString { -// return map[string]*ValidString{ -// "sms_provider": &vsl.SMSProvider, -// } -// } - -// func (vsl *ValidSettingList) GetBoolSettingsMap() map[string]*ValidBool { -// return map[string]*ValidBool{ -// "send_email_on_bet_finish": &vsl.SendEmailOnBetFinish, -// "send_sms_on_bet_finish": &vsl.SendSMSOnBetFinish, -// "welcome_bonus_active": &vsl.WelcomeBonusActive, -// } -// } - -// func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 { -// return map[string]*ValidFloat32{ -// "cashback_percentage": &vsl.CashbackPercentage, -// "welcome_bonus_multiplier": &vsl.WelcomeBonusMultiplier, -// } -// } - -// func (vsl *ValidSettingList) GetTimeSettingsMap() map[string]*ValidTime { -// return map[string]*ValidTime{} -// } - -// // Setting Functions - -// func (vsl *ValidSettingList) GetTotalSettings() int { -// return len(vsl.GetInt64SettingsMap()) + -// len(vsl.GetCurrencySettingsMap()) + -// len(vsl.GetStringSettingsMap()) + -// len(vsl.GetBoolSettingsMap()) + -// len(vsl.GetFloat32SettingsMap()) + -// len(vsl.GetTimeSettingsMap()) -// } - -// func (vsl *ValidSettingList) GetAllValid() map[string]*bool { - -// settingValid := make(map[string]*bool) - -// for key, setting := range vsl.GetInt64SettingsMap() { -// settingValid[key] = &(*setting).Valid -// } -// for key, setting := range vsl.GetCurrencySettingsMap() { -// settingValid[key] = &(*setting).Valid -// } -// for key, setting := range vsl.GetStringSettingsMap() { -// settingValid[key] = &(*setting).Valid -// } -// for key, setting := range vsl.GetBoolSettingsMap() { -// settingValid[key] = &(*setting).Valid -// } -// for key, setting := range vsl.GetFloat32SettingsMap() { -// settingValid[key] = &(*setting).Valid -// } -// for key, setting := range vsl.GetTimeSettingsMap() { -// settingValid[key] = &(*setting).Valid -// } - -// return settingValid -// } - -// // func setValidSetting[T any](settings map[string]*T, searchKey string, searchVal string, setVal func(string) (T, error)) error { -// // for key, setting := range settings { - -// // if key == searchKey { -// // s, err := setVal(searchVal) -// // if err != nil { -// // return err -// // } -// // *setting = s -// // } -// // return nil -// // } -// // return ErrSettingNotFound -// // } -// func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error { -// for key, setting := range vsl.GetInt64SettingsMap() { -// if key == searchKey { -// value, err := strconv.ParseInt(searchVal, 10, 64) -// if err != nil { -// return err -// } -// *setting = ValidInt64{Value: value, Valid: true} -// return nil -// } -// } -// return ErrSettingNotFound -// } - -// func (vsl *ValidSettingList) SetCurrencySetting(searchKey string, searchVal string) error { -// for key, setting := range vsl.GetCurrencySettingsMap() { -// if key == searchKey { -// value, err := strconv.ParseInt(searchVal, 10, 64) -// if err != nil { -// return err -// } -// *setting = ValidCurrency{Value: Currency(value), Valid: true} -// return nil -// } -// } - -// return ErrSettingNotFound -// } - -// func (vsl *ValidSettingList) SetStringSetting(searchKey string, searchVal string) error { -// for key, setting := range vsl.GetStringSettingsMap() { -// if key == searchKey { -// *setting = ValidString{Value: searchVal, Valid: true} -// return nil -// } -// } - -// return ErrSettingNotFound -// } - -// func (vsl *ValidSettingList) SetBoolSetting(searchKey string, searchVal string) error { -// for key, setting := range vsl.GetBoolSettingsMap() { - -// if key == searchKey { -// value, err := strconv.ParseBool(searchVal) -// if err != nil { -// return err -// } - -// *setting = ValidBool{Value: value, Valid: true} -// return nil -// } - -// } - -// return ErrSettingNotFound -// } - -// func (vsl *ValidSettingList) SetFloat32Setting(searchKey string, searchVal string) error { -// for key, setting := range vsl.GetFloat32SettingsMap() { -// if key == searchKey { -// value, err := strconv.ParseFloat(searchVal, 32) -// if err != nil { -// return err -// } -// *setting = ValidFloat32{Value: float32(value), Valid: true} -// return nil -// } -// } - -// return ErrSettingNotFound -// } - -// func (vsl *ValidSettingList) SetTimeSetting(searchKey string, searchVal string) error { -// for key, setting := range vsl.GetTimeSettingsMap() { -// if key == searchKey { -// value, err := time.Parse(time.RFC3339, searchVal) -// if err != nil { -// return err -// } -// *setting = ValidTime{Value: value, Valid: true} -// return nil -// } -// } -// return ErrSettingNotFound -// } - -// func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) error { -// setters := []func(string, string) error{ -// vsl.SetInt64Setting, -// vsl.SetCurrencySetting, -// vsl.SetStringSetting, -// vsl.SetBoolSetting, -// vsl.SetFloat32Setting, -// vsl.SetTimeSetting, -// } - -// for _, setter := range setters { -// if err := setter(searchKey, searchVal); err != nil { -// if err == ErrSettingNotFound { -// // fmt.Printf("setting is not found %v \n", searchKey) -// continue // not this setter, try the next -// } -// return fmt.Errorf("error while processing setting %q: %w \n", searchKey, err) -// } -// return nil // successfully set -// } - -// // If we get here, none of the setters matched -// return ErrSettingNotFound -// } - -// func convertValidSettings[T any]( -// settings map[string]*T, -// isValid func(*T) bool, -// toString func(*T) string, -// ) []Setting { -// result := make([]Setting, 0, len(settings)) -// for key, s := range settings { -// if isValid(s) { -// result = append(result, Setting{ -// Key: key, -// Value: toString(s), -// }) -// } -// } -// return result -// } - -// func (vsl *ValidSettingList) ConvertInt64Settings() []Setting { -// return convertValidSettings( -// vsl.GetInt64SettingsMap(), -// func(s *ValidInt64) bool { return s.Valid }, -// func(s *ValidInt64) string { return strconv.FormatInt(s.Value, 10) }, -// ) -// } - -// func (vsl *ValidSettingList) ConvertCurrencySettings() []Setting { -// return convertValidSettings( -// vsl.GetCurrencySettingsMap(), -// func(s *ValidCurrency) bool { return s.Valid }, -// func(s *ValidCurrency) string { return strconv.FormatInt(int64(s.Value), 10) }, -// ) -// } - -// func (vsl *ValidSettingList) ConvertStringSettings() []Setting { -// return convertValidSettings( -// vsl.GetStringSettingsMap(), -// func(s *ValidString) bool { return s.Valid }, -// func(s *ValidString) string { return s.Value }, -// ) -// } - -// func (vsl *ValidSettingList) ConvertBoolSettings() []Setting { -// return convertValidSettings( -// vsl.GetBoolSettingsMap(), -// func(s *ValidBool) bool { return s.Valid }, -// func(s *ValidBool) string { return strconv.FormatBool(s.Value) }, -// ) -// } - -// func (vsl *ValidSettingList) ConvertFloat32Settings() []Setting { -// return convertValidSettings( -// vsl.GetFloat32SettingsMap(), -// func(s *ValidFloat32) bool { return s.Valid }, -// func(s *ValidFloat32) string { return strconv.FormatFloat(float64(s.Value), 'f', -1, 32) }, -// ) -// } - -// func (vsl *ValidSettingList) ConvertTimeSettings() []Setting { -// return convertValidSettings( -// vsl.GetTimeSettingsMap(), -// func(s *ValidTime) bool { return s.Valid }, -// func(s *ValidTime) string { return s.Value.Format(time.RFC3339) }, -// ) -// } - -// func validateSettings[T any]( -// settings map[string]*T, -// customValidator func(*T) bool, -// ) error { -// var errs []string -// for key, s := range settings { -// if !customValidator(s) { - -// errs = append(errs, fmt.Sprintf("%v is invalid", key)) -// } -// } -// if len(errs) > 0 { -// return errors.New(strings.Join(errs, "; ")) -// } - -// return nil -// } - -// func (vsl *ValidSettingList) ValidateInt64Settings() error { -// return validateSettings(vsl.GetInt64SettingsMap(), -// func(s *ValidInt64) bool { -// return s.Valid -// }, -// ) -// } - -// func (vsl *ValidSettingList) ValidateCurrencySettings() error { -// return validateSettings(vsl.GetCurrencySettingsMap(), -// func(s *ValidCurrency) bool { -// return s.Valid -// }, -// ) -// } - -// func (vsl *ValidSettingList) ValidateStringSettings() error { -// return validateSettings(vsl.GetStringSettingsMap(), -// func(s *ValidString) bool { -// return s.Valid -// }, -// ) -// } - -// func (vsl *ValidSettingList) ValidateBoolSettings() error { -// return validateSettings(vsl.GetBoolSettingsMap(), -// func(s *ValidBool) bool { -// return s.Valid -// }, -// ) -// } - -// func (vsl *ValidSettingList) ValidateFloat32Settings() error { -// return validateSettings(vsl.GetFloat32SettingsMap(), -// func(s *ValidFloat32) bool { -// return s.Valid -// }, -// ) -// } - -// func (vsl *ValidSettingList) ValidateTimeSettings() error { -// return validateSettings(vsl.GetTimeSettingsMap(), -// func(s *ValidTime) bool { -// return s.Valid -// }, -// ) -// } - -// func (vsl *ValidSettingList) ValidateAllSettings() error { -// var errs []string -// validators := []func() error{ -// vsl.ValidateInt64Settings, -// vsl.ValidateCurrencySettings, -// vsl.ValidateStringSettings, -// vsl.ValidateBoolSettings, -// vsl.ValidateFloat32Settings, -// vsl.ValidateTimeSettings, -// vsl.CustomValidationSettings, -// } - -// for _, validator := range validators { -// if err := validator(); err != nil { - -// errs = append(errs, err.Error()) -// } -// } -// if len(errs) > 0 { -// return errors.New(strings.Join(errs, "; ")) -// } - -// return nil -// } - -// func (vsl *ValidSettingList) ConvertAllSettings() []Setting { -// totalCap := vsl.GetTotalSettings() -// all := make([]Setting, 0, totalCap) - -// all = append(all, vsl.ConvertInt64Settings()...) -// all = append(all, vsl.ConvertCurrencySettings()...) -// all = append(all, vsl.ConvertStringSettings()...) -// all = append(all, vsl.ConvertBoolSettings()...) -// all = append(all, vsl.ConvertFloat32Settings()...) -// all = append(all, vsl.ConvertTimeSettings()...) - -// return all -// } - -// func ConvertDBGlobalSettingList(settings []dbgen.GlobalSetting) (SettingList, error) { -// var dbSettingList ValidSettingList - -// for _, setting := range settings { -// if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil { -// if err == ErrSettingNotFound { -// MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key)) -// continue -// } -// MongoDBLogger.Error("unknown error while fetching settings", zap.Error(err)) -// } -// } - -// if err := dbSettingList.ValidateAllSettings(); err != nil { -// MongoDBLogger.Warn("setting validation error", zap.Error(err), zap.Any("db_setting_list", dbSettingList)) -// return SettingList{}, err -// } - -// settingList := dbSettingList.ToSettingList() - -// return settingList, nil -// } +import ( + dbgen "Yimaru-Backend/gen/db" + "errors" + "fmt" + "strconv" + "strings" + "time" + + "go.uber.org/zap" +) + +var ( + ErrSettingNotFound = errors.New("cannot find setting in list") +) + +type SettingList struct { + SMSProvider SMSProvider `json:"sms_provider"` + MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"` + MaxUnsettledBets int64 `json:"max_unsettled_bets"` + BetAmountLimit Currency `json:"bet_amount_limit"` + DailyTicketPerIP int64 `json:"daily_ticket_limit"` + TotalWinningLimit Currency `json:"total_winning_limit"` + TotalWinningNotify Currency `json:"total_winning_notify"` + AmountForBetReferral Currency `json:"amount_for_bet_referral"` + CashbackAmountCap Currency `json:"cashback_amount_cap"` + DefaultWinningLimit int64 `json:"default_winning_limit"` + ReferralRewardAmount Currency `json:"referral_reward_amount"` + CashbackPercentage float32 `json:"cashback_percentage"` + DefaultMaxReferrals int64 `json:"default_max_referrals"` + MinimumBetAmount Currency `json:"minimum_bet_amount"` + BetDuplicateLimit int64 `json:"bet_duplicate_limit"` + SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"` + SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"` + WelcomeBonusActive bool `json:"welcome_bonus_active"` + WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"` + WelcomeBonusCap Currency `json:"welcome_bonus_cap"` + WelcomeBonusCount int64 `json:"welcome_bonus_count"` + WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"` +} + +// Default Global Settings added to the database if the setting isn't found +// This is already configured in the seed_data.sql, but just added it here too +// in case of database to code drift +func NewDefaultSettingList() SettingList { + return SettingList{ + SMSProvider: AfroMessage, //Provider that will be used for sending sms + MaxNumberOfOutcomes: 30, //Maximum number of outcomes for a single bet + MaxUnsettledBets: 100, //Maximum number of unsettled bets before system disabled bet service + BetAmountLimit: 10000000, //Maximum amount that can be bet (100,000.00 Birr Limit) + DailyTicketPerIP: 50, //Daily Number of Tickets That can be cut + TotalWinningLimit: 100000000, //Winning Limit (1,000,000.00 Birr) + TotalWinningNotify: 10000000, //Notify if user wins (100,000.00+ Birr) + AmountForBetReferral: 1000000, //Reward for bet referral (only for betting) (10,000.00 Birr) + CashbackAmountCap: 10.00, //Cashback amount limit (10 Birr) + DefaultWinningLimit: 5000000, //Birr Limit on single event (50,000.00) + ReferralRewardAmount: 10000, //Reward for user referral (100.00 Birr) + CashbackPercentage: 0.2, //Cashback Percent (20%) + DefaultMaxReferrals: 15, //Max number of user referrals (15) + MinimumBetAmount: 100, //Minimum Bet Amount (1 Birr) + BetDuplicateLimit: 5, //Maximum number of duplicate bets (5) + SendEmailOnBetFinish: true, //Whether to send email to user when he wins bet + SendSMSOnBetFinish: false, //Whether to send sms to user when he wins bet + WelcomeBonusActive: false, //Is Welcome Bonus in effect + WelcomeBonusMultiplier: 1.5, //Welcome Bonus Multiplier + WelcomeBonusCap: 100000, //Welcome Bonus Limit + WelcomeBonusCount: 3, //Maximum number of welcome bonuses given + WelcomeBonusExpire: 10, //Welcome Bonus Expiry + } +} + +func (s SettingList) ToSettingArray() []Setting { + return []Setting{ + {"sms_provider", string(s.SMSProvider), time.Now()}, + {"max_number_of_outcomes", strconv.FormatInt(s.MaxNumberOfOutcomes, 10), time.Now()}, + {"max_unsettled_bets", strconv.FormatInt(s.MaxUnsettledBets, 10), time.Now()}, + {"bet_amount_limit", strconv.FormatInt(int64(s.BetAmountLimit), 10), time.Now()}, + {"daily_ticket_limit", strconv.FormatInt(s.DailyTicketPerIP, 10), time.Now()}, + {"total_winnings_limit", strconv.FormatInt(int64(s.TotalWinningLimit), 10), time.Now()}, + {"total_winnings_notify", strconv.FormatInt(int64(s.TotalWinningNotify), 10), time.Now()}, + {"amount_for_bet_referral", strconv.FormatInt(int64(s.AmountForBetReferral), 10), time.Now()}, + {"cashback_amount_cap", strconv.FormatInt(int64(s.CashbackAmountCap), 10), time.Now()}, + {"default_winning_limit", strconv.FormatInt(s.DefaultWinningLimit, 10), time.Now()}, + {"referral_reward_amount", strconv.FormatInt(int64(s.ReferralRewardAmount), 10), time.Now()}, + {"cashback_percentage", strconv.FormatFloat(float64(s.CashbackPercentage), 'f', -1, 32), time.Now()}, + {"default_max_referrals", strconv.FormatInt(s.DefaultMaxReferrals, 10), time.Now()}, + {"minimum_bet_amount", strconv.FormatInt(int64(s.MinimumBetAmount), 10), time.Now()}, + {"bet_duplicate_limit", strconv.FormatInt(s.BetDuplicateLimit, 10), time.Now()}, + {"send_email_on_bet_finish", strconv.FormatBool(s.SendEmailOnBetFinish), time.Now()}, + {"send_sms_on_bet_finish", strconv.FormatBool(s.SendSMSOnBetFinish), time.Now()}, + {"welcome_bonus_active", strconv.FormatBool(s.WelcomeBonusActive), time.Now()}, + {"welcome_bonus_multiplier", strconv.FormatFloat(float64(s.WelcomeBonusMultiplier), 'f', -1, 32), time.Now()}, + {"welcome_bonus_cap", strconv.FormatInt(int64(s.WelcomeBonusCap), 10), time.Now()}, + {"welcome_bonus_count", strconv.FormatInt(s.WelcomeBonusCount, 10), time.Now()}, + {"welcome_bonus_expiry", strconv.FormatInt(s.WelcomeBonusExpire, 10), time.Now()}, + } +} + +type SettingListRes struct { + SMSProvider SMSProvider `json:"sms_provider"` + MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"` + MaxUnsettledBets int64 `json:"max_unsettled_bets"` + BetAmountLimit float32 `json:"bet_amount_limit"` + DailyTicketPerIP int64 `json:"daily_ticket_limit"` + TotalWinningLimit float32 `json:"total_winning_limit"` + TotalWinningNotify float32 `json:"total_winning_notify"` + AmountForBetReferral float32 `json:"amount_for_bet_referral"` + CashbackAmountCap float32 `json:"cashback_amount_cap"` + DefaultWinningLimit int64 `json:"default_winning_limit"` + ReferralRewardAmount float32 `json:"referral_reward_amount"` + CashbackPercentage float32 `json:"cashback_percentage"` + DefaultMaxReferrals int64 `json:"default_max_referrals"` + MinimumBetAmount float32 `json:"minimum_bet_amount"` + BetDuplicateLimit int64 `json:"bet_duplicate_limit"` + SendEmailOnBetFinish bool `json:"send_email_on_bet_finish"` + SendSMSOnBetFinish bool `json:"send_sms_on_bet_finish"` + WelcomeBonusActive bool `json:"welcome_bonus_active"` + WelcomeBonusMultiplier float32 `json:"welcome_bonus_multiplier"` + WelcomeBonusCap float32 `json:"welcome_bonus_cap"` + WelcomeBonusCount int64 `json:"welcome_bonus_count"` + WelcomeBonusExpire int64 `json:"welcome_bonus_expiry"` +} + +func ConvertSettingListRes(settings SettingList) SettingListRes { + return SettingListRes{ + SMSProvider: settings.SMSProvider, + MaxNumberOfOutcomes: settings.MaxNumberOfOutcomes, + MaxUnsettledBets: settings.MaxUnsettledBets, + BetAmountLimit: settings.BetAmountLimit.Float32(), + DailyTicketPerIP: settings.DailyTicketPerIP, + TotalWinningLimit: settings.TotalWinningLimit.Float32(), + TotalWinningNotify: settings.TotalWinningNotify.Float32(), + AmountForBetReferral: settings.AmountForBetReferral.Float32(), + CashbackAmountCap: settings.CashbackAmountCap.Float32(), + DefaultWinningLimit: settings.DefaultWinningLimit, + ReferralRewardAmount: settings.ReferralRewardAmount.Float32(), + CashbackPercentage: settings.CashbackPercentage, + DefaultMaxReferrals: settings.DefaultMaxReferrals, + MinimumBetAmount: settings.MinimumBetAmount.Float32(), + BetDuplicateLimit: settings.BetDuplicateLimit, + SendEmailOnBetFinish: settings.SendEmailOnBetFinish, + SendSMSOnBetFinish: settings.SendSMSOnBetFinish, + WelcomeBonusActive: settings.WelcomeBonusActive, + WelcomeBonusMultiplier: settings.WelcomeBonusMultiplier, + WelcomeBonusCap: settings.WelcomeBonusCap.Float32(), + WelcomeBonusCount: settings.WelcomeBonusCount, + WelcomeBonusExpire: settings.WelcomeBonusExpire, + } +} + +type SaveSettingListReq struct { + SMSProvider *string `json:"sms_provider,omitempty"` + MaxNumberOfOutcomes *int64 `json:"max_number_of_outcomes,omitempty"` + MaxUnsettledBets *int64 `json:"max_unsettled_bets,omitempty"` + BetAmountLimit *float32 `json:"bet_amount_limit,omitempty"` + DailyTicketPerIP *int64 `json:"daily_ticket_limit,omitempty"` + TotalWinningLimit *float32 `json:"total_winning_limit,omitempty"` + TotalWinningNotify *float32 `json:"total_winning_notify,omitempty"` + AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"` + CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"` + DefaultWinningLimit *int64 `json:"default_winning_limit,omitempty"` + ReferralRewardAmount *float32 `json:"referral_reward_amount,omitempty"` + CashbackPercentage *float32 `json:"cashback_percentage,omitempty"` + DefaultMaxReferrals *int64 `json:"default_max_referrals,omitempty"` + MinimumBetAmount *float32 `json:"minimum_bet_amount,omitempty"` + BetDuplicateLimit *int64 `json:"bet_duplicate_limit,omitempty"` + SendEmailOnBetFinish *bool `json:"send_email_on_bet_finish,omitempty"` + SendSMSOnBetFinish *bool `json:"send_sms_on_bet_finish,omitempty"` + WelcomeBonusActive *bool `json:"welcome_bonus_active,omitempty"` + WelcomeBonusMultiplier *float32 `json:"welcome_bonus_multiplier,omitempty"` + WelcomeBonusCap *float32 `json:"welcome_bonus_cap,omitempty"` + WelcomeBonusCount *int64 `json:"welcome_bonus_count,omitempty"` + WelcomeBonusExpire *int64 `json:"welcome_bonus_expiry,omitempty"` +} + +type ValidSettingList struct { + SMSProvider ValidString + MaxNumberOfOutcomes ValidInt64 + MaxUnsettledBets ValidInt64 + BetAmountLimit ValidCurrency + DailyTicketPerIP ValidInt64 + TotalWinningLimit ValidCurrency + TotalWinningNotify ValidCurrency + AmountForBetReferral ValidCurrency + CashbackAmountCap ValidCurrency + DefaultWinningLimit ValidInt64 + ReferralRewardAmount ValidCurrency + CashbackPercentage ValidFloat32 + DefaultMaxReferrals ValidInt64 + MinimumBetAmount ValidCurrency + BetDuplicateLimit ValidInt64 + SendEmailOnBetFinish ValidBool + SendSMSOnBetFinish ValidBool + WelcomeBonusActive ValidBool + WelcomeBonusMultiplier ValidFloat32 + WelcomeBonusCap ValidCurrency + WelcomeBonusCount ValidInt64 + WelcomeBonusExpire ValidInt64 +} + +func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList { + return ValidSettingList{ + SMSProvider: ConvertStringPtr(settings.SMSProvider), + MaxNumberOfOutcomes: ConvertInt64Ptr(settings.MaxNumberOfOutcomes), + MaxUnsettledBets: ConvertInt64Ptr(settings.MaxUnsettledBets), + BetAmountLimit: ConvertFloat32PtrToCurrency(settings.BetAmountLimit), + DailyTicketPerIP: ConvertInt64Ptr(settings.DailyTicketPerIP), + TotalWinningLimit: ConvertFloat32PtrToCurrency(settings.TotalWinningLimit), + TotalWinningNotify: ConvertFloat32PtrToCurrency(settings.TotalWinningNotify), + AmountForBetReferral: ConvertFloat32PtrToCurrency(settings.AmountForBetReferral), + CashbackAmountCap: ConvertFloat32PtrToCurrency(settings.CashbackAmountCap), + DefaultWinningLimit: ConvertInt64Ptr(settings.DefaultWinningLimit), + ReferralRewardAmount: ConvertFloat32PtrToCurrency(settings.ReferralRewardAmount), + CashbackPercentage: ConvertFloat32Ptr(settings.CashbackPercentage), + DefaultMaxReferrals: ConvertInt64Ptr(settings.DefaultMaxReferrals), + MinimumBetAmount: ConvertFloat32PtrToCurrency(settings.MinimumBetAmount), + BetDuplicateLimit: ConvertInt64Ptr(settings.BetDuplicateLimit), + SendEmailOnBetFinish: ConvertBoolPtr(settings.SendEmailOnBetFinish), + SendSMSOnBetFinish: ConvertBoolPtr(settings.SendSMSOnBetFinish), + WelcomeBonusActive: ConvertBoolPtr(settings.WelcomeBonusActive), + WelcomeBonusMultiplier: ConvertFloat32Ptr(settings.WelcomeBonusMultiplier), + WelcomeBonusCap: ConvertFloat32PtrToCurrency(settings.WelcomeBonusCap), + WelcomeBonusCount: ConvertInt64Ptr(settings.WelcomeBonusCount), + WelcomeBonusExpire: ConvertInt64Ptr(settings.WelcomeBonusExpire), + } +} + +// Always make sure to run the validation before converting this +func (vsl *ValidSettingList) ToSettingList() SettingList { + return SettingList{ + SMSProvider: SMSProvider(vsl.SMSProvider.Value), + MaxNumberOfOutcomes: vsl.MaxNumberOfOutcomes.Value, + MaxUnsettledBets: vsl.MaxUnsettledBets.Value, + BetAmountLimit: vsl.BetAmountLimit.Value, + DailyTicketPerIP: vsl.DailyTicketPerIP.Value, + TotalWinningLimit: vsl.TotalWinningLimit.Value, + TotalWinningNotify: vsl.TotalWinningNotify.Value, + AmountForBetReferral: vsl.AmountForBetReferral.Value, + CashbackAmountCap: vsl.CashbackAmountCap.Value, + DefaultWinningLimit: vsl.DefaultWinningLimit.Value, + ReferralRewardAmount: vsl.ReferralRewardAmount.Value, + CashbackPercentage: vsl.CashbackPercentage.Value, + DefaultMaxReferrals: vsl.DefaultMaxReferrals.Value, + MinimumBetAmount: vsl.MinimumBetAmount.Value, + BetDuplicateLimit: vsl.BetDuplicateLimit.Value, + SendEmailOnBetFinish: vsl.SendEmailOnBetFinish.Value, + SendSMSOnBetFinish: vsl.SendSMSOnBetFinish.Value, + WelcomeBonusActive: vsl.WelcomeBonusActive.Value, + WelcomeBonusMultiplier: vsl.WelcomeBonusMultiplier.Value, + WelcomeBonusCap: vsl.WelcomeBonusCap.Value, + WelcomeBonusCount: vsl.WelcomeBonusCount.Value, + WelcomeBonusExpire: vsl.WelcomeBonusExpire.Value, + } +} + +// Custom Validations for non-generic types +func (vsl *ValidSettingList) CustomValidationSettings() error { + if !SMSProvider(vsl.SMSProvider.Value).IsValid() { + return fmt.Errorf("sms provider invalid") + } + return nil +} + +func (vsl *ValidSettingList) GetInt64SettingsMap() map[string]*ValidInt64 { + return map[string]*ValidInt64{ + "max_number_of_outcomes": &vsl.MaxNumberOfOutcomes, + "max_unsettled_bets": &vsl.MaxUnsettledBets, + "daily_ticket_limit": &vsl.DailyTicketPerIP, + "default_winning_limit": &vsl.DefaultWinningLimit, + "default_max_referrals": &vsl.DefaultMaxReferrals, + "bet_duplicate_limit": &vsl.BetDuplicateLimit, + "welcome_bonus_count": &vsl.WelcomeBonusCount, + "welcome_bonus_expiry": &vsl.WelcomeBonusExpire, + } +} + +func (vsl *ValidSettingList) GetCurrencySettingsMap() map[string]*ValidCurrency { + return map[string]*ValidCurrency{ + "bet_amount_limit": &vsl.BetAmountLimit, + "total_winnings_limit": &vsl.TotalWinningLimit, + "total_winnings_notify": &vsl.TotalWinningNotify, + "amount_for_bet_referral": &vsl.AmountForBetReferral, + "cashback_amount_cap": &vsl.CashbackAmountCap, + "referral_reward_amount": &vsl.ReferralRewardAmount, + "minimum_bet_amount": &vsl.MinimumBetAmount, + "welcome_bonus_cap": &vsl.WelcomeBonusCap, + } +} + +func (vsl *ValidSettingList) GetStringSettingsMap() map[string]*ValidString { + return map[string]*ValidString{ + "sms_provider": &vsl.SMSProvider, + } +} + +func (vsl *ValidSettingList) GetBoolSettingsMap() map[string]*ValidBool { + return map[string]*ValidBool{ + "send_email_on_bet_finish": &vsl.SendEmailOnBetFinish, + "send_sms_on_bet_finish": &vsl.SendSMSOnBetFinish, + "welcome_bonus_active": &vsl.WelcomeBonusActive, + } +} + +func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 { + return map[string]*ValidFloat32{ + "cashback_percentage": &vsl.CashbackPercentage, + "welcome_bonus_multiplier": &vsl.WelcomeBonusMultiplier, + } +} + +func (vsl *ValidSettingList) GetTimeSettingsMap() map[string]*ValidTime { + return map[string]*ValidTime{} +} + +// Setting Functions + +func (vsl *ValidSettingList) GetTotalSettings() int { + return len(vsl.GetInt64SettingsMap()) + + len(vsl.GetCurrencySettingsMap()) + + len(vsl.GetStringSettingsMap()) + + len(vsl.GetBoolSettingsMap()) + + len(vsl.GetFloat32SettingsMap()) + + len(vsl.GetTimeSettingsMap()) +} + +func (vsl *ValidSettingList) GetAllValid() map[string]*bool { + + settingValid := make(map[string]*bool) + + for key, setting := range vsl.GetInt64SettingsMap() { + settingValid[key] = &(*setting).Valid + } + for key, setting := range vsl.GetCurrencySettingsMap() { + settingValid[key] = &(*setting).Valid + } + for key, setting := range vsl.GetStringSettingsMap() { + settingValid[key] = &(*setting).Valid + } + for key, setting := range vsl.GetBoolSettingsMap() { + settingValid[key] = &(*setting).Valid + } + for key, setting := range vsl.GetFloat32SettingsMap() { + settingValid[key] = &(*setting).Valid + } + for key, setting := range vsl.GetTimeSettingsMap() { + settingValid[key] = &(*setting).Valid + } + + return settingValid +} + +// func setValidSetting[T any](settings map[string]*T, searchKey string, searchVal string, setVal func(string) (T, error)) error { +// for key, setting := range settings { + +// if key == searchKey { +// s, err := setVal(searchVal) +// if err != nil { +// return err +// } +// *setting = s +// } +// return nil +// } +// return ErrSettingNotFound +// } +func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error { + for key, setting := range vsl.GetInt64SettingsMap() { + if key == searchKey { + value, err := strconv.ParseInt(searchVal, 10, 64) + if err != nil { + return err + } + *setting = ValidInt64{Value: value, Valid: true} + return nil + } + } + return ErrSettingNotFound +} + +func (vsl *ValidSettingList) SetCurrencySetting(searchKey string, searchVal string) error { + for key, setting := range vsl.GetCurrencySettingsMap() { + if key == searchKey { + value, err := strconv.ParseInt(searchVal, 10, 64) + if err != nil { + return err + } + *setting = ValidCurrency{Value: Currency(value), Valid: true} + return nil + } + } + + return ErrSettingNotFound +} + +func (vsl *ValidSettingList) SetStringSetting(searchKey string, searchVal string) error { + for key, setting := range vsl.GetStringSettingsMap() { + if key == searchKey { + *setting = ValidString{Value: searchVal, Valid: true} + return nil + } + } + + return ErrSettingNotFound +} + +func (vsl *ValidSettingList) SetBoolSetting(searchKey string, searchVal string) error { + for key, setting := range vsl.GetBoolSettingsMap() { + + if key == searchKey { + value, err := strconv.ParseBool(searchVal) + if err != nil { + return err + } + + *setting = ValidBool{Value: value, Valid: true} + return nil + } + + } + + return ErrSettingNotFound +} + +func (vsl *ValidSettingList) SetFloat32Setting(searchKey string, searchVal string) error { + for key, setting := range vsl.GetFloat32SettingsMap() { + if key == searchKey { + value, err := strconv.ParseFloat(searchVal, 32) + if err != nil { + return err + } + *setting = ValidFloat32{Value: float32(value), Valid: true} + return nil + } + } + + return ErrSettingNotFound +} + +func (vsl *ValidSettingList) SetTimeSetting(searchKey string, searchVal string) error { + for key, setting := range vsl.GetTimeSettingsMap() { + if key == searchKey { + value, err := time.Parse(time.RFC3339, searchVal) + if err != nil { + return err + } + *setting = ValidTime{Value: value, Valid: true} + return nil + } + } + return ErrSettingNotFound +} + +func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) error { + setters := []func(string, string) error{ + vsl.SetInt64Setting, + vsl.SetCurrencySetting, + vsl.SetStringSetting, + vsl.SetBoolSetting, + vsl.SetFloat32Setting, + vsl.SetTimeSetting, + } + + for _, setter := range setters { + if err := setter(searchKey, searchVal); err != nil { + if err == ErrSettingNotFound { + // fmt.Printf("setting is not found %v \n", searchKey) + continue // not this setter, try the next + } + return fmt.Errorf("error while processing setting %q: %w \n", searchKey, err) + } + return nil // successfully set + } + + // If we get here, none of the setters matched + return ErrSettingNotFound +} + +func convertValidSettings[T any]( + settings map[string]*T, + isValid func(*T) bool, + toString func(*T) string, +) []Setting { + result := make([]Setting, 0, len(settings)) + for key, s := range settings { + if isValid(s) { + result = append(result, Setting{ + Key: key, + Value: toString(s), + }) + } + } + return result +} + +func (vsl *ValidSettingList) ConvertInt64Settings() []Setting { + return convertValidSettings( + vsl.GetInt64SettingsMap(), + func(s *ValidInt64) bool { return s.Valid }, + func(s *ValidInt64) string { return strconv.FormatInt(s.Value, 10) }, + ) +} + +func (vsl *ValidSettingList) ConvertCurrencySettings() []Setting { + return convertValidSettings( + vsl.GetCurrencySettingsMap(), + func(s *ValidCurrency) bool { return s.Valid }, + func(s *ValidCurrency) string { return strconv.FormatInt(int64(s.Value), 10) }, + ) +} + +func (vsl *ValidSettingList) ConvertStringSettings() []Setting { + return convertValidSettings( + vsl.GetStringSettingsMap(), + func(s *ValidString) bool { return s.Valid }, + func(s *ValidString) string { return s.Value }, + ) +} + +func (vsl *ValidSettingList) ConvertBoolSettings() []Setting { + return convertValidSettings( + vsl.GetBoolSettingsMap(), + func(s *ValidBool) bool { return s.Valid }, + func(s *ValidBool) string { return strconv.FormatBool(s.Value) }, + ) +} + +func (vsl *ValidSettingList) ConvertFloat32Settings() []Setting { + return convertValidSettings( + vsl.GetFloat32SettingsMap(), + func(s *ValidFloat32) bool { return s.Valid }, + func(s *ValidFloat32) string { return strconv.FormatFloat(float64(s.Value), 'f', -1, 32) }, + ) +} + +func (vsl *ValidSettingList) ConvertTimeSettings() []Setting { + return convertValidSettings( + vsl.GetTimeSettingsMap(), + func(s *ValidTime) bool { return s.Valid }, + func(s *ValidTime) string { return s.Value.Format(time.RFC3339) }, + ) +} + +func validateSettings[T any]( + settings map[string]*T, + customValidator func(*T) bool, +) error { + var errs []string + for key, s := range settings { + if !customValidator(s) { + + errs = append(errs, fmt.Sprintf("%v is invalid", key)) + } + } + if len(errs) > 0 { + return errors.New(strings.Join(errs, "; ")) + } + + return nil +} + +func (vsl *ValidSettingList) ValidateInt64Settings() error { + return validateSettings(vsl.GetInt64SettingsMap(), + func(s *ValidInt64) bool { + return s.Valid + }, + ) +} + +func (vsl *ValidSettingList) ValidateCurrencySettings() error { + return validateSettings(vsl.GetCurrencySettingsMap(), + func(s *ValidCurrency) bool { + return s.Valid + }, + ) +} + +func (vsl *ValidSettingList) ValidateStringSettings() error { + return validateSettings(vsl.GetStringSettingsMap(), + func(s *ValidString) bool { + return s.Valid + }, + ) +} + +func (vsl *ValidSettingList) ValidateBoolSettings() error { + return validateSettings(vsl.GetBoolSettingsMap(), + func(s *ValidBool) bool { + return s.Valid + }, + ) +} + +func (vsl *ValidSettingList) ValidateFloat32Settings() error { + return validateSettings(vsl.GetFloat32SettingsMap(), + func(s *ValidFloat32) bool { + return s.Valid + }, + ) +} + +func (vsl *ValidSettingList) ValidateTimeSettings() error { + return validateSettings(vsl.GetTimeSettingsMap(), + func(s *ValidTime) bool { + return s.Valid + }, + ) +} + +func (vsl *ValidSettingList) ValidateAllSettings() error { + var errs []string + validators := []func() error{ + vsl.ValidateInt64Settings, + vsl.ValidateCurrencySettings, + vsl.ValidateStringSettings, + vsl.ValidateBoolSettings, + vsl.ValidateFloat32Settings, + vsl.ValidateTimeSettings, + vsl.CustomValidationSettings, + } + + for _, validator := range validators { + if err := validator(); err != nil { + + errs = append(errs, err.Error()) + } + } + if len(errs) > 0 { + return errors.New(strings.Join(errs, "; ")) + } + + return nil +} + +func (vsl *ValidSettingList) ConvertAllSettings() []Setting { + totalCap := vsl.GetTotalSettings() + all := make([]Setting, 0, totalCap) + + all = append(all, vsl.ConvertInt64Settings()...) + all = append(all, vsl.ConvertCurrencySettings()...) + all = append(all, vsl.ConvertStringSettings()...) + all = append(all, vsl.ConvertBoolSettings()...) + all = append(all, vsl.ConvertFloat32Settings()...) + all = append(all, vsl.ConvertTimeSettings()...) + + return all +} + +func ConvertDBGlobalSettingList(settings []dbgen.GlobalSetting) (SettingList, error) { + var dbSettingList ValidSettingList + + for _, setting := range settings { + if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil { + if err == ErrSettingNotFound { + MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key)) + continue + } + MongoDBLogger.Error("unknown error while fetching settings", zap.Error(err)) + } + } + + if err := dbSettingList.ValidateAllSettings(); err != nil { + MongoDBLogger.Warn("setting validation error", zap.Error(err), zap.Any("db_setting_list", dbSettingList)) + return SettingList{}, err + } + + settingList := dbSettingList.ToSettingList() + + return settingList, nil +} // func ConvertDBOverrideSettingList(settings []dbgen.GetOverrideSettingsRow) (SettingList, error) { // var dbSettingList ValidSettingList diff --git a/internal/domain/settings.go b/internal/domain/settings.go index c30fd91..981488b 100644 --- a/internal/domain/settings.go +++ b/internal/domain/settings.go @@ -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{ diff --git a/internal/ports/notification.go b/internal/ports/notification.go index fa5c1d0..80b8840 100644 --- a/internal/ports/notification.go +++ b/internal/ports/notification.go @@ -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 } diff --git a/internal/ports/settings.go b/internal/ports/settings.go index 6e97586..0883c10 100644 --- a/internal/ports/settings.go +++ b/internal/ports/settings.go @@ -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 } diff --git a/internal/ports/user.go b/internal/ports/user.go index 2450de3..d1847a8 100644 --- a/internal/ports/user.go +++ b/internal/ports/user.go @@ -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 diff --git a/internal/repository/issue_reporting.go b/internal/repository/issue_reporting.go new file mode 100644 index 0000000..a6e8c79 --- /dev/null +++ b/internal/repository/issue_reporting.go @@ -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) +} diff --git a/internal/repository/notification.go b/internal/repository/notification.go index 78ebf0d..820af84 100644 --- a/internal/repository/notification.go +++ b/internal/repository/notification.go @@ -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 +} diff --git a/internal/repository/settings.go b/internal/repository/settings.go new file mode 100644 index 0000000..ec72c80 --- /dev/null +++ b/internal/repository/settings.go @@ -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 +} diff --git a/internal/repository/user.go b/internal/repository/user.go index 51bbe22..45e8fc2 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -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}, - CreatedAt: u.CreatedAt.Time, - UpdatedAt: u.UpdatedAt.Time, - } + 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{ diff --git a/internal/services/currency/fetcher.go b/internal/services/currency/fetcher.go deleted file mode 100644 index 87d0fec..0000000 --- a/internal/services/currency/fetcher.go +++ /dev/null @@ -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 -} diff --git a/internal/services/currency/service.go b/internal/services/currency/service.go deleted file mode 100644 index b5d96a3..0000000 --- a/internal/services/currency/service.go +++ /dev/null @@ -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 -} diff --git a/internal/services/currency/worker.go b/internal/services/currency/worker.go deleted file mode 100644 index 286d4a1..0000000 --- a/internal/services/currency/worker.go +++ /dev/null @@ -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") -} diff --git a/internal/services/issue_reporting/service.go b/internal/services/issue_reporting/service.go new file mode 100644 index 0000000..308f467 --- /dev/null +++ b/internal/services/issue_reporting/service.go @@ -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) +} diff --git a/internal/services/messenger/email.go b/internal/services/messenger/email.go index a99d2fe..4dc4eda 100644 --- a/internal/services/messenger/email.go +++ b/internal/services/messenger/email.go @@ -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}, diff --git a/internal/services/messenger/sms.go b/internal/services/messenger/sms.go index c01e54e..616946b 100644 --- a/internal/services/messenger/sms.go +++ b/internal/services/messenger/sms.go @@ -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: diff --git a/internal/services/notification/service.go b/internal/services/notification/service.go index 9a889b9..7fabe16 100644 --- a/internal/services/notification/service.go +++ b/internal/services/notification/service.go @@ -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) diff --git a/internal/services/referal/interface.go b/internal/services/referal/interface.go deleted file mode 100644 index 3b2f239..0000000 --- a/internal/services/referal/interface.go +++ /dev/null @@ -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) -// } diff --git a/internal/services/referal/service.go b/internal/services/referal/service.go deleted file mode 100644 index dcffb31..0000000 --- a/internal/services/referal/service.go +++ /dev/null @@ -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 -// } diff --git a/internal/services/settings/service.go b/internal/services/settings/service.go index fbea10b..9ddf5ee 100644 --- a/internal/services/settings/service.go +++ b/internal/services/settings/service.go @@ -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) +// } diff --git a/internal/services/transaction/service.go b/internal/services/transaction/service.go index 7947f7b..1f868cb 100644 --- a/internal/services/transaction/service.go +++ b/internal/services/transaction/service.go @@ -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 } diff --git a/internal/services/user/common.go b/internal/services/user/common.go index acd2381..aceb3ed 100644 --- a/internal/services/user/common.go +++ b/internal/services/user/common.go @@ -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 } } diff --git a/internal/services/user/direct.go b/internal/services/user/direct.go index 71a8e03..6ad7dd4 100644 --- a/internal/services/user/direct.go +++ b/internal/services/user/direct.go @@ -25,17 +25,17 @@ func (s *Service) CreateUser(ctx context.Context, User domain.CreateUserReq, is_ } return s.userStore.CreateUserWithoutOtp(ctx, domain.User{ - FirstName: User.FirstName, - LastName: User.LastName, - Email: User.Email, - PhoneNumber: User.PhoneNumber, - Password: hashedPassword, - Role: domain.Role(User.Role), - EmailVerified: true, - PhoneVerified: true, - Suspended: User.Suspended, - CompanyID: User.CompanyID, - }, is_company) + FirstName: User.FirstName, + LastName: User.LastName, + Email: User.Email, + PhoneNumber: User.PhoneNumber, + Password: hashedPassword, + Role: domain.Role(User.Role), + EmailVerified: true, + PhoneVerified: true, + Suspended: User.Suspended, + 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) -} diff --git a/internal/services/user/register.go b/internal/services/user/register.go index 1de928e..ba9c94f 100644 --- a/internal/services/user/register.go +++ b/internal/services/user/register.go @@ -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 @@ -59,18 +61,18 @@ func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterU return domain.User{}, err } userR := domain.User{ - FirstName: registerReq.FirstName, - LastName: registerReq.LastName, - Email: registerReq.Email, - PhoneNumber: registerReq.PhoneNumber, - Password: hashedPassword, - Role: domain.RoleCustomer, - EmailVerified: registerReq.OtpMedium == domain.OtpMediumEmail, - PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms, - CompanyID: registerReq.CompanyID, + FirstName: registerReq.FirstName, + LastName: registerReq.LastName, + Email: registerReq.Email, + PhoneNumber: registerReq.PhoneNumber, + Password: hashedPassword, + Role: domain.RoleStudent, + EmailVerified: registerReq.OtpMedium == domain.OtpMediumEmail, + PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms, + 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 } diff --git a/internal/services/user/reset.go b/internal/services/user/reset.go index b507a4e..ded24b5 100644 --- a/internal/services/user/reset.go +++ b/internal/services/user/reset.go @@ -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 } diff --git a/internal/services/user/user.go b/internal/services/user/user.go index b825d58..d1ccde7 100644 --- a/internal/services/user/user.go +++ b/internal/services/user/user.go @@ -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 diff --git a/internal/web_server/app.go b/internal/web_server/app.go index 007d8cb..905b730 100644 --- a/internal/web_server/app.go +++ b/internal/web_server/app.go @@ -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 + arifpaySvc *arifpay.ArifpayService + 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 + port int + settingSvc *settings.Service + authSvc *authentication.Service + userSvc *user.Service + transactionSvc *transaction.Service + validator *customvalidator.CustomValidator + JwtConfig jwtutil.JwtConfig + Logger *slog.Logger + 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,35 +74,22 @@ func NewApp( app.Static("/static", "./static") s := &App{ - // directDepositSvc: directDepositSvc, - // telebirrSvc: telebirrSvc, - arifpaySvc: arifpaySvc, - // santimpaySvc: santimpaySvc, + arifpaySvc: arifpaySvc, // issueReportingSvc: issueReportingSvc, - // instSvc: instSvc, - // currSvc: currSvc, - fiber: app, - port: port, - settingSvc: settingSvc, - authSvc: authSvc, - validator: validator, - logger: logger, - JwtConfig: JwtConfig, - userSvc: userSvc, - // ticketSvc: ticketSvc, - // chapaSvc: chapaSvc, - transactionSvc: transactionSvc, - // branchSvc: branchSvc, - // companySvc: companySvc, + fiber: app, + port: port, + settingSvc: settingSvc, + authSvc: authSvc, + validator: validator, + logger: logger, + JwtConfig: JwtConfig, + userSvc: userSvc, + transactionSvc: transactionSvc, NotidicationStore: notidicationStore, - referralSvc: referralSvc, - // raffleSvc: raffleSvc, - // bonusSvc: bonusSvc, Logger: logger, recommendationSvc: recommendationSvc, - // statSvc: statSvc, - cfg: cfg, - mongoLoggerSvc: mongoLoggerSvc, + cfg: cfg, + mongoLoggerSvc: mongoLoggerSvc, } s.initAppRoutes() diff --git a/internal/web_server/handlers/admin.go b/internal/web_server/handlers/admin.go index 4578706..6afabe9 100644 --- a/internal/web_server/handlers/admin.go +++ b/internal/web_server/handlers/admin.go @@ -13,12 +13,12 @@ import ( ) type CreateAdminReq 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"` - CompanyID *int64 `json:"company_id,omitempty" example:"1"` + 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"` + 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,36 +60,36 @@ 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, } } user := domain.CreateUserReq{ - FirstName: req.FirstName, - LastName: req.LastName, - Email: req.Email, - PhoneNumber: req.PhoneNumber, - Password: req.Password, - Role: string(domain.RoleAdmin), - CompanyID: companyID, + FirstName: req.FirstName, + LastName: req.LastName, + Email: req.Email, + PhoneNumber: req.PhoneNumber, + Password: req.Password, + Role: string(domain.RoleAdmin), + 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, }, @@ -362,10 +362,10 @@ func (h *Handler) GetAdminByID(c *fiber.Ctx) error { } 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"` + FirstName string `json:"first_name" example:"John"` + LastName string `json:"last_name" example:"Doe"` + Suspended bool `json:"suspended" example:"false"` + 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, diff --git a/internal/web_server/handlers/auth_handler.go b/internal/web_server/handlers/auth_handler.go index 02f7943..e5cb0cf 100644 --- a/internal/web_server/handlers/auth_handler.go +++ b/internal/web_server/handlers/auth_handler.go @@ -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), diff --git a/internal/web_server/handlers/cashier.go b/internal/web_server/handlers/cashier.go deleted file mode 100644 index 87f4950..0000000 --- a/internal/web_server/handlers/cashier.go +++ /dev/null @@ -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) - -} diff --git a/internal/web_server/handlers/currency.go b/internal/web_server/handlers/currency.go index ad78e3e..92a5ff4 100644 --- a/internal/web_server/handlers/currency.go +++ b/internal/web_server/handlers/currency.go @@ -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, +// }) +// } diff --git a/internal/web_server/handlers/handlers.go b/internal/web_server/handlers/handlers.go index a0dcb73..2d9b54e 100644 --- a/internal/web_server/handlers/handlers.go +++ b/internal/web_server/handlers/handlers.go @@ -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, diff --git a/internal/web_server/handlers/institutions.go b/internal/web_server/handlers/institutions.go index 219b86a..3627fcf 100644 --- a/internal/web_server/handlers/institutions.go +++ b/internal/web_server/handlers/institutions.go @@ -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, +// }) +// } diff --git a/internal/web_server/handlers/manager.go b/internal/web_server/handlers/manager.go index 5a61815..f549618 100644 --- a/internal/web_server/handlers/manager.go +++ b/internal/web_server/handlers/manager.go @@ -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 { diff --git a/internal/web_server/handlers/notification_handler.go b/internal/web_server/handlers/notification_handler.go index adc5e85..c5913f3 100644 --- a/internal/web_server/handlers/notification_handler.go +++ b/internal/web_server/handlers/notification_handler.go @@ -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 { diff --git a/internal/web_server/handlers/referal_handlers.go b/internal/web_server/handlers/referal_handlers.go index b9fa633..1241199 100644 --- a/internal/web_server/handlers/referal_handlers.go +++ b/internal/web_server/handlers/referal_handlers.go @@ -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) diff --git a/internal/web_server/handlers/settings_handler.go b/internal/web_server/handlers/settings_handler.go index 6e2a058..2effaac 100644 --- a/internal/web_server/handlers/settings_handler.go +++ b/internal/web_server/handlers/settings_handler.go @@ -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) } diff --git a/internal/web_server/handlers/transaction_approver.go b/internal/web_server/handlers/transaction_approver.go index 7e230e0..bf45f1f 100644 --- a/internal/web_server/handlers/transaction_approver.go +++ b/internal/web_server/handlers/transaction_approver.go @@ -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 != "", diff --git a/internal/web_server/handlers/user.go b/internal/web_server/handlers/user.go index fd7bda0..c3984bd 100644 --- a/internal/web_server/handlers/user.go +++ b/internal/web_server/handlers/user.go @@ -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", @@ -254,16 +249,16 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, errMsg) } user := domain.RegisterUserReq{ - FirstName: req.FirstName, - LastName: req.LastName, - Email: req.Email, - PhoneNumber: req.PhoneNumber, - Password: req.Password, - Otp: req.Otp, - ReferralCode: req.ReferralCode, - OtpMedium: domain.OtpMediumEmail, - CompanyID: companyID, - Role: string(domain.RoleCustomer), + FirstName: req.FirstName, + LastName: req.LastName, + Email: req.Email, + PhoneNumber: req.PhoneNumber, + Password: req.Password, + Otp: req.Otp, + ReferralCode: req.ReferralCode, + OtpMedium: domain.OtpMediumEmail, + 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 { @@ -599,12 +556,12 @@ func (h *Handler) ResetTenantPassword(c *fiber.Ctx) error { } resetReq := domain.ResetPasswordReq{ - Email: req.Email, - PhoneNumber: req.PhoneNumber, - Password: req.Password, - Otp: req.Otp, - OtpMedium: medium, - CompanyID: companyID.Value, + Email: req.Email, + PhoneNumber: req.PhoneNumber, + Password: req.Password, + Otp: req.Otp, + OtpMedium: medium, + 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) -// } diff --git a/internal/web_server/jwt/jwt.go b/internal/web_server/jwt/jwt.go index e1be621..d50a8db 100644 --- a/internal/web_server/jwt/jwt.go +++ b/internal/web_server/jwt/jwt.go @@ -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") -} diff --git a/internal/web_server/jwt/jwt_test.go b/internal/web_server/jwt/jwt_test.go index bffe0c5..1500fa2 100644 --- a/internal/web_server/jwt/jwt_test.go +++ b/internal/web_server/jwt/jwt_test.go @@ -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 diff --git a/internal/web_server/middleware.go b/internal/web_server/middleware.go index 88d78a3..7684af6 100644 --- a/internal/web_server/middleware.go +++ b/internal/web_server/middleware.go @@ -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)), diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index 48cee48..e334834 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -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) - } diff --git a/makefile b/makefile index 936aee9..fba54ad 100644 --- a/makefile +++ b/makefile @@ -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 diff --git a/makefile copy b/makefile copy new file mode 100644 index 0000000..fba54ad --- /dev/null +++ b/makefile copy @@ -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