change to GORM, add member and rank handlers

This commit is contained in:
2024-03-08 00:39:46 -08:00
parent 84424fdae9
commit 3b715cf331
36 changed files with 3491 additions and 230 deletions

110
api/db/award.go Normal file
View File

@@ -0,0 +1,110 @@
package db
/*
DDL
CREATE TABLE `awards` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`short_name` varchar(10) DEFAULT NULL,
`description` text DEFAULT NULL,
`type` varchar(100) DEFAULT NULL,
`footprint` varchar(50) DEFAULT NULL,
`created_at` datetime DEFAULT current_timestamp(),
`updated_at` datetime DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`image_url` varchar(250) DEFAULT NULL,
`deleted` tinytext DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=81 DEFAULT CHARSET=utf8mb4 COMMENT='Contains a list of Awards for the unit.';
*/
type Award struct {
ObjectBase
Name string `json:"name"`
ShortName string `json:"short_name"`
Description string `json:"description"`
Type string `json:"type"`
Footprint string `json:"footprint"`
ImageURL string `json:"image_url"`
Members []Member `json:"members" gorm:"many2many:member_awards;"`
}
// Get one by id
func (a *Award) GetByID(id int) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.First(a, id).Error
return err
}
// Get one by name
func (a *Award) GetByName(name string) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Where("name = ?", name).First(a).Error
return err
}
// Get all
func (a *Award) GetAll() ([]Award, error) {
db, err := GetDB()
if err != nil {
return nil, err
}
var awards []Award
err = db.Find(&awards).Error
return awards, err
}
// Create
func (a *Award) Create() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Create(a).Error
return err
}
// Update
func (a *Award) Update() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Save(a).Error
return err
}
// Delete
func (a *Award) Delete() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Delete(a).Error
return err
}
// Get all holders of an award
func (a *Award) GetHolders() ([]Member, error) {
db, err := GetDB()
if err != nil {
return nil, err
}
var members []Member
err = db.Model(a).Association("Members").Find(&members)
return members, err
}

142
api/db/course.go Normal file
View File

@@ -0,0 +1,142 @@
package db
import "context"
/*
DDL
CREATE TABLE `courses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`short_name` varchar(10) NOT NULL,
`category` varchar(100) NOT NULL,
`description` varchar(1000) DEFAULT NULL,
`image_url` varchar(255) DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`deleted` tinyint(4) DEFAULT 0,
`prereq_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `shortName` (`short_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8mb4;
*/
type Course struct {
ObjectBase
Name string `json:"name"`
ShortName string `json:"short_name"`
Category string `json:"category"`
Description string `json:"description"`
ImageURL string `json:"image_url"`
PassedMembers []Member `json:"passed_members" gorm:"many2many:member_courses_pass;"`
FailedMembers []Member `json:"failed_members" gorm:"many2many:member_courses_fail;"`
Prerequisites []*Course `json:"prerequisites" gorm:"many2many:course_prerequisites;"`
TrainingEvents []TrainingEvent `json:"training_events"`
}
// Get one by id
func (c *Course) GetByID(id int) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.First(c, id).Error
return err
}
// Get one by name
func (c *Course) GetByName(name string) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Where("name = ?", name).First(c).Error
return err
}
// Get all
func (c *Course) GetAll() ([]Course, error) {
db, err := GetDB()
if err != nil {
return nil, err
}
var courses []Course
err = db.Find(&courses).Error
return courses, err
}
// Get all training events for a course
func (c *Course) GetTrainingEvents(ctx context.Context, id int) ([]TrainingEvent, error) {
db, err := GetDB()
if err != nil {
return nil, err
}
var events []TrainingEvent
err = db.WithContext(ctx).Where("course_id = ?", id).Find(&events).Error
if err != nil {
return nil, err
}
return events, nil
}
// Get all prerequisites for a course
func (c *Course) GetPrerequisites(ctx context.Context, id int) ([]Course, error) {
var prereqs []Course
db, err := GetDB()
if err != nil {
return nil, err
}
err = db.WithContext(ctx).Model(c).Where("id = ?", id).Association("Prerequisites").Find(&prereqs)
if err != nil {
return nil, err
}
return prereqs, nil
}
// Create a new course
func (c *Course) Create(ctx context.Context, course Course) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Create(course).Error
if err != nil {
return err
}
return nil
}
// Update a course
func (c *Course) Update(ctx context.Context, course Course) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Save(course).Error
if err != nil {
return err
}
return nil
}
// Delete a course
func (c *Course) Delete(ctx context.Context, course Course) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Delete(course).Error
if err != nil {
return err
}
return nil
}

View File

@@ -3,15 +3,43 @@ package db
import (
"database/sql"
"sync"
"time"
_ "github.com/go-sql-driver/mysql"
"github.com/spf13/viper"
"gopkg.in/guregu/null.v3"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var ActiveDB *sql.DB
var ActiveDB *gorm.DB
var activeSQL *sql.DB
var lock = new(sync.Mutex)
func GetDB() (*sql.DB, error) {
type ObjectBase struct {
ID int `json:"id" gorm:"primarykey"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime; not null"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime; not null"`
Deleted bool `json:"deleted" gorm:"default:false; index; not null"`
DeletedAt null.Time `json:"deleted_at"`
}
func AutoMigrate() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.AutoMigrate(
&Award{},
&Course{},
&Member{},
&Rank{},
&TrainingEvent{},
)
return err
}
func GetDB() (*gorm.DB, error) {
if ActiveDB != nil {
return ActiveDB, nil
@@ -26,17 +54,22 @@ func GetDB() (*sql.DB, error) {
return db, nil
}
func ConnectDB() (*sql.DB, error) {
func ConnectDB() (*gorm.DB, error) {
lock.Lock()
defer lock.Unlock()
if ActiveDB != nil {
ActiveDB.Close()
ActiveDB = nil
if activeSQL != nil {
activeSQL.Close()
activeSQL = nil
}
cfg := viper.GetViper()
db, err := sql.Open("mysql", cfg.GetString("MARIADB_CONNSTRING"))
db, err := gorm.Open(mysql.Open(cfg.GetString("MARIADB_CONNSTRING")), &gorm.Config{
FullSaveAssociations: true,
PrepareStmt: true,
})
activeSQL, err = db.DB()
if err != nil {
return nil, err
}

219
api/db/member.go Normal file
View File

@@ -0,0 +1,219 @@
package db
import (
"context"
"github.com/labstack/echo/v4"
"gopkg.in/guregu/null.v3"
)
/*
DDL
CREATE TABLE `members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`timezone` varchar(5) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`website` varchar(240) DEFAULT NULL,
`guilded_id` varchar(10) DEFAULT NULL,
`steam_id_64` varchar(17) DEFAULT NULL,
`teamspeak_uid` varchar(32) DEFAULT NULL,
`steam_profile_name` varchar(32) DEFAULT NULL,
`discord_id` varchar(20) DEFAULT NULL,
`discord_username` varchar(32) DEFAULT NULL,
`aliases` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`aliases`)),
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`deleted` tinyint(4) DEFAULT NULL,
`remarks` text DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `steamId64` (`steam_id_64`) USING BTREE,
UNIQUE KEY `discordId` (`discord_id`) USING BTREE,
UNIQUE KEY `guilded_id` (`guilded_id`)
) ENGINE=InnoDB AUTO_INCREMENT=186 DEFAULT CHARSET=utf8mb4;
*/
// Member is a struct that represents a member
type Member struct {
ObjectBase
Name string `json:"name"`
Company null.String `json:"company"`
Timezone null.String `json:"timezone"`
Email null.String `json:"email"`
Website null.String `json:"website"`
GuildedID null.String `json:"guilded_id"`
SteamID64 null.String `json:"steam_id_64"`
TeamspeakUID null.String `json:"teamspeak_uid"`
SteamProfileName null.String `json:"steam_profile_name"`
DiscordID null.String `json:"discord_id"`
DiscordUsername null.String `json:"discord_username"`
Aliases null.String `json:"aliases"`
Remarks null.String `json:"remarks"`
Rank Rank `json:"rank" gorm:"references:ID; foreignkey:RankID"`
RankID int `json:"rank_id"`
Awards []Award `json:"awards" gorm:"many2many:member_awards;"`
}
// Get one by id
func (m *Member) GetByID(ctx context.Context, id int) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).First(m, id).Error
if err != nil {
return err
}
return nil
}
// Get all
func (m *Member) GetAll(ctx context.Context) ([]Member, error) {
var members []Member
db, err := GetDB()
if err != nil {
return nil, err
}
// Search by provided struct
err = db.WithContext(ctx).
Model(m).Where(m).Find(&members).Error
if err != nil {
return nil, err
}
return members, nil
}
// Create a new member
func (m *Member) Create(ctx context.Context, c echo.Context) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Create(m).Error
if err != nil {
return err
}
return nil
}
// Update a member
func (m *Member) Update(ctx context.Context, c echo.Context) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Save(m).Error
if err != nil {
return err
}
return nil
}
// Delete a member
func (m *Member) Delete(ctx context.Context, c echo.Context) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Delete(m).Error
if err != nil {
return err
}
return nil
}
// Get by name
func (m *Member) GetByName(ctx context.Context, name string) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Where("name = ?", name).First(m).Error
if err != nil {
return err
}
return nil
}
// Get by steam id
func (m *Member) GetBySteamID(ctx context.Context, steamID string) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Where("steam_id_64 = ?", steamID).First(m).Error
if err != nil {
return err
}
return nil
}
// Get by discord id
func (m *Member) GetByDiscordID(ctx context.Context, discordID string) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Where("discord_id = ?", discordID).First(m).Error
if err != nil {
return err
}
return nil
}
// Get by guilded id
func (m *Member) GetByGuildedID(ctx context.Context, guildedID string) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Where("guilded_id = ?", guildedID).First(m).Error
if err != nil {
return err
}
return nil
}
// Get by teamspeak uid
func (m *Member) GetByTeamspeakUID(ctx context.Context, teamspeakUID string) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Where("teamspeak_uid = ?", teamspeakUID).First(m).Error
if err != nil {
return err
}
return nil
}
// Get by email
func (m *Member) GetByEmail(ctx context.Context, email string) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.WithContext(ctx).Where("email = ?", email).First(m).Error
if err != nil {
return err
}
return nil
}
// Get awards a member has
func (m *Member) GetAwards(ctx context.Context) ([]Award, error) {
db, err := GetDB()
if err != nil {
return nil, err
}
err = db.WithContext(ctx).Model(m).Association("Awards").Find(&m.Awards)
if err != nil {
return nil, err
}
return m.Awards, nil
}

109
api/db/rank.go Normal file
View File

@@ -0,0 +1,109 @@
package db
/*
DDL
CREATE TABLE `ranks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`short_name` varchar(70) NOT NULL,
`category` varchar(100) NOT NULL,
`sort_id` int(11) NOT NULL DEFAULT 0,
`image_url` varchar(240) DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`deleted` tinyint(4) DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `shortName` (`short_name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4;
*/
type Rank struct {
ObjectBase
Name string `json:"name"`
ShortName string `json:"short_name"`
Category string `json:"category"`
SortID int `json:"sort_id"`
ImageURL string `json:"image_url"`
Members []Member `json:"members" gorm:"references:ID; foreignkey:RankID"`
}
// Get one by id
func (r *Rank) GetByID(id int) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.First(r, id).Error
return err
}
// Get one by name
func (r *Rank) GetByName(name string) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Where("name = ?", name).First(r).Error
return err
}
// Get all
func (r *Rank) GetAll() ([]Rank, error) {
db, err := GetDB()
if err != nil {
return nil, err
}
var ranks []Rank
err = db.Find(&ranks).Error
return ranks, err
}
// Add a member to a rank
func (r *Rank) AddHolder(m *Member) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Model(r).Association("Members").Append(m)
return err
}
// Create
func (r *Rank) Create() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Create(r).Error
return err
}
// Update
func (r *Rank) Update() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Save(r).Error
return err
}
// Delete
func (r *Rank) Delete() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Delete(r).Error
return err
}

113
api/db/trainingEvent.go Normal file
View File

@@ -0,0 +1,113 @@
package db
/*
DDL
CREATE TABLE `course_events` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`course_id` int(11) DEFAULT NULL,
`event_type` int(11) DEFAULT NULL,
`event_date` datetime NOT NULL,
`guilded_event_id` int(11) DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`deleted` tinyint(4) DEFAULT 0,
`report_url` varchar(2048) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_course_events_event_type_id` (`event_type`) USING BTREE,
KEY `courseId` (`course_id`) USING BTREE,
CONSTRAINT `fk_coures_events_course_id` FOREIGN KEY (`course_id`) REFERENCES `courses` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_course_events_event_type_id` FOREIGN KEY (`event_type`) REFERENCES `event_types` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
*/
type TrainingEvent struct {
ObjectBase
EventType int `json:"event_type"`
EventDate string `json:"event_date"`
GuildedEventID int `json:"guilded_event_id"`
ReportURL string `json:"report_url"`
Course Course `json:"course" gorm:"references:ID; foreignkey:CourseID"`
CourseID int `json:"course_id"`
}
// Get one by id
func (te *TrainingEvent) GetByID(id int) error {
db, err := GetDB()
if err != nil {
return err
}
err = db.First(te, id).Error
return err
}
// Get all
func (te *TrainingEvent) GetAll() ([]TrainingEvent, error) {
db, err := GetDB()
if err != nil {
return nil, err
}
var tes []TrainingEvent
err = db.Find(&tes).Error
return tes, err
}
// Get all by course
func (te *TrainingEvent) GetAllByCourse(courseID int) ([]TrainingEvent, error) {
db, err := GetDB()
if err != nil {
return nil, err
}
var tes []TrainingEvent
err = db.Where("course_id = ?", courseID).Find(&tes).Error
return tes, err
}
// Create
func (te *TrainingEvent) Create() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Create(te).Error
return err
}
// Update
func (te *TrainingEvent) Update() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Save(te).Error
return err
}
// Delete
func (te *TrainingEvent) Delete() error {
db, err := GetDB()
if err != nil {
return err
}
err = db.Delete(te).Error
return err
}
// Get all by date range
func (te *TrainingEvent) GetByDateRange(start string, end string) ([]TrainingEvent, error) {
db, err := GetDB()
if err != nil {
return nil, err
}
var tes []TrainingEvent
err = db.Where("event_date BETWEEN ? AND ?", start, end).Find(&tes).Error
return tes, err
}

View File

@@ -13,6 +13,8 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@@ -40,4 +42,6 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/mysql v1.5.4 // indirect
gorm.io/gorm v1.25.7 // indirect
)

View File

@@ -7,6 +7,7 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -16,6 +17,10 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -103,3 +108,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
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=
gorm.io/driver/mysql v1.5.4 h1:igQmHfKcbaTVyAIHNhhB888vvxh8EdQ2uSUT0LPcBso=
gorm.io/driver/mysql v1.5.4/go.mod h1:9rYxJph/u9SWkWc9yY4XJ1F/+xO0S/ChOmbk3+Z5Tvs=
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=

View File

@@ -51,6 +51,13 @@ func main() {
logger.Log.Info().Msg("Connected to the database")
}
err = db.AutoMigrate()
if err != nil {
logger.Log.Fatal().Err(err).Msg("Error migrating the database")
} else {
logger.Log.Info().Msg("Database migrated")
}
// Middleware
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
LogLatency: true,

View File

@@ -1,198 +0,0 @@
package ops
import (
"context"
"fmt"
"time"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/db"
"github.com/labstack/echo/v4"
"gopkg.in/guregu/null.v3"
)
/*
DDL
CREATE TABLE `members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`timezone` varchar(5) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`website` varchar(240) DEFAULT NULL,
`guilded_id` varchar(10) DEFAULT NULL,
`steam_id_64` varchar(17) DEFAULT NULL,
`teamspeak_uid` varchar(32) DEFAULT NULL,
`steam_profile_name` varchar(32) DEFAULT NULL,
`discord_id` varchar(20) DEFAULT NULL,
`discord_username` varchar(32) DEFAULT NULL,
`aliases` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`aliases`)),
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`deleted` tinyint(4) DEFAULT NULL,
`remarks` text DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `steamId64` (`steam_id_64`) USING BTREE,
UNIQUE KEY `discordId` (`discord_id`) USING BTREE,
UNIQUE KEY `guilded_id` (`guilded_id`)
) ENGINE=InnoDB AUTO_INCREMENT=186 DEFAULT CHARSET=utf8mb4;
*/
// Member is a struct that represents a member
type Member struct {
ID int `json:"id"`
Name string `json:"name"`
Timezone null.String `json:"timezone"`
Email null.String `json:"email"`
Website null.String `json:"website"`
GuildedID null.String `json:"guilded_id"`
SteamID64 null.String `json:"steam_id_64"`
TeamspeakUID null.String `json:"teamspeak_uid"`
SteamProfileName null.String `json:"steam_profile_name"`
DiscordID null.String `json:"discord_id"`
DiscordUsername null.String `json:"discord_username"`
Aliases null.String `json:"aliases"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Deleted null.Int `json:"deleted"`
Remarks null.String `json:"remarks"`
}
/*
DDL
CREATE TABLE `members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`timezone` varchar(5) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`website` varchar(240) DEFAULT NULL,
`guilded_id` varchar(10) DEFAULT NULL,
`steam_id_64` varchar(17) DEFAULT NULL,
`teamspeak_uid` varchar(32) DEFAULT NULL,
`steam_profile_name` varchar(32) DEFAULT NULL,
`discord_id` varchar(20) DEFAULT NULL,
`discord_username` varchar(32) DEFAULT NULL,
`aliases` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`aliases`)),
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`deleted` tinyint(4) DEFAULT NULL,
`remarks` text DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `steamId64` (`steam_id_64`) USING BTREE,
UNIQUE KEY `discordId` (`discord_id`) USING BTREE,
UNIQUE KEY `guilded_id` (`guilded_id`)
) ENGINE=InnoDB AUTO_INCREMENT=186 DEFAULT CHARSET=utf8mb4;
*/
// GetMembers returns a list of all members
func GetMembers(c echo.Context) error {
members := []Member{}
ctx, cancel := context.WithTimeout(
context.Background(),
2*time.Second,
)
defer cancel()
rows, err := db.ActiveDB.QueryContext(
ctx,
"SELECT * FROM members",
)
if err != nil {
return c.JSON(500, map[string]interface{}{
"error": err.Error(),
})
}
if ctx.Err() == context.DeadlineExceeded {
return c.JSON(500, map[string]interface{}{
"error": "request timed out",
})
}
// scan the rows into the members slice
for rows.Next() {
var m Member
err = rows.Scan(
&m.ID,
&m.Name,
&m.Timezone,
&m.Email,
&m.Website,
&m.GuildedID,
&m.SteamID64,
&m.TeamspeakUID,
&m.SteamProfileName,
&m.DiscordID,
&m.DiscordUsername,
&m.Aliases,
&m.CreatedAt,
&m.UpdatedAt,
&m.Deleted,
&m.Remarks,
)
if err != nil {
return c.JSON(500, map[string]interface{}{
"error": err.Error(),
})
}
members = append(members, m)
}
return c.JSON(200, members)
}
// GetMember returns a single member by ID
func GetMember(c echo.Context) error {
id := c.Param("id")
if id == "" {
return fmt.Errorf("id is required")
}
ctx, cancel := context.WithTimeout(
context.Background(),
2*time.Second,
)
defer cancel()
returnMember := Member{}
err := db.ActiveDB.QueryRowContext(
ctx,
"SELECT * FROM members WHERE id = ?",
id,
).Scan(
&returnMember.ID,
&returnMember.Name,
&returnMember.Timezone,
&returnMember.Email,
&returnMember.Website,
&returnMember.GuildedID,
&returnMember.SteamID64,
&returnMember.TeamspeakUID,
&returnMember.SteamProfileName,
&returnMember.DiscordID,
&returnMember.DiscordUsername,
&returnMember.Aliases,
&returnMember.CreatedAt,
&returnMember.UpdatedAt,
&returnMember.Deleted,
&returnMember.Remarks,
)
if err != nil {
return c.JSON(500, map[string]interface{}{
"error": err.Error(),
})
}
if ctx.Err() == context.DeadlineExceeded {
return c.JSON(500, map[string]interface{}{
"error": "request timed out",
})
}
return c.JSON(200, returnMember)
}

View File

@@ -1,9 +1,6 @@
package routes
import (
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/db"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/ops"
"errors"
"strings"
@@ -19,22 +16,8 @@ func SetupRoutes(
cfg := viper.GetViper()
prefixURL := strings.TrimRight(cfg.GetString("API_PREFIX"), "/")
mainPrefix := e.Group(prefixURL, func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// do something before the next handler
// ensure we always have a db connection
_, err := db.GetDB()
if err != nil {
return err
}
return next(c)
}
})
// MEMBER OPERATIONS
g := mainPrefix.Group("/members")
g.GET("", ops.GetMembers)
g.GET("/:id", ops.GetMember)
mainPrefix := e.Group(prefixURL)
setupMemberRoutes(e, mainPrefix)
setupRankRoutes(e, mainPrefix)
}

141
api/routes/member.go Normal file
View File

@@ -0,0 +1,141 @@
package routes
import (
"strconv"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/db"
"github.com/labstack/echo/v4"
"gorm.io/gorm"
)
func setupMemberRoutes(
e *echo.Echo,
mainPrefix *echo.Group,
) {
// MEMBER OPERATIONS
g := mainPrefix.Group("/member")
// Get all
g.GET("", func(c echo.Context) error {
var err error
member := new(db.Member)
// if query params are present, use them to filter
// otherwise, return all
rank := new(db.Rank)
if c.QueryParam("rank") != "" {
queriedID, err := strconv.Atoi(c.QueryParam("rank"))
if err != nil {
return c.JSON(400, err)
}
err = rank.GetByID(queriedID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
}
award := new(db.Award)
if c.QueryParam("award") != "" {
queriedID, err := strconv.Atoi(c.QueryParam("award"))
if err != nil {
return c.JSON(400, err)
}
err = award.GetByID(queriedID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
}
course := new(db.Course)
if c.QueryParam("course") != "" {
queriedID, err := strconv.Atoi(c.QueryParam("course"))
if err != nil {
return c.JSON(400, err)
}
err = course.GetByID(queriedID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
}
members, err := member.GetAll(c.Request().Context())
if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, members)
})
// Get one by id
g.GET("/:id", func(c echo.Context) error {
member := new(db.Member)
searchID, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, err)
}
err = member.GetByID(c.Request().Context(), searchID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, member)
})
// Create a new member
g.POST("", func(c echo.Context) error {
member := new(db.Member)
err := c.Bind(member)
if err != nil {
return c.JSON(400, err)
}
err = member.Create(c.Request().Context(), c)
if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, member)
})
// Update a member
g.PUT("/:id", func(c echo.Context) error {
member := new(db.Member)
searchID, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, err)
}
err = member.GetByID(c.Request().Context(), searchID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
err = c.Bind(member)
if err != nil {
return c.JSON(400, err)
}
err = member.Update(c.Request().Context(), c)
if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, member)
})
// Delete a member
g.DELETE("/:id", func(c echo.Context) error {
member := new(db.Member)
searchID, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, err)
}
err = member.GetByID(c.Request().Context(), searchID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
err = member.Delete(c.Request().Context(), c)
if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, member)
})
}

127
api/routes/rank.go Normal file
View File

@@ -0,0 +1,127 @@
package routes
import (
"strconv"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/db"
"github.com/labstack/echo/v4"
"gorm.io/gorm"
)
func setupRankRoutes(
e *echo.Echo,
mainPrefix *echo.Group,
) {
// RANK OPERATIONS
g := mainPrefix.Group("/rank")
// Get all
g.GET("", func(c echo.Context) error {
rank := new(db.Rank)
ranks, err := rank.GetAll()
if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, ranks)
})
// Get one by id
g.GET("/:id", func(c echo.Context) error {
rank := new(db.Rank)
searchID, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, err)
}
err = rank.GetByID(searchID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, rank)
})
// Create a new rank
g.POST("", func(c echo.Context) error {
rank := new(db.Rank)
err := c.Bind(rank)
if err != nil {
return c.JSON(400, err)
}
err = rank.Create()
if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, rank)
})
// Update a rank
g.PUT("/:id", func(c echo.Context) error {
rank := new(db.Rank)
searchID, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, err)
}
err = rank.GetByID(searchID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
err = c.Bind(rank)
if err != nil {
return c.JSON(400, err)
}
err = rank.Update()
if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, rank)
})
// Delete a rank
g.DELETE("/:id", func(c echo.Context) error {
rank := new(db.Rank)
searchID, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, err)
}
err = rank.GetByID(searchID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
err = rank.Delete()
if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, rank)
})
// Add a member to a rank
g.POST("/:id/member/:memberID", func(c echo.Context) error {
rank := new(db.Rank)
searchID, err := strconv.Atoi(c.Param("id"))
if err != nil {
return c.JSON(400, err)
}
err = rank.GetByID(searchID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
member := new(db.Member)
searchMemberID, err := strconv.Atoi(c.Param("memberID"))
if err != nil {
return c.JSON(400, err)
}
err = member.GetByID(c.Request().Context(), searchMemberID)
if err == gorm.ErrRecordNotFound {
return c.JSON(404, err)
} else if err != nil {
return c.JSON(500, err)
}
err = rank.AddHolder(member)
if err != nil {
return c.JSON(500, err)
}
return c.JSON(200, rank)
})
}