init
This commit is contained in:
parent
b250fbc77e
commit
ab3f6d4313
46
.air.toml
Normal file
46
.air.toml
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
root = "."
|
||||||
|
testdata_dir = "testdata"
|
||||||
|
tmp_dir = "tmp"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
args_bin = []
|
||||||
|
bin = "./tmp/main"
|
||||||
|
cmd = "go build -o ./tmp/main ./cmd"
|
||||||
|
delay = 1000
|
||||||
|
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||||
|
exclude_file = []
|
||||||
|
exclude_regex = ["_test.go"]
|
||||||
|
exclude_unchanged = false
|
||||||
|
follow_symlink = false
|
||||||
|
full_bin = ""
|
||||||
|
include_dir = []
|
||||||
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||||
|
include_file = []
|
||||||
|
kill_delay = "0s"
|
||||||
|
log = "build-errors.log"
|
||||||
|
poll = false
|
||||||
|
poll_interval = 0
|
||||||
|
post_cmd = []
|
||||||
|
pre_cmd = []
|
||||||
|
rerun = false
|
||||||
|
rerun_delay = 500
|
||||||
|
send_interrupt = false
|
||||||
|
stop_on_error = false
|
||||||
|
|
||||||
|
[color]
|
||||||
|
app = ""
|
||||||
|
build = "yellow"
|
||||||
|
main = "magenta"
|
||||||
|
runner = "green"
|
||||||
|
watcher = "cyan"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
main_only = false
|
||||||
|
time = false
|
||||||
|
|
||||||
|
[misc]
|
||||||
|
clean_on_exit = false
|
||||||
|
|
||||||
|
[screen]
|
||||||
|
clear_on_rebuild = false
|
||||||
|
keep_scroll = true
|
||||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
bin
|
||||||
|
coverage.out
|
||||||
|
coverage
|
||||||
|
.env
|
||||||
|
tmp
|
||||||
|
build
|
||||||
5
cmd/main.go
Normal file
5
cmd/main.go
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
}
|
||||||
37
compose.db.yaml
Normal file
37
compose.db.yaml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
ports:
|
||||||
|
- 5422:5432
|
||||||
|
environment:
|
||||||
|
- POSTGRES_PASSWORD=secret
|
||||||
|
- POSTGRES_USER=root
|
||||||
|
- POSTGRES_DB=gh
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U root -d gh"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 5
|
||||||
|
migrate:
|
||||||
|
image: migrate/migrate
|
||||||
|
volumes:
|
||||||
|
- ./db/migrations:/migrations
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
command:
|
||||||
|
[
|
||||||
|
"-path",
|
||||||
|
"/migrations",
|
||||||
|
"-database",
|
||||||
|
"postgresql://root:secret@postgres:5432/gh?sslmode=disable",
|
||||||
|
"up",
|
||||||
|
]
|
||||||
|
networks:
|
||||||
|
- app
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app:
|
||||||
|
driver: bridge
|
||||||
74
db/migrations/000001_fortune.down.sql
Normal file
74
db/migrations/000001_fortune.down.sql
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
-- Drop tables that depend on service_type_setting
|
||||||
|
DROP TABLE IF EXISTS service_type_setting;
|
||||||
|
|
||||||
|
-- Drop product-related tables and types
|
||||||
|
DROP TABLE IF EXISTS product;
|
||||||
|
DROP TYPE IF EXISTS tier_group;
|
||||||
|
|
||||||
|
-- Drop onboarding-related tables and types
|
||||||
|
DROP TABLE IF EXISTS verification_key;
|
||||||
|
DROP TABLE IF EXISTS onboarding_user;
|
||||||
|
DROP TYPE IF EXISTS verification_status;
|
||||||
|
DROP TYPE IF EXISTS onboarding_status;
|
||||||
|
|
||||||
|
-- Drop staff-related tables and types
|
||||||
|
DROP TABLE IF EXISTS staff_session;
|
||||||
|
DROP TABLE IF EXISTS user_agent;
|
||||||
|
DROP TABLE IF EXISTS staff;
|
||||||
|
DROP TYPE IF EXISTS password_status;
|
||||||
|
|
||||||
|
-- Drop mobile app-related tables and types
|
||||||
|
DROP TABLE IF EXISTS user_devices;
|
||||||
|
DROP TABLE IF EXISTS user_session;
|
||||||
|
DROP TABLE IF EXISTS linked_accounts;
|
||||||
|
DROP TABLE IF EXISTS users;
|
||||||
|
DROP TYPE IF EXISTS device_type;
|
||||||
|
DROP TYPE IF EXISTS registeration_type;
|
||||||
|
DROP TYPE IF EXISTS customer_group;
|
||||||
|
|
||||||
|
-- Drop linked accounts and beneficiary tables and types
|
||||||
|
DROP TABLE IF EXISTS beneficiary;
|
||||||
|
DROP TYPE IF EXISTS fund_destination;
|
||||||
|
|
||||||
|
-- Drop maker checker-related tables and types
|
||||||
|
DROP TABLE IF EXISTS workflow;
|
||||||
|
DROP TYPE IF EXISTS approval_status;
|
||||||
|
DROP TYPE IF EXISTS action_type;
|
||||||
|
DROP TYPE IF EXISTS context_type;
|
||||||
|
|
||||||
|
-- Drop authorization-related tables and types
|
||||||
|
DROP TRIGGER IF EXISTS enforce_unique_array ON policy;
|
||||||
|
DROP FUNCTION IF EXISTS check_unique_array;
|
||||||
|
DROP TABLE IF EXISTS policy;
|
||||||
|
DROP TABLE IF EXISTS roles;
|
||||||
|
DROP TYPE IF EXISTS policy_action;
|
||||||
|
DROP TYPE IF EXISTS policy_object;
|
||||||
|
|
||||||
|
-- Drop bank-related tables and types
|
||||||
|
DROP TABLE IF EXISTS bank;
|
||||||
|
DROP TABLE IF EXISTS flagged_users;
|
||||||
|
|
||||||
|
-- Drop transaction-related tables and types
|
||||||
|
DROP TABLE IF EXISTS transaction_daily;
|
||||||
|
DROP TABLE IF EXISTS system_limits;
|
||||||
|
DROP TABLE IF EXISTS transactions;
|
||||||
|
DROP TYPE IF EXISTS payment_status;
|
||||||
|
DROP TYPE IF EXISTS service_type;
|
||||||
|
DROP TYPE IF EXISTS channel;
|
||||||
|
DROP TYPE IF EXISTS transaction_category;
|
||||||
|
DROP TYPE IF EXISTS registration_type;
|
||||||
|
|
||||||
|
-- Drop branches and related tables
|
||||||
|
DROP TABLE IF EXISTS branches;
|
||||||
|
DROP TABLE IF EXISTS cities;
|
||||||
|
DROP TABLE IF EXISTS districts;
|
||||||
|
DROP TABLE IF EXISTS regions;
|
||||||
|
|
||||||
|
-- Drop activity logs
|
||||||
|
DROP TABLE IF EXISTS activity;
|
||||||
|
|
||||||
|
-- Drop ussd account and related enums
|
||||||
|
DROP TABLE IF EXISTS ussd_account;
|
||||||
|
DROP TYPE IF EXISTS ua_pin_status;
|
||||||
|
DROP TYPE IF EXISTS ua_status;
|
||||||
|
DROP TYPE IF EXISTS ua_registaration_type;
|
||||||
12
db/migrations/000001_fortune.up.sql
Normal file
12
db/migrations/000001_fortune.up.sql
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
CREATE TABLE users (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
first_name VARCHAR(255) NOT NULL,
|
||||||
|
last_name VARCHAR(255) NOT NULL,
|
||||||
|
email VARCHAR(255) UNIQUE NOT NULL,
|
||||||
|
phone_number VARCHAR(20) UNIQUE NOT NULL,
|
||||||
|
password TEXT NOT NULL,
|
||||||
|
role VARCHAR(50) NOT NULL,
|
||||||
|
verified BOOLEAN DEFAULT FALSE,
|
||||||
|
created_at TIMESTAMP ,
|
||||||
|
updated_at TIMESTAMP
|
||||||
|
);
|
||||||
16
db/query/user.sql
Normal file
16
db/query/user.sql
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
-- name: CreateUser :one
|
||||||
|
INSERT INTO users (first_name, last_name, email, phone_number, password, role, verified)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetUserByID :one
|
||||||
|
SELECT * FROM users WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: GetAllUsers :many
|
||||||
|
SELECT * FROM users;
|
||||||
|
|
||||||
|
-- name: UpdateUser :exec
|
||||||
|
UPDATE users SET first_name = $2, last_name = $3, email = $4, phone_number = $5, password = $6, role = $7, verified = $8, updated_at = CURRENT_TIMESTAMP WHERE id = $1;
|
||||||
|
|
||||||
|
-- name: DeleteUser :exec
|
||||||
|
DELETE FROM users WHERE id = $1;
|
||||||
32
gen/db/db.go
Normal file
32
gen/db/db.go
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.26.0
|
||||||
|
|
||||||
|
package dbgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DBTX interface {
|
||||||
|
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
|
||||||
|
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
|
||||||
|
QueryRow(context.Context, string, ...interface{}) pgx.Row
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db DBTX) *Queries {
|
||||||
|
return &Queries{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queries struct {
|
||||||
|
db DBTX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
|
||||||
|
return &Queries{
|
||||||
|
db: tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
22
gen/db/models.go
Normal file
22
gen/db/models.go
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.26.0
|
||||||
|
|
||||||
|
package dbgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int64
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
Email string
|
||||||
|
PhoneNumber string
|
||||||
|
Password string
|
||||||
|
Role string
|
||||||
|
Verified pgtype.Bool
|
||||||
|
CreatedAt pgtype.Timestamp
|
||||||
|
UpdatedAt pgtype.Timestamp
|
||||||
|
}
|
||||||
149
gen/db/user.sql.go
Normal file
149
gen/db/user.sql.go
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.26.0
|
||||||
|
// source: user.sql
|
||||||
|
|
||||||
|
package dbgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CreateUser = `-- name: CreateUser :one
|
||||||
|
INSERT INTO users (first_name, last_name, email, phone_number, password, role, verified)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
|
RETURNING id, first_name, last_name, email, phone_number, password, role, verified, created_at, updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateUserParams struct {
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
Email string
|
||||||
|
PhoneNumber string
|
||||||
|
Password string
|
||||||
|
Role string
|
||||||
|
Verified pgtype.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CreateUser,
|
||||||
|
arg.FirstName,
|
||||||
|
arg.LastName,
|
||||||
|
arg.Email,
|
||||||
|
arg.PhoneNumber,
|
||||||
|
arg.Password,
|
||||||
|
arg.Role,
|
||||||
|
arg.Verified,
|
||||||
|
)
|
||||||
|
var i User
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.FirstName,
|
||||||
|
&i.LastName,
|
||||||
|
&i.Email,
|
||||||
|
&i.PhoneNumber,
|
||||||
|
&i.Password,
|
||||||
|
&i.Role,
|
||||||
|
&i.Verified,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeleteUser = `-- name: DeleteUser :exec
|
||||||
|
DELETE FROM users WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteUser(ctx context.Context, id int64) error {
|
||||||
|
_, err := q.db.Exec(ctx, DeleteUser, id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetAllUsers = `-- name: GetAllUsers :many
|
||||||
|
SELECT id, first_name, last_name, email, phone_number, password, role, verified, created_at, updated_at FROM users
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetAllUsers(ctx context.Context) ([]User, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetAllUsers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []User
|
||||||
|
for rows.Next() {
|
||||||
|
var i User
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.FirstName,
|
||||||
|
&i.LastName,
|
||||||
|
&i.Email,
|
||||||
|
&i.PhoneNumber,
|
||||||
|
&i.Password,
|
||||||
|
&i.Role,
|
||||||
|
&i.Verified,
|
||||||
|
&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 GetUserByID = `-- name: GetUserByID :one
|
||||||
|
SELECT id, first_name, last_name, email, phone_number, password, role, verified, created_at, updated_at FROM users WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
|
||||||
|
row := q.db.QueryRow(ctx, GetUserByID, id)
|
||||||
|
var i User
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.FirstName,
|
||||||
|
&i.LastName,
|
||||||
|
&i.Email,
|
||||||
|
&i.PhoneNumber,
|
||||||
|
&i.Password,
|
||||||
|
&i.Role,
|
||||||
|
&i.Verified,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateUser = `-- name: UpdateUser :exec
|
||||||
|
UPDATE users SET first_name = $2, last_name = $3, email = $4, phone_number = $5, password = $6, role = $7, verified = $8, updated_at = CURRENT_TIMESTAMP WHERE id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateUserParams struct {
|
||||||
|
ID int64
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
Email string
|
||||||
|
PhoneNumber string
|
||||||
|
Password string
|
||||||
|
Role string
|
||||||
|
Verified pgtype.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, UpdateUser,
|
||||||
|
arg.ID,
|
||||||
|
arg.FirstName,
|
||||||
|
arg.LastName,
|
||||||
|
arg.Email,
|
||||||
|
arg.PhoneNumber,
|
||||||
|
arg.Password,
|
||||||
|
arg.Role,
|
||||||
|
arg.Verified,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
14
go.mod
Normal file
14
go.mod
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
module github.com/SamuelTariku/FortuneBet-Backend
|
||||||
|
|
||||||
|
go 1.24.1
|
||||||
|
|
||||||
|
require github.com/jackc/pgx/v5 v5.7.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
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
|
||||||
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
|
golang.org/x/text v0.21.0 // indirect
|
||||||
|
)
|
||||||
28
go.sum
Normal file
28
go.sum
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
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=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
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/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||||
|
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||||
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
|
||||||
1
internal/domain/auth.go
Normal file
1
internal/domain/auth.go
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
package domain
|
||||||
12
internal/domain/user.go
Normal file
12
internal/domain/user.go
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int64
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
Email string
|
||||||
|
PhoneNumber string
|
||||||
|
Password string
|
||||||
|
Role string
|
||||||
|
Verified bool
|
||||||
|
}
|
||||||
41
internal/repository/store.go
Normal file
41
internal/repository/store.go
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Store struct {
|
||||||
|
queries *dbgen.Queries
|
||||||
|
conn *pgxpool.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStore(conn *pgxpool.Pool) *Store {
|
||||||
|
|
||||||
|
queries := dbgen.New(conn)
|
||||||
|
return &Store{
|
||||||
|
queries: queries,
|
||||||
|
conn: conn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenDB(url string) (*pgxpool.Pool, func(), error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
conn, err := pgxpool.New(ctx, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, func() {}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conn.Ping(ctx); err != nil {
|
||||||
|
return nil, func() {}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, func() {
|
||||||
|
conn.Close()
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
81
internal/repository/user.go
Normal file
81
internal/repository/user.go
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Store) CreateUser(ctx context.Context, firstName, lastName, email, phoneNumber, password, role string, verified bool) (domain.User, error) {
|
||||||
|
user, err := s.queries.CreateUser(ctx, dbgen.CreateUserParams{
|
||||||
|
FirstName: firstName,
|
||||||
|
LastName: lastName,
|
||||||
|
Email: email,
|
||||||
|
PhoneNumber: phoneNumber,
|
||||||
|
Password: password,
|
||||||
|
Role: role,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return domain.User{}, err
|
||||||
|
}
|
||||||
|
return domain.User{
|
||||||
|
ID: user.ID,
|
||||||
|
FirstName: user.FirstName,
|
||||||
|
LastName: user.LastName,
|
||||||
|
Email: user.Email,
|
||||||
|
PhoneNumber: user.PhoneNumber,
|
||||||
|
Password: user.Password,
|
||||||
|
Role: user.Role,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error) {
|
||||||
|
user, err := s.queries.GetUserByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return domain.User{}, err
|
||||||
|
}
|
||||||
|
return domain.User{
|
||||||
|
ID: user.ID,
|
||||||
|
FirstName: user.FirstName,
|
||||||
|
LastName: user.LastName,
|
||||||
|
Email: user.Email,
|
||||||
|
PhoneNumber: user.PhoneNumber,
|
||||||
|
Password: user.Password,
|
||||||
|
Role: user.Role,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
func (s *Store) GetAllUsers(ctx context.Context) ([]domain.User, error) {
|
||||||
|
users, err := s.queries.GetAllUsers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var result []domain.User
|
||||||
|
for _, user := range users {
|
||||||
|
result = append(result, domain.User{
|
||||||
|
ID: user.ID,
|
||||||
|
FirstName: user.FirstName,
|
||||||
|
LastName: user.LastName,
|
||||||
|
Email: user.Email,
|
||||||
|
PhoneNumber: user.PhoneNumber,
|
||||||
|
Password: user.Password,
|
||||||
|
Role: user.Role,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
func (s *Store) UpdateUser(ctx context.Context, id int64, firstName, lastName, email, phoneNumber, password, role string, verified bool) error {
|
||||||
|
err := s.queries.UpdateUser(ctx, dbgen.UpdateUserParams{
|
||||||
|
ID: id,
|
||||||
|
FirstName: firstName,
|
||||||
|
LastName: lastName,
|
||||||
|
Email: email,
|
||||||
|
PhoneNumber: phoneNumber,
|
||||||
|
Password: password,
|
||||||
|
Role: role,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (s *Store) DeleteUser(ctx context.Context, id int64) error {
|
||||||
|
return s.queries.DeleteUser(ctx, id)
|
||||||
|
}
|
||||||
15
internal/services/user/port.go
Normal file
15
internal/services/user/port.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserStore interface {
|
||||||
|
CreateUser(ctx context.Context, CfirstName, lastName, email, phoneNumber, password, role string, verified bool) (domain.User, error)
|
||||||
|
GetUserByID(ctx context.Context, id int64) (domain.User, error)
|
||||||
|
GetAllUsers(ctx context.Context) ([]domain.User, error)
|
||||||
|
UpdateUser(ctx context.Context, id int64, firstName, lastName, email, phoneNumber, password, role string, verified bool) error
|
||||||
|
DeleteUser(ctx context.Context, id int64) error
|
||||||
|
}
|
||||||
33
internal/services/user/service.go
Normal file
33
internal/services/user/service.go
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
userStore UserStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(userStore UserStore) *Service {
|
||||||
|
return &Service{
|
||||||
|
userStore: userStore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CreateUser(ctx context.Context, firstName, lastName, email, phoneNumber, password, role string, verified bool) (domain.User, error) {
|
||||||
|
return s.userStore.CreateUser(ctx, firstName, lastName, email, phoneNumber, password, role, verified)
|
||||||
|
}
|
||||||
|
func (s *Service) GetUserByID(ctx context.Context, id int64) (domain.User, error) {
|
||||||
|
return s.userStore.GetUserByID(ctx, id)
|
||||||
|
}
|
||||||
|
func (s *Service) GetAllUsers(ctx context.Context) ([]domain.User, error) {
|
||||||
|
return s.userStore.GetAllUsers(ctx)
|
||||||
|
}
|
||||||
|
func (s *Service) UpdateUser(ctx context.Context, id int64, firstName, lastName, email, phoneNumber, password, role string, verified bool) error {
|
||||||
|
return s.userStore.UpdateUser(ctx, id, firstName, lastName, email, phoneNumber, password, role, verified)
|
||||||
|
}
|
||||||
|
func (s *Service) DeleteUser(ctx context.Context, id int64) error {
|
||||||
|
return s.userStore.DeleteUser(ctx, id)
|
||||||
|
}
|
||||||
76
internal/web_server/server.go
Normal file
76
internal/web_server/server.go
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
package httpserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
port int
|
||||||
|
Router *http.ServeMux
|
||||||
|
logger *slog.Logger
|
||||||
|
notificationSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApp(
|
||||||
|
port int, logger *slog.Logger,
|
||||||
|
notificationSecret string,
|
||||||
|
|
||||||
|
) *App {
|
||||||
|
a := &App{
|
||||||
|
Router: http.NewServeMux(),
|
||||||
|
logger: logger,
|
||||||
|
port: port,
|
||||||
|
notificationSecret: notificationSecret,
|
||||||
|
}
|
||||||
|
a.initAppRoutes()
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
func (a *App) Run() error {
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%s", strconv.Itoa(a.port)),
|
||||||
|
Handler: a.Router,
|
||||||
|
IdleTimeout: time.Minute,
|
||||||
|
ReadTimeout: 10 * time.Second,
|
||||||
|
WriteTimeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdownError := make(chan error)
|
||||||
|
go func() {
|
||||||
|
quit := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
<-quit
|
||||||
|
|
||||||
|
a.logger.Info("shutting down server")
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
shutdownError <- srv.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
err := srv.ListenAndServe()
|
||||||
|
|
||||||
|
if !errors.Is(err, http.ErrServerClosed) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = <-shutdownError
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.logger.Info("server stopped")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) initAppRoutes() {
|
||||||
|
// a.Router.HandleFunc("/users",)
|
||||||
|
}
|
||||||
32
makefile
Normal file
32
makefile
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
include .env
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
go test ./app
|
||||||
|
.PHONY: coverage
|
||||||
|
coverage:
|
||||||
|
mkdir -p coverage
|
||||||
|
go test -coverprofile=coverage.out ./internal/... ;
|
||||||
|
go tool cover -func=coverage.out -o coverage/coverage.txt
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
go build -ldflags="-s" -o ./bin/web ./
|
||||||
|
.PHONY: run
|
||||||
|
run:
|
||||||
|
echo "Running Go application"; \
|
||||||
|
go run ./cmd/main.go
|
||||||
|
.PHONY: air
|
||||||
|
air:
|
||||||
|
echo "Running air"; \
|
||||||
|
air -c .air.toml
|
||||||
|
.PHONY: migrations/up
|
||||||
|
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...'
|
||||||
|
migrate -path ./db/migrations -database $(DB_URL) up
|
||||||
|
|
||||||
|
.PHONY: swagger
|
||||||
|
swagger:
|
||||||
|
swag init -g cmd/main.go
|
||||||
18
sqlc.yaml
Normal file
18
sqlc.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
version: "2"
|
||||||
|
sql:
|
||||||
|
- schema: "./db/migrations/"
|
||||||
|
queries: "./db/query/"
|
||||||
|
engine: "postgresql"
|
||||||
|
gen:
|
||||||
|
go:
|
||||||
|
package: "dbgen"
|
||||||
|
sql_package: "pgx/v5"
|
||||||
|
out: "./gen/db"
|
||||||
|
emit_exported_queries: true
|
||||||
|
emit_json_tags: false
|
||||||
|
overrides:
|
||||||
|
- db_type: "uuid"
|
||||||
|
go_type: "github.com/google/uuid.UUID"
|
||||||
|
- db_type: "uuid"
|
||||||
|
go_type: "github.com/google/uuid.NullUUID"
|
||||||
|
nullable: true
|
||||||
Loading…
Reference in New Issue
Block a user