From 8110172a38b91a7a5076e0d8ef3daca47b681299 Mon Sep 17 00:00:00 2001 From: cdricms <36056008+cdricms@users.noreply.github.com> Date: Wed, 29 Jan 2025 18:09:41 +0100 Subject: [PATCH] Reorganization of backend + new routes --- backend/api/blogs/blog.go | 5 +- backend/api/blogs/blogs.go | 5 +- .../api/blogs/{delete_blog.go => delete.go} | 0 backend/api/blogs/new.go | 8 +- backend/api/contact.go | 2 +- backend/api/core/schemas.go | 242 ------------------ backend/api/events/delete.go | 5 +- backend/api/events/event.go | 25 +- backend/api/events/events.go | 29 +++ backend/api/events/new.go | 5 +- backend/api/events/update.go | 15 +- backend/api/get_csrf.go | 2 +- backend/api/media/delete.go | 5 +- backend/api/media/media.go | 15 +- backend/api/media/upload.go | 5 +- backend/api/media/verify.go | 2 +- backend/api/middlewares.go | 2 +- backend/api/permissions/delete.go | 1 - backend/api/permissions/new.go | 1 - backend/api/permissions/permission.go | 40 +++ backend/api/permissions/permissions.go | 35 +++ backend/api/permissions/resource.go | 38 +++ backend/api/permissions/update.go | 1 - backend/api/roles/add_permission.go | 61 +++++ backend/api/roles/delete.go | 29 +++ backend/api/roles/new.go | 36 +++ backend/api/roles/permissions.go | 34 +++ backend/api/roles/remove_permission.go | 31 +++ backend/api/roles/role.go | 39 +++ backend/api/roles/roles.go | 38 +++ backend/api/roles/update.go | 51 ++++ backend/api/shortcodes/delete.go | 5 +- backend/api/shortcodes/new.go | 5 +- backend/api/shortcodes/shortcode.go | 5 +- backend/api/shortcodes/shortcodes.go | 5 +- backend/api/shortcodes/update.go | 15 +- backend/api/users/add_role.go | 61 +++++ backend/api/users/auth.go | 5 +- backend/api/users/delete.go | 5 +- backend/api/users/me.go | 2 +- backend/api/users/new.go | 7 +- backend/api/users/remove_role.go | 32 +++ backend/api/users/roles.go | 31 +++ backend/api/users/update.go | 17 +- backend/api/users/user.go | 6 +- backend/api/users/users.go | 8 +- backend/cmd/migrate/main.go | 2 +- backend/{api => }/core/csrf.go | 0 backend/core/models/blogs.go | 24 ++ backend/core/models/events.go | 25 ++ backend/core/models/events_to_user.go | 16 ++ backend/core/models/main.go | 8 + backend/core/models/media.go | 18 ++ backend/core/models/permissions.go | 15 ++ backend/core/models/permissions_to_roles.go | 16 ++ backend/core/models/roles.go | 14 + backend/core/models/shortcodes.go | 36 +++ backend/core/models/users.go | 60 +++++ backend/core/models/users_to_roles.go | 16 ++ backend/core/models/website_settings.go | 13 + backend/{api => }/core/paginated.go | 0 backend/core/permissions.go | 61 +++++ backend/{api => }/core/router.go | 0 backend/core/schemas.go | 64 +++++ backend/main.go | 117 ++++----- frontend/app/(main)/planning/page.tsx | 2 +- .../{planning.tsx.tsx => planning.tsx} | 1 + 67 files changed, 1124 insertions(+), 400 deletions(-) rename backend/api/blogs/{delete_blog.go => delete.go} (100%) delete mode 100644 backend/api/core/schemas.go create mode 100644 backend/api/events/events.go delete mode 100644 backend/api/permissions/delete.go delete mode 100644 backend/api/permissions/new.go create mode 100644 backend/api/permissions/resource.go delete mode 100644 backend/api/permissions/update.go create mode 100644 backend/api/users/add_role.go create mode 100644 backend/api/users/remove_role.go create mode 100644 backend/api/users/roles.go rename backend/{api => }/core/csrf.go (100%) create mode 100644 backend/core/models/blogs.go create mode 100644 backend/core/models/events.go create mode 100644 backend/core/models/events_to_user.go create mode 100644 backend/core/models/main.go create mode 100644 backend/core/models/media.go create mode 100644 backend/core/models/permissions.go create mode 100644 backend/core/models/permissions_to_roles.go create mode 100644 backend/core/models/roles.go create mode 100644 backend/core/models/shortcodes.go create mode 100644 backend/core/models/users.go create mode 100644 backend/core/models/users_to_roles.go create mode 100644 backend/core/models/website_settings.go rename backend/{api => }/core/paginated.go (100%) create mode 100644 backend/core/permissions.go rename backend/{api => }/core/router.go (100%) create mode 100644 backend/core/schemas.go rename frontend/components/{planning.tsx.tsx => planning.tsx} (99%) diff --git a/backend/api/blogs/blog.go b/backend/api/blogs/blog.go index 0101c24..4260204 100644 --- a/backend/api/blogs/blog.go +++ b/backend/api/blogs/blog.go @@ -4,7 +4,8 @@ import ( "context" "net/http" - core "fr.latosa-escrima/api/core" + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleBlog(w http.ResponseWriter, r *http.Request) { @@ -12,7 +13,7 @@ func HandleBlog(w http.ResponseWriter, r *http.Request) { blog_uuid := r.PathValue("uuid") - var blog core.Blog + var blog models.Blog _, err := core.DB.NewSelect(). Model(&blog). Where("blog_id = ?", blog_uuid). diff --git a/backend/api/blogs/blogs.go b/backend/api/blogs/blogs.go index ab10e53..3b0ee3b 100644 --- a/backend/api/blogs/blogs.go +++ b/backend/api/blogs/blogs.go @@ -5,11 +5,12 @@ import ( "fmt" "net/http" - core "fr.latosa-escrima/api/core" + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleGetBlogs(w http.ResponseWriter, r *http.Request) { - var blog []core.Blog + var blog []models.Blog count, err := core.DB.NewSelect(). Model(&blog). Relation("Author"). diff --git a/backend/api/blogs/delete_blog.go b/backend/api/blogs/delete.go similarity index 100% rename from backend/api/blogs/delete_blog.go rename to backend/api/blogs/delete.go diff --git a/backend/api/blogs/new.go b/backend/api/blogs/new.go index 96b4916..fe69e3b 100644 --- a/backend/api/blogs/new.go +++ b/backend/api/blogs/new.go @@ -3,9 +3,11 @@ package blogs import ( "context" "encoding/json" - "net/http" "io" - core "fr.latosa-escrima/api/core" + "net/http" + + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleNew(w http.ResponseWriter, r *http.Request) { @@ -17,7 +19,7 @@ func HandleNew(w http.ResponseWriter, r *http.Request) { }.Respond(w, http.StatusNoContent) return } - var blog core.Blog + var blog models.Blog if err := json.NewDecoder(r.Body).Decode(&blog); err != nil { core.JSONError{ Status: core.Error, diff --git a/backend/api/contact.go b/backend/api/contact.go index 8d4333a..f03f815 100644 --- a/backend/api/contact.go +++ b/backend/api/contact.go @@ -7,7 +7,7 @@ import ( "os" "strconv" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" "gopkg.in/gomail.v2" ) diff --git a/backend/api/core/schemas.go b/backend/api/core/schemas.go deleted file mode 100644 index 1569ff8..0000000 --- a/backend/api/core/schemas.go +++ /dev/null @@ -1,242 +0,0 @@ -package core - -import ( - "context" - "database/sql" - "errors" - "fmt" - "time" - - "github.com/google/uuid" - "github.com/uptrace/bun" - "github.com/uptrace/bun/dialect/pgdialect" - "github.com/uptrace/bun/driver/pgdriver" -) - -var DB *bun.DB - -type DSN struct { - Hostname string - Port string - DBName string - User string - Password string -} - -func (dsn *DSN) ToString() string { - return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable", dsn.User, dsn.Password, dsn.Hostname, dsn.Port, dsn.DBName) -} - -type Status string - -const ( - Active Status = "Active" - Inactive Status = "Inactive" -) - -type Group string - -const ( - LatosaGroup Group = "latosa" - WingTsunGroup Group = "wing-tsun" -) - -type UserAttributes struct { - Groups []Group `json:"groups"` -} - -type PermissionConditions struct { - Groups *[]Group `json:"groups,omitempty"` -} - -type User struct { - bun.BaseModel `bun:"table:users"` - - UserID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"userId"` - FirstName string `bun:"firstname,notnull" json:"firstname"` - LastName string `bun:"lastname,notnull" json:"lastname"` - Email string `bun:"email,unique,notnull" json:"email"` - Password string `bun:"password,notnull" json:"password,omitempty"` - Phone string `bun:"phone,notnull" json:"phone"` - CreatedAt time.Time `bun:"created_at,default:current_timestamp" json:"createdAt"` - UpdatedAt time.Time `bun:"updated_at,default:current_timestamp" json:"updatedAt"` - Events []Event `bun:"m2m:events_to_users,join:User=Event" json:"events,omitempty"` - Articles []*Blog `bun:"rel:has-many,join:user_id=blog_id" json:"articles,omitempty"` - Attributes UserAttributes `bun:"attributes,type:jsonb" json:"attributes"` -} - -func (u *User) Insert(ctx context.Context) (sql.Result, error) { - u.Password = fmt.Sprintf("crypt('%s', gen_salt('bf'))", u.Password) - return DB.NewInsert(). - Model(u). - Value("password", u.Password). - Exec(ctx) -} - -func Verify(ctx context.Context, email, password string) (*User, error) { - var user User - count, err := DB.NewSelect(). - Model(&user). - Where("email = ? AND password = crypt(?, password)", email, password). - Limit(1). - ScanAndCount(context.Background()) - if count == 0 { - return nil, fmt.Errorf("invalid email or password") - } - if err != nil { - if err == sql.ErrNoRows { - return nil, fmt.Errorf("invalid email or password") - } - return nil, err - } - - return &user, nil -} - -type Permission struct { - bun.BaseModel `bun:"table:permissions"` - ID int `bun:"id,pk,autoincrement" json:"id"` - Resource string `bun:"resource,notnull" json:"resource"` - Action string `bun:"action,notnull" json:"action"` - Conditions PermissionConditions `bun:"conditions,type:jsonb" json:"conditions"` -} - -type Role struct { - bun.BaseModel `bun:"table:roles"` - ID uuid.UUID `bun:"id,pk,type:uuid,default:gen_random_uuid()" json:"id"` - Name string `bun:"name,unique,notnull" json:"name"` -} - -type PermissionToRole struct { - bun.BaseModel `bun:"table:permissions_to_users"` - - PermissionID int `bun:"permission_id,pk"` - RoleID uuid.UUID `bun:"type:uuid,pk"` - - Permission *Permission `bun:"rel:belongs-to,join:permission_id=id"` - Role *Role `bun:"rel:belongs-to,join:role_id=id"` -} - -type UserToRole struct { - bun.BaseModel `bun:"table:users_to_roles"` - - UserID uuid.UUID `bun:"user_id,type:uuid,pk"` - RoleID uuid.UUID `bun:"type:uuid,pk"` - - User *User `bun:"rel:belongs-to,join:user_id=user_id"` - Role *Role `bun:"rel:belongs-to,join:role_id=id"` -} - -type Event struct { - bun.BaseModel `bun:"table:events"` - - EventID uuid.UUID `bun:"event_id,type:uuid,pk,default:gen_random_uuid()" json:"id"` - CreationDate time.Time `bun:"creation_date,notnull,default:current_timestamp" json:"creationDate"` - ScheduleStart time.Time `bun:"schedule_start,notnull" json:"start"` - ScheduleEnd time.Time `bun:"schedule_end,notnull" json:"end"` - Status Status `bun:"status,notnull,default:'Inactive'" json:"status"` -} - -type EventToUser struct { - bun.BaseModel `bun:"table:events_to_users"` - - EventID uuid.UUID `bun:"type:uuid,pk"` - UserID uuid.UUID `bun:"type:uuid,pk"` - - Event *Event `bun:"rel:belongs-to,join:event_id=event_id"` - User *User `bun:"rel:belongs-to,join:user_id=user_id"` -} - -type Blog struct { - bun.BaseModel `bun:"table:blogs"` - - BlogID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"blogID"` - Slug string `bun:"slug,unique,notnull" json:"slug"` - Content string `bun:"content,notnull" json:"content"` - Label string `bun:"label" json:"label"` - AuthorID uuid.UUID `bun:"author_id,type:uuid,notnull" json:"authorID"` - Published time.Time `bun:"published,default:current_timestamp" json:"published"` - Summary string `bun:"summary" json:"summary"` - Image string `bun:"image" json:"image"` - Href string `bun:"href" json:"href"` - - Author User `bun:"rel:belongs-to,join:author_id=user_id" json:"author"` -} - -type WebsiteSettings struct { - bun.BaseModel `bun:"table:website_settings"` - - ID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"id"` - AutoAcceptDemand bool `bun:"auto_accept_demand,default:false" json:"autoAcceptDemand"` -} - -type Media struct { - bun.BaseModel `bun:"table:media"` - ID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"id"` - AuthorID uuid.UUID `bun:"author_id,type:uuid,notnull" json:"authorID"` - Author *User `bun:"rel:belongs-to,join:author_id=user_id" json:"author,omitempty"` - Type string `bun:"media_type" json:"type"` // Image, Video, GIF etc. Add support for PDFs? - Alt string `bun:"media_alt" json:"alt"` - Path string `bun:"media_path" json:"path"` - Size int64 `bun:"media_size" json:"size"` - URL string `bun:"-" json:"url"` -} - -type ShortcodeType string - -const ( - ShortcodeMedia ShortcodeType = "media" - ShortcodeValue ShortcodeType = "value" -) - -type Shortcode struct { - bun.BaseModel `bun:"table:shortcodes,alias:sc"` - - ID int64 `bun:"id,pk,autoincrement" json:"id"` // Primary key - Code string `bun:"code,notnull,unique" json:"code"` // The shortcode value - Type ShortcodeType `bun:"shortcode_type,notnull" json:"type"` - Value *string `bun:"value" json:"value,omitempty"` - MediaID *uuid.UUID `bun:"media_id,type:uuid" json:"media_id,omitempty"` // Nullable reference to another table's ID - Media *Media `bun:"rel:belongs-to,join:media_id=id" json:"media,omitempty"` // Relation to Media -} - -func (s *Shortcode) Validate() error { - if s.Value != nil && s.MediaID != nil { - return errors.New("both value and media_id cannot be set at the same time") - } - if s.Value == nil && s.MediaID == nil { - return errors.New("either value or media_id must be set") - } - return nil -} - -func InitDatabase(dsn DSN) (*bun.DB, error) { - sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn.ToString()))) - db := bun.NewDB(sqldb, pgdialect.New()) - - ctx := context.Background() - - _, err := db.ExecContext(ctx, "CREATE EXTENSION IF NOT EXISTS pgcrypto;") - if err != nil { - return nil, err - } - db.RegisterModel((*EventToUser)(nil)) - db.RegisterModel((*PermissionToRole)(nil)) - db.RegisterModel((*UserToRole)(nil)) - _, err = db.NewCreateTable().Model((*User)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*Event)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*EventToUser)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*Blog)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*WebsiteSettings)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*Media)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*Shortcode)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*Role)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*Permission)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*PermissionToRole)(nil)).IfNotExists().Exec(ctx) - _, err = db.NewCreateTable().Model((*UserToRole)(nil)).IfNotExists().Exec(ctx) - if err != nil { - return nil, err - } - - return db, nil -} diff --git a/backend/api/events/delete.go b/backend/api/events/delete.go index ca57870..dc2a0c6 100644 --- a/backend/api/events/delete.go +++ b/backend/api/events/delete.go @@ -5,12 +5,13 @@ import ( "log" "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleDelete(w http.ResponseWriter, r *http.Request) { uuid := r.PathValue("event_uuid") - var event core.Event + var event models.Event res, err := core.DB.NewDelete(). Model(&event). Where("event_id = ?", uuid). diff --git a/backend/api/events/event.go b/backend/api/events/event.go index 796a987..79ea199 100644 --- a/backend/api/events/event.go +++ b/backend/api/events/event.go @@ -2,15 +2,15 @@ package events import ( "context" - "fmt" "net/http" - core "fr.latosa-escrima/api/core" + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleEvent(w http.ResponseWriter, r *http.Request) { event_uuid := r.PathValue("event_uuid") - var event core.Event + var event models.Event _, err := core.DB.NewSelect().Model(&event).Where("uuid = ?", event_uuid).ScanAndCount(context.Background()) if err != nil { core.JSONError{ @@ -27,22 +27,3 @@ func HandleEvent(w http.ResponseWriter, r *http.Request) { }.Respond(w, http.StatusOK) return } - -func HandleEvents(w http.ResponseWriter, r *http.Request) { - var events []core.Event - rowsCount, err := core.DB.NewSelect().Model(&events).ScanAndCount(context.Background()) - if err != nil { - core.JSONError{ - Status: core.Error, - Message: err.Error(), - }.Respond(w, http.StatusInternalServerError) - return - } - - core.JSONSuccess{ - Status: core.Success, - Message: fmt.Sprintf("%d Event successfully sent", rowsCount), - Data: events, - }.Respond(w, http.StatusOK) - return -} diff --git a/backend/api/events/events.go b/backend/api/events/events.go new file mode 100644 index 0000000..e78c67c --- /dev/null +++ b/backend/api/events/events.go @@ -0,0 +1,29 @@ +package events + +import ( + "context" + "fmt" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleEvents(w http.ResponseWriter, r *http.Request) { + var events []models.Event + rowsCount, err := core.DB.NewSelect().Model(&events).ScanAndCount(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: fmt.Sprintf("%d Event successfully sent", rowsCount), + Data: events, + }.Respond(w, http.StatusOK) + return +} diff --git a/backend/api/events/new.go b/backend/api/events/new.go index c253e11..55699c0 100644 --- a/backend/api/events/new.go +++ b/backend/api/events/new.go @@ -5,11 +5,12 @@ import ( "encoding/json" "net/http" - core "fr.latosa-escrima/api/core" + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleNew(w http.ResponseWriter, r *http.Request) { - var event core.Event + var event models.Event err := json.NewDecoder(r.Body).Decode(&event) if err != nil { core.JSONError{ diff --git a/backend/api/events/update.go b/backend/api/events/update.go index 58dd47e..cac415f 100644 --- a/backend/api/events/update.go +++ b/backend/api/events/update.go @@ -6,12 +6,13 @@ import ( "log" "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" "github.com/google/uuid" ) func HandleUpdate(w http.ResponseWriter, r *http.Request) { - var event core.Event + var event models.Event err := json.NewDecoder(r.Body).Decode(&event) if err != nil { core.JSONError{ @@ -33,16 +34,6 @@ func HandleUpdate(w http.ResponseWriter, r *http.Request) { log.Println(event) - // val := reflect.ValueOf(event) - // typ := val.Type() - // - // for i := 0; i < val.NumField(); i++ { - // field := val.Field(i) - // fieldType := typ.Field(i) - // fmt.Printf("Field Name: %s, Field Value: %v\n", fieldType.Name, field.Interface()) - // if fiel - // } - _, err = core.DB.NewUpdate(). Model(&event). OmitZero(). diff --git a/backend/api/get_csrf.go b/backend/api/get_csrf.go index 6f27b6d..88d3b2a 100644 --- a/backend/api/get_csrf.go +++ b/backend/api/get_csrf.go @@ -4,7 +4,7 @@ import ( "fmt" "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" "github.com/gorilla/csrf" ) diff --git a/backend/api/media/delete.go b/backend/api/media/delete.go index fbb6903..a9b7faa 100644 --- a/backend/api/media/delete.go +++ b/backend/api/media/delete.go @@ -6,12 +6,13 @@ import ( "net/http" "os" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleDelete(w http.ResponseWriter, r *http.Request) { uuid := r.PathValue("media_uuid") - var media core.Media + var media models.Media res, err := core.DB.NewDelete(). Model(&media). Where("id = ?", uuid). diff --git a/backend/api/media/media.go b/backend/api/media/media.go index ce36d05..ab9a842 100644 --- a/backend/api/media/media.go +++ b/backend/api/media/media.go @@ -7,7 +7,8 @@ import ( "net/http" "strconv" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" "fr.latosa-escrima/utils" ) @@ -32,12 +33,12 @@ func HandleMedia(w http.ResponseWriter, r *http.Request) { offset := (page - 1) * limit total, err := core.DB.NewSelect(). - Model((*core.Media)(nil)). + Model((*models.Media)(nil)). Count(context.Background()) totalPages := int(math.Max(1, float64(total/limit))) - var media []core.Media + var media []models.Media err = core.DB.NewSelect(). Model(&media). Limit(limit). @@ -51,7 +52,7 @@ func HandleMedia(w http.ResponseWriter, r *http.Request) { return } baseURL := utils.GetURL(r) - media = utils.Map(media, func(m core.Media) core.Media { + media = utils.Map(media, func(m models.Media) models.Media { m.Author = nil m.URL = fmt.Sprintf("%s%s/file", baseURL, m.ID) return m @@ -60,7 +61,7 @@ func HandleMedia(w http.ResponseWriter, r *http.Request) { core.JSONSuccess{ Status: core.Success, Message: "Media successfully retrieved", - Data: core.Paginated[core.Media]{ + Data: core.Paginated[models.Media]{ Page: page, Limit: limit, TotalPages: totalPages, @@ -71,7 +72,7 @@ func HandleMedia(w http.ResponseWriter, r *http.Request) { func HandleMediaDetails(w http.ResponseWriter, r *http.Request) { uuid := r.PathValue("media_uuid") - var media core.Media + var media models.Media err := core.DB.NewSelect(). Model(&media). Where("id = ?", uuid). @@ -98,7 +99,7 @@ func HandleMediaDetails(w http.ResponseWriter, r *http.Request) { func HandleMediaFile(w http.ResponseWriter, r *http.Request) { uuid := r.PathValue("media_uuid") - var media core.Media + var media models.Media err := core.DB.NewSelect(). Model(&media). Where("id = ?", uuid). diff --git a/backend/api/media/upload.go b/backend/api/media/upload.go index 91fc6db..e942b76 100644 --- a/backend/api/media/upload.go +++ b/backend/api/media/upload.go @@ -8,7 +8,8 @@ import ( "os" "path/filepath" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" "fr.latosa-escrima/utils" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" @@ -95,7 +96,7 @@ func HandleUpload(w http.ResponseWriter, r *http.Request) { }.Respond(w, http.StatusInternalServerError) return } - media := &core.Media{ + media := &models.Media{ AuthorID: id, Type: fileHeader.Header.Get("Content-Type"), Alt: "To be implemented", diff --git a/backend/api/media/verify.go b/backend/api/media/verify.go index d0c37b1..73be74a 100644 --- a/backend/api/media/verify.go +++ b/backend/api/media/verify.go @@ -6,7 +6,7 @@ import ( "net/http" "path/filepath" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" "fr.latosa-escrima/utils" ) diff --git a/backend/api/middlewares.go b/backend/api/middlewares.go index 1296c47..4c2423a 100644 --- a/backend/api/middlewares.go +++ b/backend/api/middlewares.go @@ -7,8 +7,8 @@ import ( "os" "strings" - core "fr.latosa-escrima/api/core" "fr.latosa-escrima/api/users" + core "fr.latosa-escrima/core" "fr.latosa-escrima/utils" "github.com/golang-jwt/jwt/v5" ) diff --git a/backend/api/permissions/delete.go b/backend/api/permissions/delete.go deleted file mode 100644 index 1b7b952..0000000 --- a/backend/api/permissions/delete.go +++ /dev/null @@ -1 +0,0 @@ -package permissions diff --git a/backend/api/permissions/new.go b/backend/api/permissions/new.go deleted file mode 100644 index 1b7b952..0000000 --- a/backend/api/permissions/new.go +++ /dev/null @@ -1 +0,0 @@ -package permissions diff --git a/backend/api/permissions/permission.go b/backend/api/permissions/permission.go index 1b7b952..9c145dd 100644 --- a/backend/api/permissions/permission.go +++ b/backend/api/permissions/permission.go @@ -1 +1,41 @@ package permissions + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandlePermission(w http.ResponseWriter, r *http.Request) { + id := r.PathValue("permission_id") + var permission models.Permission + count, err := core.DB.NewSelect(). + Model(&permission). + Where("id = ?", id). + Limit(1). + ScanAndCount(context.Background()) + + if count == 0 { + core.JSONSuccess{ + Status: core.Success, + Message: "Permission not found.", + }.Respond(w, http.StatusNotFound) + return + } + + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Permission found.", + Data: permission, + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/permissions/permissions.go b/backend/api/permissions/permissions.go index 1b7b952..47a4ffe 100644 --- a/backend/api/permissions/permissions.go +++ b/backend/api/permissions/permissions.go @@ -1 +1,36 @@ package permissions + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" +) + +func HandlePermissions(w http.ResponseWriter, r *http.Request) { + var permissions core.Permissions + count, err := core.DB.NewSelect(). + Model(&permissions). + ScanAndCount(context.Background()) + if count == 0 { + core.JSONSuccess{ + Status: core.Success, + Message: "No permissions found.", + }.Respond(w, http.StatusNotFound) + return + } + + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Permissions found.", + Data: permissions, + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/permissions/resource.go b/backend/api/permissions/resource.go new file mode 100644 index 0000000..a6407e8 --- /dev/null +++ b/backend/api/permissions/resource.go @@ -0,0 +1,38 @@ +package permissions + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" +) + +func HandlePermissionsResource(w http.ResponseWriter, r *http.Request) { + resource := r.PathValue("resource") + var permissions core.Permissions + count, err := core.DB.NewSelect(). + Model(&permissions). + Where("resource = ?", resource). + ScanAndCount(context.Background()) + if count == 0 { + core.JSONSuccess{ + Status: core.Success, + Message: "No permission with this given resource found.", + }.Respond(w, http.StatusNotFound) + return + } + + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Permissions found.", + Data: permissions, + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/permissions/update.go b/backend/api/permissions/update.go deleted file mode 100644 index 1b7b952..0000000 --- a/backend/api/permissions/update.go +++ /dev/null @@ -1 +0,0 @@ -package permissions diff --git a/backend/api/roles/add_permission.go b/backend/api/roles/add_permission.go index 3258a66..8fbb8ba 100644 --- a/backend/api/roles/add_permission.go +++ b/backend/api/roles/add_permission.go @@ -1 +1,62 @@ package roles + +import ( + "context" + "net/http" + "strconv" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" + "github.com/google/uuid" +) + +func HandleAddPermission(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + role_id := r.PathValue("role_uuid") + permission_id := r.PathValue("permission_id") + var permission models.Permission + count, err := core.DB.NewSelect().Model(&permission). + Where("id = ?", permission_id). + Limit(1).ScanAndCount(ctx) + if count == 0 { + core.JSONError{ + Status: core.Error, + Message: "Permission doesn't exist.", + }.Respond(w, http.StatusNotFound) + return + } + + var role models.Role + count, err = core.DB.NewSelect().Model(&role). + Where("id = ?", role_id). + Limit(1).ScanAndCount(ctx) + + if count == 0 { + core.JSONError{ + Status: core.Error, + Message: "Role doesn't exist.", + }.Respond(w, http.StatusNotFound) + return + } + + pid, err := strconv.Atoi(permission_id) + rid, err := uuid.Parse(role_id) + permissionRole := models.PermissionToRole{ + PermissionID: pid, + RoleID: rid, + } + _, err = core.DB.NewInsert().Model(&permissionRole).Ignore(). + Exec(ctx) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Permission added.", + }.Respond(w, http.StatusCreated) +} diff --git a/backend/api/roles/delete.go b/backend/api/roles/delete.go index 3258a66..4c30c3c 100644 --- a/backend/api/roles/delete.go +++ b/backend/api/roles/delete.go @@ -1 +1,30 @@ package roles + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleDelete(w http.ResponseWriter, r *http.Request) { + uuid := r.PathValue("role_uuid") + _, err := core.DB.NewDelete(). + Model((*models.Role)(nil)). + Where("id = ?", uuid). + Exec(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + // TODO: Check SQL Result + core.JSONSuccess{ + Status: core.Success, + Message: "Role deteled", + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/roles/new.go b/backend/api/roles/new.go index 3258a66..d6f4958 100644 --- a/backend/api/roles/new.go +++ b/backend/api/roles/new.go @@ -1 +1,37 @@ package roles + +import ( + "context" + "encoding/json" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleNew(w http.ResponseWriter, r *http.Request) { + var role models.Role + err := json.NewDecoder(r.Body).Decode(&role) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + _, err = core.DB.NewInsert().Model(&role).Exec(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Role inserted", + Data: role, + }.Respond(w, http.StatusCreated) +} diff --git a/backend/api/roles/permissions.go b/backend/api/roles/permissions.go index 3258a66..bb8358c 100644 --- a/backend/api/roles/permissions.go +++ b/backend/api/roles/permissions.go @@ -1 +1,35 @@ package roles + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleRolePermissions(w http.ResponseWriter, r *http.Request) { + uuid := r.PathValue("role_uuid") + + var role models.Role + err := core.DB.NewSelect(). + Model(&role). + Where("id = ?", uuid). + Relation("Permissions"). + Limit(1). + Scan(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Permissions.", + Data: role.Permissions, + }.Respond(w, http.StatusOK) + +} diff --git a/backend/api/roles/remove_permission.go b/backend/api/roles/remove_permission.go index 3258a66..448c50b 100644 --- a/backend/api/roles/remove_permission.go +++ b/backend/api/roles/remove_permission.go @@ -1 +1,32 @@ package roles + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleRemovePermission(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + role_id := r.PathValue("role_uuid") + permission_id := r.PathValue("permission_id") + + _, err := core.DB.NewDelete().Model((*models.PermissionToRole)(nil)). + Where("permission_id = ? AND role_id = ?", permission_id, role_id). + Exec(ctx) + + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Permission removed.", + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/roles/role.go b/backend/api/roles/role.go index 3258a66..6ad0d2a 100644 --- a/backend/api/roles/role.go +++ b/backend/api/roles/role.go @@ -1 +1,40 @@ package roles + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleRole(w http.ResponseWriter, r *http.Request) { + uuid := r.PathValue("role_uuid") + var role models.Role + count, err := core.DB.NewSelect(). + Model(&role). + Where("id = ?", uuid). + Relation("Permissions"). + Limit(1). + ScanAndCount(context.Background()) + if count == 0 { + core.JSONError{ + Status: core.Error, + Message: "The role requested was not found.", + }.Respond(w, http.StatusNotFound) + return + } + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Role found.", + Data: role, + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/roles/roles.go b/backend/api/roles/roles.go index 3258a66..2b160e8 100644 --- a/backend/api/roles/roles.go +++ b/backend/api/roles/roles.go @@ -1 +1,39 @@ package roles + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleRoles(w http.ResponseWriter, r *http.Request) { + var roles []models.Role + count, err := core.DB.NewSelect(). + Model(&roles). + Relation("Permissions"). + ScanAndCount(context.Background()) + + if count == 0 { + core.JSONError{ + Status: core.Error, + Message: "No role found.", + }.Respond(w, http.StatusNotFound) + return + } + + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Roles found.", + Data: roles, + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/roles/update.go b/backend/api/roles/update.go index 3258a66..b3074a3 100644 --- a/backend/api/roles/update.go +++ b/backend/api/roles/update.go @@ -1 +1,52 @@ package roles + +import ( + "context" + "encoding/json" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" + "github.com/google/uuid" +) + +func HandleUpdate(w http.ResponseWriter, r *http.Request) { + var role models.Role + err := json.NewDecoder(r.Body).Decode(&role) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusBadRequest) + return + } + role_uuid := r.PathValue("role_uuid") + role.ID, err = uuid.Parse(role_uuid) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusBadRequest) + return + } + + _, err = core.DB.NewUpdate(). + Model(&role). + OmitZero(). + WherePK(). + Exec(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: "Event not found.", + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Event updated", + Data: role, + }.Respond(w, http.StatusOK) + +} diff --git a/backend/api/shortcodes/delete.go b/backend/api/shortcodes/delete.go index 67ea9fb..91ff8f6 100644 --- a/backend/api/shortcodes/delete.go +++ b/backend/api/shortcodes/delete.go @@ -4,13 +4,14 @@ import ( "context" "net/http" - core "fr.latosa-escrima/api/core" + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleDelete(w http.ResponseWriter, r *http.Request) { code := r.PathValue("shortcode") _, err := core.DB.NewDelete(). - Model((*core.Shortcode)(nil)). + Model((*models.Shortcode)(nil)). Where("code = ?", code). Exec(context.Background()) if err != nil { diff --git a/backend/api/shortcodes/new.go b/backend/api/shortcodes/new.go index 2663bca..19db9e2 100644 --- a/backend/api/shortcodes/new.go +++ b/backend/api/shortcodes/new.go @@ -5,11 +5,12 @@ import ( "encoding/json" "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleNew(w http.ResponseWriter, r *http.Request) { - var shortcode core.Shortcode + var shortcode models.Shortcode err := json.NewDecoder(r.Body).Decode(&shortcode) if err != nil { core.JSONError{ diff --git a/backend/api/shortcodes/shortcode.go b/backend/api/shortcodes/shortcode.go index 83b2645..893a020 100644 --- a/backend/api/shortcodes/shortcode.go +++ b/backend/api/shortcodes/shortcode.go @@ -4,12 +4,13 @@ import ( "context" "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleShortcode(w http.ResponseWriter, r *http.Request) { code := r.PathValue("shortcode") - var shortcode core.Shortcode + var shortcode models.Shortcode err := core.DB.NewSelect(). Model(&shortcode). Where("code = ?", code). diff --git a/backend/api/shortcodes/shortcodes.go b/backend/api/shortcodes/shortcodes.go index e27d0c7..fb30284 100644 --- a/backend/api/shortcodes/shortcodes.go +++ b/backend/api/shortcodes/shortcodes.go @@ -4,11 +4,12 @@ import ( "context" "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleShortcodes(w http.ResponseWriter, r *http.Request) { - var shortcodes []core.Shortcode + var shortcodes []models.Shortcode err := core.DB.NewSelect().Model(&shortcodes).Scan(context.Background()) if err != nil { core.JSONError{ diff --git a/backend/api/shortcodes/update.go b/backend/api/shortcodes/update.go index 08dc035..2ce6c69 100644 --- a/backend/api/shortcodes/update.go +++ b/backend/api/shortcodes/update.go @@ -8,16 +8,17 @@ import ( "reflect" "strings" - core "fr.latosa-escrima/api/core" + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" "github.com/google/uuid" ) type UpdateShortcodeArgs struct { - ID *int64 `json:"id,omitempty"` - Code *string `json:"code,omitempty"` // The shortcode value - Type *core.ShortcodeType `bun:"shortcode_type" json:"type,omitempty"` - Value *string `json:"value,omitempty"` - MediaID *uuid.UUID `json:"media_id,omitempty"` // Nullable reference to another table's ID + ID *int64 `json:"id,omitempty"` + Code *string `json:"code,omitempty"` // The shortcode value + Type *models.ShortcodeType `bun:"shortcode_type" json:"type,omitempty"` + Value *string `json:"value,omitempty"` + MediaID *uuid.UUID `json:"media_id,omitempty"` // Nullable reference to another table's ID } func HandleUpdate(w http.ResponseWriter, r *http.Request) { @@ -31,7 +32,7 @@ func HandleUpdate(w http.ResponseWriter, r *http.Request) { return } - var shortcode core.Shortcode + var shortcode models.Shortcode updateQuery := core.DB.NewUpdate().Model(&shortcode) val := reflect.ValueOf(updateArgs) typ := reflect.TypeOf(updateArgs) diff --git a/backend/api/users/add_role.go b/backend/api/users/add_role.go new file mode 100644 index 0000000..df97e1c --- /dev/null +++ b/backend/api/users/add_role.go @@ -0,0 +1,61 @@ +package users + +import ( + "context" + "net/http" + + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" + "github.com/google/uuid" +) + +func HandleAddRole(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + user_id := r.PathValue("user_uuid") + role_id := r.PathValue("role_id") + var user models.User + count, err := core.DB.NewSelect().Model(&user). + Where("user_id = ?", user_id). + Limit(1).ScanAndCount(ctx) + if count == 0 { + core.JSONError{ + Status: core.Error, + Message: "User doesn't exist.", + }.Respond(w, http.StatusNotFound) + return + } + + var role models.Role + count, err = core.DB.NewSelect().Model(&role). + Where("id = ?", role_id). + Limit(1).ScanAndCount(ctx) + + if count == 0 { + core.JSONError{ + Status: core.Error, + Message: "Role doesn't exist.", + }.Respond(w, http.StatusNotFound) + return + } + + uid, err := uuid.Parse(user_id) + rid, err := uuid.Parse(role_id) + userRole := models.UserToRole{ + UserID: uid, + RoleID: rid, + } + _, err = core.DB.NewInsert().Model(&userRole).Ignore(). + Exec(ctx) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Role added.", + }.Respond(w, http.StatusCreated) +} diff --git a/backend/api/users/auth.go b/backend/api/users/auth.go index e6cfeb0..f9df8f7 100644 --- a/backend/api/users/auth.go +++ b/backend/api/users/auth.go @@ -8,7 +8,8 @@ import ( "net/http" "time" - core "fr.latosa-escrima/api/core" + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" "github.com/golang-jwt/jwt/v5" ) @@ -52,7 +53,7 @@ func HandleLogin(w http.ResponseWriter, r *http.Request) { return } - user, err := core.Verify(context.Background(), login.Email, login.Password) + user, err := models.Verify(core.DB, context.Background(), login.Email, login.Password) if user == nil { core.JSONError{ Status: core.Error, diff --git a/backend/api/users/delete.go b/backend/api/users/delete.go index 766a150..c83b007 100644 --- a/backend/api/users/delete.go +++ b/backend/api/users/delete.go @@ -4,13 +4,14 @@ import ( "context" "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleDelete(w http.ResponseWriter, r *http.Request) { uuid := r.PathValue("user_uuid") _, err := core.DB.NewDelete(). - Model((*core.User)(nil)). + Model((*models.User)(nil)). Where("user_id = ?", uuid). Exec(context.Background()) diff --git a/backend/api/users/me.go b/backend/api/users/me.go index dfc6c97..c864836 100644 --- a/backend/api/users/me.go +++ b/backend/api/users/me.go @@ -3,7 +3,7 @@ package users import ( "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" "github.com/golang-jwt/jwt/v5" ) diff --git a/backend/api/users/new.go b/backend/api/users/new.go index ed31f09..08c7368 100644 --- a/backend/api/users/new.go +++ b/backend/api/users/new.go @@ -6,11 +6,12 @@ import ( "log" "net/http" - core "fr.latosa-escrima/api/core" + core "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleNew(w http.ResponseWriter, r *http.Request) { - var user core.User + var user models.User err := json.NewDecoder(r.Body).Decode(&user) if err != nil { core.JSONError{ @@ -21,7 +22,7 @@ func HandleNew(w http.ResponseWriter, r *http.Request) { } log.Println("User : ", user) - res, err := user.Insert(context.Background()) + res, err := user.Insert(core.DB, context.Background()) log.Println(res) // if res == nil { // core.JSONError{ diff --git a/backend/api/users/remove_role.go b/backend/api/users/remove_role.go new file mode 100644 index 0000000..b8fee9d --- /dev/null +++ b/backend/api/users/remove_role.go @@ -0,0 +1,32 @@ +package users + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleRemoveRole(w http.ResponseWriter, r *http.Request) { + ctx := context.Background() + user_id := r.PathValue("user_uuid") + role_id := r.PathValue("role_id") + + _, err := core.DB.NewDelete().Model((*models.UserToRole)(nil)). + Where("user_id = ? AND role_id = ?", user_id, role_id). + Exec(ctx) + + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Role removed.", + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/users/roles.go b/backend/api/users/roles.go new file mode 100644 index 0000000..81d4f56 --- /dev/null +++ b/backend/api/users/roles.go @@ -0,0 +1,31 @@ +package users + +import ( + "context" + "net/http" + + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" +) + +func HandleRoles(w http.ResponseWriter, r *http.Request) { + user_id := r.PathValue("user_uuid") + var user models.User + err := core.DB.NewSelect().Model(&user).Where("user_id = ?", user_id). + Relation("Roles"). + Limit(1). + Scan(context.Background()) + if err != nil { + core.JSONError{ + Status: core.Error, + Message: err.Error(), + }.Respond(w, http.StatusInternalServerError) + return + } + + core.JSONSuccess{ + Status: core.Success, + Message: "Roles found.", + Data: user.Roles, + }.Respond(w, http.StatusOK) +} diff --git a/backend/api/users/update.go b/backend/api/users/update.go index 78ed85f..18950dc 100644 --- a/backend/api/users/update.go +++ b/backend/api/users/update.go @@ -9,16 +9,17 @@ import ( "strings" "time" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) type UpdateUserArgs struct { - FirstName *string `json:"firstname,omitempty"` - LastName *string `json:"lastname,omitempty"` - Email *string `json:"email,omitempty"` - Password *string `json:"password,omitempty"` - Phone *string `json:"phone,omitempty"` - Attributes *core.UserAttributes `json:"attributes"` + FirstName *string `json:"firstname,omitempty"` + LastName *string `json:"lastname,omitempty"` + Email *string `json:"email,omitempty"` + Password *string `json:"password,omitempty"` + Phone *string `json:"phone,omitempty"` + Attributes *models.UserAttributes `json:"attributes"` } func HandleUpdate(w http.ResponseWriter, r *http.Request) { @@ -32,7 +33,7 @@ func HandleUpdate(w http.ResponseWriter, r *http.Request) { return } - var user core.User + var user models.User updateQuery := core.DB.NewUpdate().Model(&user) val := reflect.ValueOf(updateArgs) diff --git a/backend/api/users/user.go b/backend/api/users/user.go index 4ae00cc..05c77d5 100644 --- a/backend/api/users/user.go +++ b/backend/api/users/user.go @@ -4,15 +4,17 @@ import ( "context" "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" ) func HandleUser(w http.ResponseWriter, r *http.Request) { uuid := r.PathValue("user_uuid") - var user core.User + var user models.User count, err := core.DB.NewSelect(). Model(&user). Where("user_id = ?", uuid). + Relation("Roles"). Limit(1). ScanAndCount(context.Background()) diff --git a/backend/api/users/users.go b/backend/api/users/users.go index e368f7c..65bf362 100644 --- a/backend/api/users/users.go +++ b/backend/api/users/users.go @@ -4,17 +4,19 @@ import ( "context" "net/http" - "fr.latosa-escrima/api/core" + "fr.latosa-escrima/core" + "fr.latosa-escrima/core/models" "fr.latosa-escrima/utils" ) func HandleUsers(w http.ResponseWriter, r *http.Request) { - var users []core.User + var users []models.User count, err := core.DB.NewSelect(). Model(&users). + Relation("Roles"). ScanAndCount(context.Background()) - users = utils.Map(users, func(user core.User) core.User { + users = utils.Map(users, func(user models.User) models.User { user.Password = "" return user }) diff --git a/backend/cmd/migrate/main.go b/backend/cmd/migrate/main.go index a3ee4e6..c8fda33 100644 --- a/backend/cmd/migrate/main.go +++ b/backend/cmd/migrate/main.go @@ -7,8 +7,8 @@ import ( "os" "strings" - "fr.latosa-escrima/api/core" "fr.latosa-escrima/cmd/migrate/migrations" + "fr.latosa-escrima/core" "github.com/joho/godotenv" "github.com/uptrace/bun/dialect/pgdialect" "github.com/uptrace/bun/driver/pgdriver" diff --git a/backend/api/core/csrf.go b/backend/core/csrf.go similarity index 100% rename from backend/api/core/csrf.go rename to backend/core/csrf.go diff --git a/backend/core/models/blogs.go b/backend/core/models/blogs.go new file mode 100644 index 0000000..9943256 --- /dev/null +++ b/backend/core/models/blogs.go @@ -0,0 +1,24 @@ +package models + +import ( + "time" + + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type Blog struct { + bun.BaseModel `bun:"table:blogs"` + + BlogID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"blogID"` + Slug string `bun:"slug,unique,notnull" json:"slug"` + Content string `bun:"content,notnull" json:"content"` + Label string `bun:"label" json:"label"` + AuthorID uuid.UUID `bun:"author_id,type:uuid,notnull" json:"authorID"` + Published time.Time `bun:"published,default:current_timestamp" json:"published"` + Summary string `bun:"summary" json:"summary"` + Image string `bun:"image" json:"image"` + Href string `bun:"href" json:"href"` + + Author User `bun:"rel:belongs-to,join:author_id=user_id" json:"author"` +} diff --git a/backend/core/models/events.go b/backend/core/models/events.go new file mode 100644 index 0000000..e222922 --- /dev/null +++ b/backend/core/models/events.go @@ -0,0 +1,25 @@ +package models + +import ( + "time" + + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type Status string + +const ( + Active Status = "Active" + Inactive Status = "Inactive" +) + +type Event struct { + bun.BaseModel `bun:"table:events"` + + EventID uuid.UUID `bun:"event_id,type:uuid,pk,default:gen_random_uuid()" json:"id"` + CreationDate time.Time `bun:"creation_date,notnull,default:current_timestamp" json:"creationDate"` + ScheduleStart time.Time `bun:"schedule_start,notnull" json:"start"` + ScheduleEnd time.Time `bun:"schedule_end,notnull" json:"end"` + Status Status `bun:"status,notnull,default:'Inactive'" json:"status"` +} diff --git a/backend/core/models/events_to_user.go b/backend/core/models/events_to_user.go new file mode 100644 index 0000000..f39635f --- /dev/null +++ b/backend/core/models/events_to_user.go @@ -0,0 +1,16 @@ +package models + +import ( + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type EventToUser struct { + bun.BaseModel `bun:"table:events_to_users"` + + EventID uuid.UUID `bun:"type:uuid,pk"` + UserID uuid.UUID `bun:"type:uuid,pk"` + + Event *Event `bun:"rel:belongs-to,join:event_id=event_id"` + User *User `bun:"rel:belongs-to,join:user_id=user_id"` +} diff --git a/backend/core/models/main.go b/backend/core/models/main.go new file mode 100644 index 0000000..58d4863 --- /dev/null +++ b/backend/core/models/main.go @@ -0,0 +1,8 @@ +package models + +type Group string + +const ( + LatosaGroup Group = "latosa" + WingTsunGroup Group = "wing-tsun" +) diff --git a/backend/core/models/media.go b/backend/core/models/media.go new file mode 100644 index 0000000..87fcba0 --- /dev/null +++ b/backend/core/models/media.go @@ -0,0 +1,18 @@ +package models + +import ( + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type Media struct { + bun.BaseModel `bun:"table:media"` + ID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"id"` + AuthorID uuid.UUID `bun:"author_id,type:uuid,notnull" json:"authorID"` + Author *User `bun:"rel:belongs-to,join:author_id=user_id" json:"author,omitempty"` + Type string `bun:"media_type" json:"type"` // Image, Video, GIF etc. Add support for PDFs? + Alt string `bun:"media_alt" json:"alt"` + Path string `bun:"media_path" json:"path"` + Size int64 `bun:"media_size" json:"size"` + URL string `bun:"-" json:"url"` +} diff --git a/backend/core/models/permissions.go b/backend/core/models/permissions.go new file mode 100644 index 0000000..5532edc --- /dev/null +++ b/backend/core/models/permissions.go @@ -0,0 +1,15 @@ +package models + +import "github.com/uptrace/bun" + +type PermissionConditions struct { + Groups *[]Group `json:"groups,omitempty"` +} + +type Permission struct { + bun.BaseModel `bun:"table:permissions"` + ID int `bun:"id,pk,autoincrement" json:"id"` + Resource string `bun:"resource,notnull,unique:permission" json:"resource"` + Action string `bun:"action,notnull,unique:permission" json:"action"` + Conditions PermissionConditions `bun:"conditions,type:jsonb" json:"conditions"` +} diff --git a/backend/core/models/permissions_to_roles.go b/backend/core/models/permissions_to_roles.go new file mode 100644 index 0000000..341f96b --- /dev/null +++ b/backend/core/models/permissions_to_roles.go @@ -0,0 +1,16 @@ +package models + +import ( + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type PermissionToRole struct { + bun.BaseModel `bun:"table:permissions_to_users"` + + PermissionID int `bun:"permission_id,pk"` + RoleID uuid.UUID `bun:"type:uuid,pk"` + + Permission *Permission `bun:"rel:belongs-to,join:permission_id=id"` + Role *Role `bun:"rel:belongs-to,join:role_id=id"` +} diff --git a/backend/core/models/roles.go b/backend/core/models/roles.go new file mode 100644 index 0000000..914d541 --- /dev/null +++ b/backend/core/models/roles.go @@ -0,0 +1,14 @@ +package models + +import ( + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type Role struct { + bun.BaseModel `bun:"table:roles"` + ID uuid.UUID `bun:"id,pk,type:uuid,default:gen_random_uuid()" json:"id"` + Name string `bun:"name,unique,notnull" json:"name"` + + Permissions []Permission `bun:"m2m:permissions_to_users,join:Role=Permission" json:"permissions,omitempty"` +} diff --git a/backend/core/models/shortcodes.go b/backend/core/models/shortcodes.go new file mode 100644 index 0000000..837fa6f --- /dev/null +++ b/backend/core/models/shortcodes.go @@ -0,0 +1,36 @@ +package models + +import ( + "errors" + + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type ShortcodeType string + +const ( + ShortcodeMedia ShortcodeType = "media" + ShortcodeValue ShortcodeType = "value" +) + +type Shortcode struct { + bun.BaseModel `bun:"table:shortcodes,alias:sc"` + + ID int64 `bun:"id,pk,autoincrement" json:"id"` // Primary key + Code string `bun:"code,notnull,unique" json:"code"` // The shortcode value + Type ShortcodeType `bun:"shortcode_type,notnull" json:"type"` + Value *string `bun:"value" json:"value,omitempty"` + MediaID *uuid.UUID `bun:"media_id,type:uuid" json:"media_id,omitempty"` // Nullable reference to another table's ID + Media *Media `bun:"rel:belongs-to,join:media_id=id" json:"media,omitempty"` // Relation to Media +} + +func (s *Shortcode) Validate() error { + if s.Value != nil && s.MediaID != nil { + return errors.New("both value and media_id cannot be set at the same time") + } + if s.Value == nil && s.MediaID == nil { + return errors.New("either value or media_id must be set") + } + return nil +} diff --git a/backend/core/models/users.go b/backend/core/models/users.go new file mode 100644 index 0000000..91fe42a --- /dev/null +++ b/backend/core/models/users.go @@ -0,0 +1,60 @@ +package models + +import ( + "context" + "database/sql" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type UserAttributes struct { + Groups []Group `json:"groups"` +} + +type User struct { + bun.BaseModel `bun:"table:users"` + + UserID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"userId"` + FirstName string `bun:"firstname,notnull" json:"firstname"` + LastName string `bun:"lastname,notnull" json:"lastname"` + Email string `bun:"email,unique,notnull" json:"email"` + Password string `bun:"password,notnull" json:"password,omitempty"` + Phone string `bun:"phone,notnull" json:"phone"` + CreatedAt time.Time `bun:"created_at,default:current_timestamp" json:"createdAt"` + UpdatedAt time.Time `bun:"updated_at,default:current_timestamp" json:"updatedAt"` + Events []Event `bun:"m2m:events_to_users,join:User=Event" json:"events,omitempty"` + Articles []*Blog `bun:"rel:has-many,join:user_id=blog_id" json:"articles,omitempty"` + Attributes UserAttributes `bun:"attributes,type:jsonb" json:"attributes"` + Roles []Role `bun:"m2m:users_to_roles,join:User=Role" json:"roles,omitempty"` +} + +func (u *User) Insert(db *bun.DB, ctx context.Context) (sql.Result, error) { + u.Password = fmt.Sprintf("crypt('%s', gen_salt('bf'))", u.Password) + return db.NewInsert(). + Model(u). + Value("password", u.Password). + Exec(ctx) +} + +func Verify(db *bun.DB, ctx context.Context, email, password string) (*User, error) { + var user User + count, err := db.NewSelect(). + Model(&user). + Where("email = ? AND password = crypt(?, password)", email, password). + Limit(1). + ScanAndCount(context.Background()) + if count == 0 { + return nil, fmt.Errorf("invalid email or password") + } + if err != nil { + if err == sql.ErrNoRows { + return nil, fmt.Errorf("invalid email or password") + } + return nil, err + } + + return &user, nil +} diff --git a/backend/core/models/users_to_roles.go b/backend/core/models/users_to_roles.go new file mode 100644 index 0000000..5bbc4b2 --- /dev/null +++ b/backend/core/models/users_to_roles.go @@ -0,0 +1,16 @@ +package models + +import ( + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type UserToRole struct { + bun.BaseModel `bun:"table:users_to_roles"` + + UserID uuid.UUID `bun:"user_id,type:uuid,pk"` + RoleID uuid.UUID `bun:"type:uuid,pk"` + + User *User `bun:"rel:belongs-to,join:user_id=user_id"` + Role *Role `bun:"rel:belongs-to,join:role_id=id"` +} diff --git a/backend/core/models/website_settings.go b/backend/core/models/website_settings.go new file mode 100644 index 0000000..63a628e --- /dev/null +++ b/backend/core/models/website_settings.go @@ -0,0 +1,13 @@ +package models + +import ( + "github.com/google/uuid" + "github.com/uptrace/bun" +) + +type WebsiteSettings struct { + bun.BaseModel `bun:"table:website_settings"` + + ID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"id"` + AutoAcceptDemand bool `bun:"auto_accept_demand,default:false" json:"autoAcceptDemand"` +} diff --git a/backend/api/core/paginated.go b/backend/core/paginated.go similarity index 100% rename from backend/api/core/paginated.go rename to backend/core/paginated.go diff --git a/backend/core/permissions.go b/backend/core/permissions.go new file mode 100644 index 0000000..e291fa0 --- /dev/null +++ b/backend/core/permissions.go @@ -0,0 +1,61 @@ +package core + +import ( + "context" + + "fr.latosa-escrima/core/models" + "github.com/uptrace/bun" +) + +type Permissions []models.Permission + +func GetAllPermissions() Permissions { + resources := []string{"users", "roles", "media", "events", "permissions", "shortcodes", "blogs"} + var perms Permissions + for _, resource := range resources { + perms = append(perms, Permissions{ + { + Resource: resource, + Action: "insert", + }, + { + Resource: resource, + Action: "update", + }, + { + Resource: resource, + Action: "delete", + }, + { + Resource: resource, + Action: "get", + }, + { + Resource: resource, + Action: "own:insert", + }, + { + Resource: resource, + Action: "own:update", + }, + { + Resource: resource, + Action: "own:delete", + }, + { + Resource: resource, + Action: "own:get", + }, + }...) + } + return perms +} + +func (perms Permissions) InsertAll(db *bun.DB, ctx context.Context) error { + _, err := db.NewInsert(). + Model(&perms). + Ignore(). + Exec(ctx) + + return err +} diff --git a/backend/api/core/router.go b/backend/core/router.go similarity index 100% rename from backend/api/core/router.go rename to backend/core/router.go diff --git a/backend/core/schemas.go b/backend/core/schemas.go new file mode 100644 index 0000000..346c72c --- /dev/null +++ b/backend/core/schemas.go @@ -0,0 +1,64 @@ +package core + +import ( + "context" + "database/sql" + "fmt" + + "github.com/uptrace/bun" + "github.com/uptrace/bun/dialect/pgdialect" + "github.com/uptrace/bun/driver/pgdriver" + + m "fr.latosa-escrima/core/models" +) + +var DB *bun.DB + +type DSN struct { + Hostname string + Port string + DBName string + User string + Password string +} + +func (dsn *DSN) ToString() string { + return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable", dsn.User, dsn.Password, dsn.Hostname, dsn.Port, dsn.DBName) +} + +func InitDatabase(dsn DSN) (*bun.DB, error) { + sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn.ToString()))) + db := bun.NewDB(sqldb, pgdialect.New()) + + ctx := context.Background() + + _, err := db.ExecContext(ctx, "CREATE EXTENSION IF NOT EXISTS pgcrypto;") + if err != nil { + return nil, err + } + db.RegisterModel((*m.EventToUser)(nil)) + db.RegisterModel((*m.PermissionToRole)(nil)) + db.RegisterModel((*m.UserToRole)(nil)) + _, err = db.NewCreateTable().Model((*m.User)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.Event)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.EventToUser)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.Blog)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.WebsiteSettings)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.Media)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.Shortcode)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.Role)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.Permission)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.PermissionToRole)(nil)).IfNotExists().Exec(ctx) + _, err = db.NewCreateTable().Model((*m.UserToRole)(nil)).IfNotExists().Exec(ctx) + if err != nil { + return nil, err + } + + perms := GetAllPermissions() + err = perms.InsertAll(db, ctx) + if err != nil { + return nil, err + } + + return db, nil +} diff --git a/backend/main.go b/backend/main.go index 37962d9..67e5153 100644 --- a/backend/main.go +++ b/backend/main.go @@ -13,11 +13,13 @@ import ( "fr.latosa-escrima/api" "fr.latosa-escrima/api/blogs" - "fr.latosa-escrima/api/core" "fr.latosa-escrima/api/events" "fr.latosa-escrima/api/media" + "fr.latosa-escrima/api/permissions" + "fr.latosa-escrima/api/roles" "fr.latosa-escrima/api/shortcodes" "fr.latosa-escrima/api/users" + "fr.latosa-escrima/core" "github.com/gorilla/csrf" ) @@ -93,24 +95,33 @@ func main() { "/users/{user_uuid}/update": { Handler: users.HandleUpdate, Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}}, + "/users/{user_uuid}/roles": { + Handler: users.HandleRoles, + Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}}, + "/users/{user_uuid}/roles/{role_id}/add": { + Handler: users.HandleAddRole, + Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}}, + "/users/{user_uuid}/roles/{role_id}/remove": { + Handler: users.HandleRemoveRole, + Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}}, "/events": { Handler: events.HandleEvents, Middlewares: []core.Middleware{api.Methods("GET")}}, "/events/new": { Handler: events.HandleNew, - Middlewares: []core.Middleware{api.Methods("POST")}}, + Middlewares: []core.Middleware{api.Methods("POST"), api.AuthJWT}}, "/events/{event_uuid}": { Handler: events.HandleEvent, Middlewares: []core.Middleware{api.Methods("GET")}}, "/events/{event_uuid}/delete": { Handler: events.HandleDelete, - Middlewares: []core.Middleware{api.Methods("DELETE")}}, + Middlewares: []core.Middleware{api.Methods("DELETE"), api.AuthJWT}}, "/events/{event_uuid}/update": { Handler: events.HandleUpdate, - Middlewares: []core.Middleware{api.Methods("PATCH")}}, + Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}}, "/blogs/new": { Handler: blogs.HandleNew, - Middlewares: []core.Middleware{api.Methods(("POST"))}}, + Middlewares: []core.Middleware{api.Methods(("POST")), api.AuthJWT}}, "/blogs/{uuid}": { Handler: blogs.HandleBlog, Middlewares: []core.Middleware{api.Methods("GET")}}, @@ -164,58 +175,50 @@ func main() { Handler: shortcodes.HandleUpdate, Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}, }, - // "/roles": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, - // }, - // "/roles/new": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("POST"), api.AuthJWT}, - // }, - // "/roles/{role_uuid}": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, - // }, - // "/roles/{role_uuid}/update": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}, - // }, - // "/roles/{role_uuid}/delete": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("DELETE"), api.AuthJWT}, - // }, - // "/permissions": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, - // }, - // "/permissions/new": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("POST"), api.AuthJWT}, - // }, - // "/permissions/{permission_id}": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}, - // }, - // "/permissions/{permission_id}/update": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}, - // }, - // "/permissions/{permission_id}/delete": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("DELETE"), api.AuthJWT}, - // }, - // "/roles/{role_uuid}/permissions/": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, - // }, - // "/roles/{role_uuid}/permissions/{permission_id}/add": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("POST"), api.AuthJWT}, - // }, - // "/roles/{role_uuid}/permissions/{permission_id}/remove": { - // Handler: nil, - // Middlewares: []core.Middleware{api.Methods("POST"), api.AuthJWT}, - // }, + "/roles/": { + Handler: roles.HandleRoles, + Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, + }, + "/roles/new": { + Handler: roles.HandleNew, + Middlewares: []core.Middleware{api.Methods("POST"), api.AuthJWT}, + }, + "/roles/{role_uuid}": { + Handler: roles.HandleRole, + Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, + }, + "/roles/{role_uuid}/update": { + Handler: roles.HandleUpdate, + Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}, + }, + "/roles/{role_uuid}/delete": { + Handler: roles.HandleDelete, + Middlewares: []core.Middleware{api.Methods("DELETE"), api.AuthJWT}, + }, + "/permissions": { + Handler: permissions.HandlePermissions, + Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, + }, + "/permissions/{permission_id}": { + Handler: permissions.HandlePermission, + Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, + }, + "/permissions/resources/{resource}": { + Handler: permissions.HandlePermissionsResource, + Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, + }, + "/roles/{role_uuid}/permissions/": { + Handler: roles.HandleRolePermissions, + Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}, + }, + "/roles/{role_uuid}/permissions/{permission_id}/add": { + Handler: roles.HandleAddPermission, + Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}, + }, + "/roles/{role_uuid}/permissions/{permission_id}/remove": { + Handler: roles.HandleRemovePermission, + Middlewares: []core.Middleware{api.Methods("PATCH"), api.AuthJWT}, + }, "/contact": { Handler: api.HandleContact, Middlewares: []core.Middleware{api.Methods("POST"), CSRFMiddleware}, diff --git a/frontend/app/(main)/planning/page.tsx b/frontend/app/(main)/planning/page.tsx index b7fbcb9..a4a5cc6 100644 --- a/frontend/app/(main)/planning/page.tsx +++ b/frontend/app/(main)/planning/page.tsx @@ -1,5 +1,5 @@ "use client"; -import Planning from "@/components/planning.tsx"; +import Planning from "@/components/planning"; import { useApi } from "@/hooks/use-api"; import { type CalendarEventExternal } from "@schedule-x/calendar"; import { Loader2 } from "lucide-react"; diff --git a/frontend/components/planning.tsx.tsx b/frontend/components/planning.tsx similarity index 99% rename from frontend/components/planning.tsx.tsx rename to frontend/components/planning.tsx index 675612c..155c781 100644 --- a/frontend/components/planning.tsx.tsx +++ b/frontend/components/planning.tsx @@ -90,6 +90,7 @@ const Planning: React.FC<{ setEventSelected(event); }, async onEventUpdate(event) { + console.log(event); await handleEventUpdate(event); }, },