B: Rework Model framework and finish vendor model so UI and callback controller can be built

This commit is contained in:
Preston Baxter 2023-11-01 21:40:50 -05:00
parent b1a573b840
commit ca70d241d6
11 changed files with 104 additions and 89 deletions

View File

@ -19,8 +19,8 @@ type MongoConfig struct {
var cfg *config var cfg *config
func Init() { func Init() {
viper.SetConfigName("config") // name of config file (without extension) viper.SetConfigName("config") // name of config file (without extension)
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
viper.AddConfigPath("/etc/capstone") // path to look for the config file in viper.AddConfigPath("/etc/capstone") // path to look for the config file in
err := viper.ReadInConfig() err := viper.ReadInConfig()

View File

@ -16,11 +16,11 @@ import (
var VALIDATE_EMAIL_REGEX = regexp.MustCompile(`^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$`) var VALIDATE_EMAIL_REGEX = regexp.MustCompile(`^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$`)
type LoginPostBody struct { type LoginPostBody struct {
Email string `json:"email"` Email string `json:"email"`
Password string `json:"password"` Password string `json:"password"`
} }
func SignUpHandler (c *gin.Context) { func SignUpHandler(c *gin.Context) {
//get uname and password. //get uname and password.
conf := config.Config() conf := config.Config()
reqBody := &LoginPostBody{} reqBody := &LoginPostBody{}
@ -80,16 +80,12 @@ func SignUpHandler (c *gin.Context) {
return return
} }
//add vendor accounts
mongo.AddAccountsForUser(user)
now := time.Now().Unix() now := time.Now().Unix()
exp := time.Now().Add(12 * time.Hour).Unix() exp := time.Now().Add(12 * time.Hour).Unix()
//build jwt //build jwt
token := jwt.NewWithClaims(jwt.SigningMethodHS256, token := jwt.NewWithClaims(jwt.SigningMethodHS256,
AuthClaims{ AuthClaims{
Subject: user.UserId, Subject: user.MongoId().String(),
Expires: exp, Expires: exp,
IssuedAt: now, IssuedAt: now,
NotBefore: now, NotBefore: now,
@ -107,7 +103,7 @@ func SignUpHandler (c *gin.Context) {
//store jwt as cookie //store jwt as cookie
//TODO: Make sure set secure for prd deployment //TODO: Make sure set secure for prd deployment
c.SetCookie("authorization", jwtStr, 3600 * 24, "", "", false, true) c.SetCookie("authorization", jwtStr, 3600*24, "", "", false, true)
c.Redirect(302, "/dashboard") c.Redirect(302, "/dashboard")
} }
@ -163,7 +159,7 @@ func LoginHandler(c *gin.Context) {
//build jwt //build jwt
token := jwt.NewWithClaims(jwt.SigningMethodHS256, token := jwt.NewWithClaims(jwt.SigningMethodHS256,
AuthClaims{ AuthClaims{
Subject: user.UserId, Subject: user.MongoId().String(),
Expires: exp, Expires: exp,
IssuedAt: now, IssuedAt: now,
NotBefore: now, NotBefore: now,
@ -184,7 +180,7 @@ func LoginHandler(c *gin.Context) {
} else { } else {
secure = true secure = true
} }
c.SetCookie("authorization", jwtStr, 3600 * 24, "", "", secure, true) c.SetCookie("authorization", jwtStr, 3600*24, "", "", secure, true)
c.Redirect(302, "/dashboard") c.Redirect(302, "/dashboard")
} }
@ -198,7 +194,7 @@ func LogoutHandler(c *gin.Context) {
} else { } else {
secure = true secure = true
} }
c.SetCookie("authorization", "", 3600 * 24, "", "", secure, true) c.SetCookie("authorization", "", 3600*24, "", "", secure, true)
c.Redirect(302, "/login") c.Redirect(302, "/login")
} }

View File

@ -73,11 +73,11 @@ func AuthMiddleware(strict bool) gin.HandlerFunc {
return []byte(conf.JwtSecret), nil return []byte(conf.JwtSecret), nil
}) })
if err != nil { if err != nil {
if err == jwt.ErrTokenExpired{ if err == jwt.ErrTokenExpired {
log.Warn("Redirecting, jwt expired") log.Warn("Redirecting, jwt expired")
c.Redirect(301, "/login") c.Redirect(301, "/login")
return return
}else{ } else {
if strict { if strict {
log.Warnf("Redirecting, jwt issue: %s", err) log.Warnf("Redirecting, jwt issue: %s", err)
c.Redirect(301, "/login") c.Redirect(301, "/login")
@ -89,8 +89,7 @@ func AuthMiddleware(strict bool) gin.HandlerFunc {
} }
} }
if !parsedToken.Valid {
if !parsedToken.Valid {
if strict { if strict {
log.Warn("Redirecting, jwt invalid") log.Warn("Redirecting, jwt invalid")
c.Redirect(301, "/login") c.Redirect(301, "/login")

View File

@ -21,12 +21,12 @@ func BuildRouter(r *gin.Engine) {
log = logrus.New() log = logrus.New()
log.SetFormatter(&logrus.TextFormatter{ log.SetFormatter(&logrus.TextFormatter{
ForceColors: true, ForceColors: true,
}) })
r.GET("/", AuthMiddleware(false), LandingPage) r.GET("/", AuthMiddleware(false), LandingPage)
r.GET("/login", AuthMiddleware(false), LoginPage) r.GET("/login", AuthMiddleware(false), LoginPage)
r.GET("/signup",AuthMiddleware(false), SignUpPage) r.GET("/signup", AuthMiddleware(false), SignUpPage)
r.POST("/login", LoginHandler) r.POST("/login", LoginHandler)
r.POST("/signup", SignUpHandler) r.POST("/signup", SignUpHandler)
@ -36,7 +36,3 @@ func BuildRouter(r *gin.Engine) {
dashboard.Use(AuthMiddleware(true)) dashboard.Use(AuthMiddleware(true))
dashboard.GET("/", DashboardPage) dashboard.GET("/", DashboardPage)
} }

View File

@ -25,6 +25,7 @@ func SignUpPage(c *gin.Context) {
} }
func DashboardPage(c *gin.Context) { func DashboardPage(c *gin.Context) {
//Get vendors
if raw, exists := c.Get(USER_OBJ_KEY); exists { if raw, exists := c.Get(USER_OBJ_KEY); exists {
if user, ok := raw.(*models.User); ok { if user, ok := raw.(*models.User); ok {
renderTempl(c, templates.DashboardPage(user)) renderTempl(c, templates.DashboardPage(user))

View File

@ -7,7 +7,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
//Responds with 200ok and the rendered template // Responds with 200ok and the rendered template
func renderTempl(c *gin.Context, tmpl templ.Component) { func renderTempl(c *gin.Context, tmpl templ.Component) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
tmpl.Render(c.Request.Context(), buf) tmpl.Render(c.Request.Context(), buf)
@ -19,7 +19,6 @@ func badRequest(c *gin.Context, reason string) {
c.JSON(400, map[string]string{"error": reason}) c.JSON(400, map[string]string{"error": reason})
} }
func serverError(c *gin.Context, reason string) { func serverError(c *gin.Context, reason string) {
c.JSON(504, map[string]string{"error": reason}) c.JSON(504, map[string]string{"error": reason})
} }

View File

@ -2,14 +2,19 @@ package db
import ( import (
"context" "context"
"errors"
"git.preston-baxter.com/Preston_PLB/capstone/frontend-service/config"
"git.preston-baxter.com/Preston_PLB/capstone/frontend-service/db/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
) )
type Model interface { type Model interface {
Save(client *mongo.Client) error MongoId() primitive.ObjectID
Delete(client *mongo.Client) error UpdateObjectInfo()
} }
type DB struct { type DB struct {
@ -26,5 +31,33 @@ func NewClient(uri string) (*DB, error) {
} }
func (db *DB) SaveModel(m Model) error { func (db *DB) SaveModel(m Model) error {
return m.Save(db.client) conf := config.Config()
opts := options.Update().SetUpsert(true)
res, err := db.client.Database(conf.Mongo.EntDb).Collection(conf.Mongo.EntCol).UpdateOne(context.Background(), bson.M{"_id": m.MongoId()}, bson.M{"$set": m}, opts)
if err != nil {
return err
}
if res.MatchedCount == 0 && res.ModifiedCount == 0 && res.UpsertedCount == 0 {
return errors.New("Failed to update vendor account properly")
}
return nil
}
func (db *DB) DeleteModel(m Model) error {
conf := config.Config()
opts := options.Delete()
res, err := db.client.Database(conf.Mongo.EntDb).Collection(conf.Mongo.EntCol).DeleteOne(context.Background(), bson.M{"_id": m.MongoId()}, opts)
if err != nil {
return err
}
if res.DeletedCount == 0 {
return errors.New("There was no item to delete")
}
return nil
} }

View File

@ -1,66 +1,36 @@
package models package models
import ( import (
"context"
"errors"
"time" "time"
"git.preston-baxter.com/Preston_PLB/capstone/frontend-service/config"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
) )
const USER_TYPE string = "user" const USER_TYPE string = "user"
type User struct { type User struct {
CommonFields `bson:"obj_info"` *CommonFields `bson:"obj_info"`
mongoId primitive.ObjectID `bson:"_id,omitempty"` mongoId primitive.ObjectID `bson:"_id,omitempty"`
UserId string `bson:"user_id,omitempty"` Email string `bson:"email,omitempty"`
Email string `bson:"email,omitempty"` PassowrdHash string `bson:"password_hash,omitempty"`
PassowrdHash string `bson:"password_hash,omitempty"`
} }
func (user *User) Save(client *mongo.Client) error { func (user *User) MongoId() primitive.ObjectID {
conf := config.Config()
if user.mongoId.IsZero() { if user.mongoId.IsZero() {
now := time.Now() now := time.Now()
user.EntityType = USER_TYPE
user.CreatedAt = now
user.UpdatedAt = now
user.UserId = uuid.New().String()
user.mongoId = primitive.NewObjectIDFromTimestamp(now) user.mongoId = primitive.NewObjectIDFromTimestamp(now)
} }
opts := options.Update().SetUpsert(true) return user.mongoId
res, err := client.Database(conf.Mongo.EntDb).Collection(conf.Mongo.EntCol).UpdateOne(context.Background(), bson.M{"user_id": user.UserId}, bson.M{"$set": user},opts)
if err != nil {
return err
}
if res.MatchedCount == 0 && res.ModifiedCount == 0 && res.UpsertedCount == 0 {
return errors.New("Failed to update client properly")
}
return nil
} }
func (user *User) Delete(client *mongo.Client) error { func (user *User) UpdateObjectInfo() {
conf := config.Config() now := time.Now()
opts := options.Delete() if user.CommonFields == nil {
user.CommonFields = new(CommonFields)
res, err := client.Database(conf.Mongo.EntDb).Collection(conf.Mongo.EntCol).DeleteOne(context.Background(), bson.M{"_id": user.mongoId}, opts) user.EntityType = USER_TYPE
if err != nil { user.CreatedAt = now
return err
} }
user.UpdatedAt = now
if res.DeletedCount == 0 {
return errors.New("There was no item to delete")
}
return nil
} }

View File

@ -1,24 +1,46 @@
package models package models
import "go.mongodb.org/mongo-driver/bson/primitive" import (
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
const VENDOR_TYPE = "vendor"
const VENDOR_ACCOUNT_TYPE = "vendor_account" const VENDOR_ACCOUNT_TYPE = "vendor_account"
type Vendor struct { type OauthCredential struct {
CommonFields `bson:"obj_info"` AccessToken string `bson:"access_token,omitempty" json:"access_token,omitempty"`
mongoId primitive.ObjectID `bson:"_id,omitempty"` ExpiresIn int `bson:"expires_in,omitempty" json:"expires_in,omitempty"`
VendorId string `bson:"vendor_id,omitempty"` ExpiresAt time.Time `bson:"expires_at,omitempty" json:"expires_at,omitempty"`
Name string `bson:"name,omitempty"` TokenType string `bson:"token_type,omitempty" json:"token_type,omitempty"`
OAuthUrl string `bson:"oauth_url"` Scope string `bson:"scope,omitempty" json:"scope,omitempty"`
RefreshToken string `bson:"refresh_token,omitempty" json:"refresh_token,omitempty"`
} }
type VendorAccount struct { type VendorAccount struct {
CommonFields `bson:"obj_info"` *CommonFields `bson:"obj_info"`
*Vendor mongoId primitive.ObjectID `bson:"_id,omitempty"`
mongoId primitive.ObjectID `bson:"_id,omitempty"` UserId primitive.ObjectID `bson:"user_id,omitempty"`
UserId string `bson:"user_id,omitempty"` Secret string `bson:"secret,omitempty"`
Secret string `bson:"secret,omitempty"` VendorId string `bson:"vendor_id,omitempty"`
VendorId string `bson:"vendor_id,omitempty"` OauthCredentials *OauthCredential `bson:"ouath_credentials,omitempty"`
Authenticated bool `bson:"authenticated,omitempty"` }
func (va *VendorAccount) MongoId() primitive.ObjectID {
if va.mongoId.IsZero() {
now := time.Now()
va.mongoId = primitive.NewObjectIDFromTimestamp(now)
}
return va.mongoId
}
func (va *VendorAccount) UpdateObjectInfo() {
now := time.Now()
if va.CommonFields == nil {
va.CommonFields = new(CommonFields)
va.EntityType = VENDOR_ACCOUNT_TYPE
va.CreatedAt = now
}
va.UpdatedAt = now
} }

View File

@ -36,7 +36,7 @@ func (db *DB) FindUserById(id string) (*models.User, error) {
conf := config.Config() conf := config.Config()
opts := options.FindOne() opts := options.FindOne()
res := db.client.Database(conf.Mongo.EntDb).Collection(conf.Mongo.EntCol).FindOne(context.Background(), bson.M{"user_id": id, "obj_info.ent": models.USER_TYPE}, opts) res := db.client.Database(conf.Mongo.EntDb).Collection(conf.Mongo.EntCol).FindOne(context.Background(), bson.M{"_id": id, "obj_info.ent": models.USER_TYPE}, opts)
if res.Err() != nil { if res.Err() != nil {
if res.Err() == mongo.ErrNoDocuments { if res.Err() == mongo.ErrNoDocuments {

View File

@ -9,7 +9,7 @@ import (
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
) )
func (db *DB) FindVendorAccountByUser(userId string) ([]models.VendorAccount, error){ func (db *DB) FindVendorAccountByUser(userId string) ([]models.VendorAccount, error) {
conf := config.Config() conf := config.Config()
opts := options.Find() opts := options.Find()
@ -26,4 +26,3 @@ func (db *DB) FindVendorAccountByUser(userId string) ([]models.VendorAccount, er
return vendors, nil return vendors, nil
} }