diff --git a/src/db/accounts.go b/src/db/accounts.go new file mode 100644 index 0000000..f947863 --- /dev/null +++ b/src/db/accounts.go @@ -0,0 +1,57 @@ +package db + +import ( + "context" + "strings" + + "github.com/google/uuid" + log "github.com/sirupsen/logrus" +) + +// AccountCreate writes a user to database +func (d Db) AccountCreate(input AccountCreateInput) (CreatedAccount, error) { + accountSQL := "INSERT INTO accounts (id, \"accountName\", \"apiKey\", password) VALUES($1,$2,$3,$4);" + + _, err := d.DbPool.Exec(context.Background(), accountSQL, input.ID, input.AccountName, input.APIKey, input.Password) + if err != nil { + if strings.HasPrefix(err.Error(), "ERROR: duplicate key") { + log.WithFields(log.Fields{"accountName": input.AccountName}).Debug("Duplicate accountName in accounts database") + } else { + log.Error("Database error when trying to add account: " + err.Error()) + } + + return CreatedAccount{}, err + } + + log.WithFields(log.Fields{ + "id": input.ID, + "accountName": input.AccountName, + }).Info("Added account to database") + + accountFieldsSQL := "INSERT INTO \"accountsFields\" (id, \"accountId\", name, value) VALUES($1,$2,$3,$4);" + for _, field := range input.Fields { + newFieldID, uuidErr := uuid.NewRandom() + if uuidErr != nil { + log.Fatal("Could not create new Uuid, err: " + uuidErr.Error()) + } + + _, err := d.DbPool.Exec(context.Background(), accountFieldsSQL, newFieldID, input.ID, field.Name, field.Values) + if err != nil { + if strings.HasPrefix(err.Error(), "ERROR: duplicate key") { + log.Error("Database error when trying to account field: " + err.Error()) + } + } + + log.WithFields(log.Fields{ + "accountId": input.ID, + "fieldName": field.Name, + "fieldValues": field.Values, + }).Debug("Added account field") + } + + return CreatedAccount{ + ID: input.ID, + AccountName: input.AccountName, + APIKey: input.APIKey, + }, nil +} diff --git a/src/db/types.go b/src/db/types.go index aae70a5..d838a4c 100644 --- a/src/db/types.go +++ b/src/db/types.go @@ -5,8 +5,8 @@ import ( "github.com/jackc/pgx/v4/pgxpool" ) -// Account is an account in the system -type Account struct { +// CreatedAccount is a newly created account in the system +type CreatedAccount struct { ID uuid.UUID `json:"id"` AccountName string `json:"accountName"` APIKey string `json:"apiKey"` diff --git a/src/handlers/post.go b/src/handlers/post.go index 9da8222..ce157eb 100644 --- a/src/handlers/post.go +++ b/src/handlers/post.go @@ -1,7 +1,6 @@ package handlers import ( - "fmt" "strings" log "github.com/sirupsen/logrus" @@ -28,8 +27,6 @@ func (h Handlers) AccountCreate(c *fiber.Ctx) error { }) } - fmt.Println(accountInput) - var errors []ResJSONError if accountInput.AccountName == "" { diff --git a/src/main.go b/src/main.go new file mode 100644 index 0000000..01e7ecd --- /dev/null +++ b/src/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "context" + "os" + "strings" + + "github.com/gofiber/fiber/v2" + "github.com/google/uuid" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/joho/godotenv" + log "github.com/sirupsen/logrus" + "gitlab.larvit.se/power-plan/auth/src/db" + h "gitlab.larvit.se/power-plan/auth/src/handlers" +) + +func createAdminAccount(Db db.Db) { + adminAccountID, uuidErr := uuid.NewRandom() + if uuidErr != nil { + log.Fatal("Could not create new Uuid, err: " + uuidErr.Error()) + } + _, adminAccountErr := Db.AccountCreate(db.AccountCreateInput{ + ID: adminAccountID, + AccountName: "admin", + APIKey: os.Getenv("ADMIN_API_KEY"), + Password: "", + }) + if adminAccountErr != nil && strings.HasPrefix(adminAccountErr.Error(), "ERROR: duplicate key") { + log.Info("Admin account already created, nothing written to database") + } else if adminAccountErr != nil { + log.Fatal("Could not create admin account, err: " + adminAccountErr.Error()) + } +} + +func main() { + err := godotenv.Load() + if err != nil { + log.Fatal("Error loading .env file") + } + + // Add this line for logging filename and line number! + // log.SetReportCaller(true) + log.SetLevel(log.DebugLevel) + + dbPool, err := pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL")) + if err != nil { + log.Fatal("Failed to open DB connection: ", err) + } else { + log.Info("Connected to PostgreSQL database") + } + defer dbPool.Close() + + app := fiber.New() + + Db := db.Db{DbPool: dbPool} + handlers := h.Handlers{Db: Db} + + createAdminAccount(Db) + + // Always require application/json + app.Use(handlers.RequireJSON) + + app.Get("/", handlers.Hello) + app.Get("/account/:accountID", handlers.AccountGet) + app.Post("/account", handlers.AccountCreate) + app.Post("/auth/api-key", handlers.AccountAuthAPIKey) + + log.WithFields(log.Fields{"WEB_BIND_HOST": os.Getenv("WEB_BIND_HOST")}).Info("Trying to start web server") + + if err := app.Listen(os.Getenv("WEB_BIND_HOST")); err != nil { + log.Fatal(err) + } + + log.Info("Webb server closed, shutting down") +} diff --git a/src/utils/utils.go b/src/utils/utils.go new file mode 100644 index 0000000..cfdb116 --- /dev/null +++ b/src/utils/utils.go @@ -0,0 +1,50 @@ +package utils + +import ( + "math/rand" + "strings" + "time" + + "golang.org/x/crypto/bcrypt" +) + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letterBytes) { + sb.WriteByte(letterBytes[idx]) + i-- + } + cache >>= letterIdxBits + remain-- + } + + return sb.String() +}