Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

21 changed files with 18 additions and 955 deletions

2
.gitignore vendored
View file

@ -7,5 +7,3 @@ tls/*
!tls/.gitkeep
.DS_Store
storage/sqlite/*
.idea
cup

View file

@ -1,4 +1,4 @@
![goffee logo](https://git.smarteching.com/avatars/cd7cd5b690adc8e5ec6d6cdb117f1bf5a9e9353dae111bfbb394d2c3d4497537?size=200)
![gocondor logo](https://git.smarteching.com/avatars/cd7cd5b690adc8e5ec6d6cdb117f1bf5a9e9353dae111bfbb394d2c3d4497537?size=200)
# Cup of Goffee
## What is Goffee?

View file

@ -1,20 +0,0 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package config
import "git.smarteching.com/goffee/core"
// Retrieve the main config for the Queue
func GetQueueConfig() core.QueueConfig {
//#####################################
//# Main configuration for Queue #####
//#####################################
return core.QueueConfig{
// For enabling and disabling the queue system
// set to true to enable it, set to false to disable
EnableQueue: false,
}
}

View file

@ -19,29 +19,6 @@ import (
func AdminUsersList(c *core.Context) *core.Response {
// initiate authority
auth := new(utils.Authority)
var session = new(utils.SessionUser)
// true if session is active
hassession := session.Init(c)
if !hassession {
type emptytemplate struct{}
emptyData := emptytemplate{}
return c.Response.Template("nopermission.html", emptyData)
}
session_uid := session.GetUserID()
// check if user has role admin
is_admin, _ := auth.CheckUserRole(c, session_uid, "admin")
if !is_admin {
type emptytemplate struct{}
emptyData := emptytemplate{}
return c.Response.Template("nopermission.html", emptyData)
}
// continue if has session and is admin
var users []models.User
db := c.GetGorm()
db.Find(&users)
@ -69,10 +46,6 @@ func AdminUsersList(c *core.Context) *core.Response {
Value: "Email",
ValueType: "string",
},
{
Value: "Roles",
ValueType: "string",
},
{
Value: "Created",
},
@ -84,26 +57,13 @@ func AdminUsersList(c *core.Context) *core.Response {
},
}
var listroles string
rows := make([][]components.ContentTableTD, len(users))
for i, u := range users {
roles, _ := auth.GetUserRoles(c, u.ID)
listroles = ""
for _, role := range roles {
if listroles != "" {
listroles += ", "
}
listroles += role.Name
}
row := []components.ContentTableTD{
{Value: strconv.Itoa(int(u.ID))},
{Value: u.Name},
{Value: u.Fullname},
{Value: u.Email},
{Value: listroles},
{Value: utils.FormatUnix(u.Created)},
{Value: utils.FormatUnix(u.Updated)},
{Value: components.ContentHref{
@ -133,44 +93,10 @@ func AdminUsersList(c *core.Context) *core.Response {
func AdminUsersAdd(c *core.Context) *core.Response {
// initiate authority
auth := new(utils.Authority)
var session = new(utils.SessionUser)
// true if session is active
hassession := session.Init(c)
if !hassession {
type emptytemplate struct{}
emptyData := emptytemplate{}
return c.Response.Template("nopermission.html", emptyData)
}
session_uid := session.GetUserID()
// check if user has role admin
is_admin, _ := auth.CheckUserRole(c, session_uid, "admin")
if !is_admin {
type emptytemplate struct{}
emptyData := emptytemplate{}
return c.Response.Template("nopermission.html", emptyData)
}
// check if is submit
submit := c.GetRequestParam("submit").(string)
errormessages := make([]string, 0)
var listroles []components.FormCheckboxItem
systemroles, _ := auth.GetAllRoles(c)
for _, systemrole := range systemroles {
var userrole components.FormCheckboxItem
userrole.Label = systemrole.Name
userrole.Name = "roles"
userrole.Value = systemrole.Slug
if systemrole.Slug == "authenticated" {
userrole.IsChecked = true
}
listroles = append(listroles, userrole)
}
errormessages := make([]string, 0)
name := ""
fullname := ""
@ -183,7 +109,6 @@ func AdminUsersAdd(c *core.Context) *core.Response {
fullname = c.GetRequestParam("fullname").(string)
email = c.GetRequestParam("email").(string)
password = c.GetRequestParam("password").(string)
roles := c.GetRequesForm("roles").([]string)
// check if email exists
var user models.User
@ -236,11 +161,6 @@ func AdminUsersAdd(c *core.Context) *core.Response {
errormessages = append(errormessages, res.Error.Error())
} else {
// assign roles
for _, role := range roles {
auth.AssignRoleToUser(c, user.ID, role)
}
// fire user registered event
err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_REGISTERED, Payload: map[string]interface{}{
"user": user,
@ -260,7 +180,6 @@ func AdminUsersAdd(c *core.Context) *core.Response {
FieldName components.FormInput
FieldFullname components.FormInput
FieldEmail components.FormInput
FieldRoles components.FormCheckbox
FieldPassword components.FormInput
ErrorMessages []string
SubmitButton components.FormButton
@ -289,10 +208,6 @@ func AdminUsersAdd(c *core.Context) *core.Response {
//Autocomplete: true,
IsRequired: true,
},
FieldRoles: components.FormCheckbox{
Label: "Roles",
AllCheckbox: listroles,
},
FieldPassword: components.FormInput{
ID: "password",
Label: "Password",
@ -321,29 +236,6 @@ func AdminUsersEdit(c *core.Context) *core.Response {
user_id := c.GetPathParam("id")
errormessages := make([]string, 0)
// initiate authority
auth := new(utils.Authority)
var listroles []components.FormCheckboxItem
systemroles, _ := auth.GetAllRoles(c)
user_id_uint, _ := strconv.ParseUint(user_id.(string), 10, 32)
userroles, _ := auth.GetUserRoles(c, uint(user_id_uint))
for _, systemrole := range systemroles {
var userrole components.FormCheckboxItem
userrole.Label = systemrole.Name
userrole.Name = "roles"
userrole.Value = systemrole.Slug
for _, ur := range userroles {
if ur.Slug == systemrole.Slug {
userrole.IsChecked = true
break
}
}
listroles = append(listroles, userrole)
}
var origin_user models.User
@ -367,7 +259,6 @@ func AdminUsersEdit(c *core.Context) *core.Response {
fullname = c.GetRequestParam("fullname").(string)
email = c.GetRequestParam("email").(string)
password = c.GetRequestParam("password").(string)
roles := c.GetRequesForm("roles").([]string)
key := c.GetRequestParam("key")
// check if email exists
@ -425,14 +316,6 @@ func AdminUsersEdit(c *core.Context) *core.Response {
c.GetLogger().Error("Admin user: error updating")
errormessages = append(errormessages, fmt.Sprintf("Error updating user %s:", user_id_string))
} else {
// delete roles
auth.RevokeAllUserRole(c, origin_user.ID)
// assign roles
for _, role := range roles {
auth.AssignRoleToUser(c, origin_user.ID, role)
}
return c.Response.Redirect("/admin/users")
}
}
@ -442,7 +325,6 @@ func AdminUsersEdit(c *core.Context) *core.Response {
FieldName components.FormInput
FieldFullname components.FormInput
FieldEmail components.FormInput
FieldRoles components.FormCheckbox
FieldPassword components.FormInput
FieldKey components.FormInput
ErrorMessages []string
@ -473,10 +355,6 @@ func AdminUsersEdit(c *core.Context) *core.Response {
//Autocomplete: true,
IsRequired: true,
},
FieldRoles: components.FormCheckbox{
Label: "Roles",
AllCheckbox: listroles,
},
FieldPassword: components.FormInput{
ID: "password",
Label: "Password",
@ -583,9 +461,6 @@ func AdminUsersDelConfirm(c *core.Context) *core.Response {
// check if is the seed user
seed := "1"
if user_id != seed {
// initiate authority
auth := new(utils.Authority)
// Delete the user
// fire user delete event
err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_DELETED, Payload: map[string]interface{}{
@ -594,7 +469,6 @@ func AdminUsersDelConfirm(c *core.Context) *core.Response {
if err != nil {
c.GetLogger().Error(err.Error())
}
auth.RevokeAllUserRole(c, origin_user.ID)
result_db.Unscoped().Delete(&origin_user)
}
}

View file

@ -1,52 +0,0 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package controllers
import (
"encoding/json"
"log"
"time"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/cup/workers"
"github.com/hibiken/asynq"
)
// Make samples queues
func Queuesample(c *core.Context) *core.Response {
// Get client queue asynq
client := c.GetQueueClient()
// Create a task with typename and payload.
payload, err := json.Marshal(workers.EmailTaskPayload{UserID: 42})
if err != nil {
log.Fatal(err)
}
t1 := asynq.NewTask(workers.TypeWelcomeEmail, payload)
t2 := asynq.NewTask(workers.TypeReminderEmail, payload)
// Process the task immediately.
info, err := client.Enqueue(t1)
if err != nil {
log.Fatal(err)
}
log.Printf(" [*] Successfully enqueued task: %+v", info)
// Process 2 task 1 min later.
for i := 1; i < 3; i++ {
info, err = client.Enqueue(t2, asynq.ProcessIn(1*time.Minute))
if err != nil {
log.Fatal(err)
}
log.Printf(" [*] Successfully enqueued task: %+v", info)
}
message := "{\"message\": \"Task queued\"}"
return c.Response.Json(message)
}

32
go.mod
View file

@ -10,9 +10,8 @@ replace (
go 1.23.1
require (
git.smarteching.com/goffee/core v1.9.2
git.smarteching.com/goffee/core v1.8.4
github.com/google/uuid v1.6.0
github.com/hibiken/asynq v0.25.1
github.com/joho/godotenv v1.5.1
github.com/julienschmidt/httprouter v1.3.0
gorm.io/gorm v1.25.12
@ -25,40 +24,35 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-chi/chi/v5 v5.2.1 // indirect
github.com/go-chi/chi/v5 v5.1.0 // indirect
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
github.com/go-sql-driver/mysql v1.9.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/harranali/mailing v1.2.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.2 // indirect
github.com/jackc/pgx/v5 v5.7.1 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailgun/errors v0.4.0 // indirect
github.com/mailgun/mailgun-go/v4 v4.23.0 // indirect
github.com/mailgun/mailgun-go/v4 v4.17.3 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/redis/go-redis/v9 v9.7.1 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/redis/go-redis/v9 v9.7.0 // indirect
github.com/sendgrid/rest v2.6.9+incompatible // indirect
github.com/sendgrid/sendgrid-go v3.16.0+incompatible // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/cast v1.7.1 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/image v0.25.0 // indirect
golang.org/x/net v0.37.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/image v0.21.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.19.0 // indirect
gorm.io/driver/mysql v1.5.7 // indirect
gorm.io/driver/postgres v1.5.11 // indirect
gorm.io/driver/sqlite v1.5.7 // indirect
gorm.io/driver/postgres v1.5.9 // indirect
gorm.io/driver/sqlite v1.5.6 // indirect
)

67
go.sum
View file

@ -2,12 +2,6 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.smarteching.com/goffee/core v1.8.4 h1:XB+vpe7e8muiDChRVDaJ1TG7H+/FBxDQcMfWp4zloPs=
git.smarteching.com/goffee/core v1.8.4/go.mod h1:JxXDvTQU2shKYY6c9aS3s6sFh7mEDzgmjzdc85HhBV8=
git.smarteching.com/goffee/core v1.9.0 h1:aND9YkkDXEqsgEoDA04D9pmum2D3/nNljO1ojE1nSM4=
git.smarteching.com/goffee/core v1.9.0/go.mod h1:L9a+kL1RVHRHzp+DzCS1apwVLyZAvGE6B94UlyIMhIg=
git.smarteching.com/goffee/core v1.9.1 h1:/WkIHkLJuNGf72tUJx7s/jt4gIA0EMkR1wemr+gnKg0=
git.smarteching.com/goffee/core v1.9.1/go.mod h1:L9a+kL1RVHRHzp+DzCS1apwVLyZAvGE6B94UlyIMhIg=
git.smarteching.com/goffee/core v1.9.2 h1:SpLAhsTxssPItgkBYLN3UxMH5s+q8qVtbvmRom3WKh8=
git.smarteching.com/goffee/core v1.9.2/go.mod h1:L9a+kL1RVHRHzp+DzCS1apwVLyZAvGE6B94UlyIMhIg=
git.smarteching.com/zeni/go-chart/v2 v2.1.4 h1:pF06+F6eqJLIG8uMiTVPR5TygPGMjM/FHMzTxmu5V/Q=
git.smarteching.com/zeni/go-chart/v2 v2.1.4/go.mod h1:b3ueW9h3pGGXyhkormZAvilHaG4+mQti+bMNPdQBeOQ=
github.com/SparkPost/gosparkpost v0.2.0 h1:yzhHQT7cE+rqzd5tANNC74j+2x3lrPznqPJrxC1yR8s=
@ -37,15 +31,11 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQD
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE=
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo=
github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
@ -57,16 +47,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/harranali/mailing v1.2.0 h1:ihIyJwB8hyRVcdk+v465wk1PHMrSrgJqo/kMd+gZClY=
github.com/harranali/mailing v1.2.0/go.mod h1:4a5N3yG98pZKluMpmcYlTtll7bisvOfGQEMIng3VQk4=
github.com/hibiken/asynq v0.25.1 h1:phj028N0nm15n8O2ims+IvJ2gz4k2auvermngh9JhTw=
github.com/hibiken/asynq v0.25.1/go.mod h1:pazWNOLBu0FEynQRBvHA26qdIKRSmfdIfUm4HdsLmXg=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
@ -87,10 +73,6 @@ github.com/mailgun/errors v0.4.0 h1:6LFBvod6VIW83CMIOT9sYNp28TCX0NejFPP4dSX++i8=
github.com/mailgun/errors v0.4.0/go.mod h1:xGBaaKdEdQT0/FhwvoXv4oBaqqmVZz9P1XEnvD/onc0=
github.com/mailgun/mailgun-go/v4 v4.17.3 h1:WoO48/VeXgAVSzjgzyeLvF08AoPzWU2EBz79INN8rEA=
github.com/mailgun/mailgun-go/v4 v4.17.3/go.mod h1:0ood70bQR/SffQ9NxIsAY06H+HG0hrvMVApfUp9TihI=
github.com/mailgun/mailgun-go/v4 v4.22.1 h1:yMvPeo9m5XPVVg3XF0aPiJiiGt/n/cayBa4eQBDYqtc=
github.com/mailgun/mailgun-go/v4 v4.22.1/go.mod h1:JA2xbLTkEWrX2TO+RUiJQALZus6WLLoXym2i8a8F5sE=
github.com/mailgun/mailgun-go/v4 v4.23.0 h1:jPEMJzzin2s7lvehcfv/0UkyBu18GvcURPr2+xtZRbk=
github.com/mailgun/mailgun-go/v4 v4.23.0/go.mod h1:imTtizoFtpfZqPqGP8vltVBB6q9yWcv6llBhfFeElZU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
@ -107,10 +89,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/redis/go-redis/v9 v9.7.1 h1:4LhKRCIduqXqtvCUlaq9c8bdHOkICjDMrr1+Zb3osAc=
github.com/redis/go-redis/v9 v9.7.1/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
@ -118,72 +96,31 @@ github.com/sendgrid/sendgrid-go v3.16.0+incompatible h1:i8eE6IMkiCy7vusSdacHHSBU
github.com/sendgrid/sendgrid-go v3.16.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA=
golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
@ -192,12 +129,8 @@ gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=

View file

@ -62,9 +62,6 @@ func main() {
registerGlobalHooks()
registerRoutes()
registerEvents()
if config.GetQueueConfig().EnableQueue == true {
registerQueues()
}
if config.GetGormConfig().EnableGorm == true {
RunAutoMigrations()
}

View file

@ -1,16 +0,0 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package models
type Permission struct {
BaseModel
Name string
Slug string
}
// TableName sets the table name
func (Permission) TableName() string {
return "permissions"
}

View file

@ -1,16 +0,0 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package models
type RolePermission struct {
BaseModel
RoleID uint // Role id
PermissionID uint // Permission id
}
// TableName sets the table name
func (RolePermission) TableName() string {
return "role_permissions"
}

View file

@ -1,16 +0,0 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package models
type Role struct {
BaseModel
Name string // The name of the role
Slug string // String based unique identifier of the role, (use hyphen seperated role name '-', instead of space)
}
// TableName sets the table name
func (Role) TableName() string {
return "roles"
}

View file

@ -1,16 +0,0 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package models
type UserRole struct {
BaseModel
UserID uint // The user id
RoleID uint // The role id
}
// TableName sets the table name
func (UserRole) TableName() string {
return "user_roles"
}

View file

@ -1,30 +0,0 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package main
import (
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/cup/workers"
)
// Register queues
func registerQueues() {
var queque = new(core.Queuemux)
queque.QueueInit()
//########################################
//# quques registration #####
//########################################
// register your queues here ...
queque.AddWork(workers.TypeWelcomeEmail, workers.HandleWelcomeEmailTask)
queque.AddWork(workers.TypeReminderEmail, workers.HandleReminderEmailTask)
//########################################
// Start queue server, DO NOT TOUCH
//########################################
go queque.RunQueueserver()
}

View file

@ -26,7 +26,6 @@ func registerRoutes() {
controller.Get("/themecontent", controllers.Themecontent)
controller.Get("/themepanel", controllers.Themedemo)
controller.Get("/themeelements", controllers.ThemeElements)
controller.Get("/queuesample", controllers.Queuesample)
// Uncomment the lines below to enable authentication
controller.Post("/signup", controllers.Signup)

View file

@ -6,12 +6,8 @@
package main
import (
"errors"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/cup/models"
"git.smarteching.com/goffee/cup/utils"
"gorm.io/gorm"
)
func RunAutoMigrations() {
@ -21,18 +17,5 @@ func RunAutoMigrations() {
//##############################
// Add auto migrations for your models here...
db.AutoMigrate(&models.UserRole{})
db.AutoMigrate(&models.Role{})
db.AutoMigrate(&models.RolePermission{})
db.AutoMigrate(&models.Permission{})
// End your auto migrations
// Create seed data data, DO NOT TOUCH
if err := db.AutoMigrate(&models.User{}); err == nil && db.Migrator().HasTable(&models.User{}) {
if err := db.First(&models.User{}).Error; errors.Is(err, gorm.ErrRecordNotFound) {
utils.CreateSeedData()
}
}
db.AutoMigrate(&models.User{})
}

View file

@ -15,8 +15,7 @@
<form method="POST" id="add_user" action="/admin/users/add">
{{template "form_input" .FieldName}}
{{template "form_input" .FieldFullname}}
{{template "form_input" .FieldEmail}}
{{template "form_checkbox" .FieldRoles}}
{{template "form_input" .FieldEmail}}
{{template "form_input" .FieldPassword}}
<hr>
{{template "form_button" .SubmitButton}}

View file

@ -15,8 +15,7 @@
<form method="POST" id="add_user" action="/admin/users/edit/{{.FieldKey.Value}}">
{{template "form_input" .FieldName}}
{{template "form_input" .FieldFullname}}
{{template "form_input" .FieldEmail}}
{{template "form_checkbox" .FieldRoles}}
{{template "form_input" .FieldEmail}}
{{template "form_input" .FieldPassword}}
{{template "form_input" .FieldKey}}
{{template "form_button" .SubmitButton}}

View file

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
{{template "page_head" "No permission"}}
<body>
<div class="container">
<div class="row">
You do not have permission to visit this page.
</div>
</div>
{{template "page_footer"}}
</body>
</html>

View file

@ -1,436 +0,0 @@
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file.
package utils
import (
"errors"
"fmt"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/cup/models"
"gorm.io/gorm"
)
type Authority struct{}
var auth *Authority
var (
ErrPermissionInUse = errors.New("cannot delete assigned permission")
ErrPermissionNotFound = errors.New("permission not found")
ErrRoleInUse = errors.New("cannot delete assigned role")
ErrRoleNotFound = errors.New("role not found")
)
// Add a new role to the database
func (a *Authority) CreateRole(c *core.Context, r models.Role) error {
roleSlug := r.Slug
var dbRole models.Role
res := c.GetGorm().Where("slug = ?", roleSlug).First(&dbRole)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
// create
createRes := c.GetGorm().Create(&r)
if createRes.Error != nil {
return createRes.Error
}
return nil
}
return res.Error
}
return errors.New(fmt.Sprintf("role '%v' already exists", roleSlug))
}
// Add a new permission to the database
func (a *Authority) CreatePermission(c *core.Context, p models.Permission) error {
permSlug := p.Slug
var dbPerm models.Permission
res := c.GetGorm().Where("slug = ?", permSlug).First(&dbPerm)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
// create
createRes := c.GetGorm().Create(&p)
if createRes.Error != nil {
return createRes.Error
}
return nil
}
return res.Error
}
return errors.New(fmt.Sprintf("permission '%v' already exists", permSlug))
}
// Assigns a group of permissions to a given role
func (a *Authority) AssignPermissionsToRole(c *core.Context, roleSlug string, permSlugs []string) error {
var role models.Role
rRes := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
if rRes.Error != nil {
if errors.Is(rRes.Error, gorm.ErrRecordNotFound) {
return ErrRoleNotFound
}
return rRes.Error
}
var perms []models.Permission
for _, permSlug := range permSlugs {
var perm models.Permission
pRes := c.GetGorm().Where("slug = ?", permSlug).First(&perm)
if pRes.Error != nil {
if errors.Is(pRes.Error, gorm.ErrRecordNotFound) {
return ErrPermissionNotFound
}
return pRes.Error
}
perms = append(perms, perm)
}
tx := c.GetGorm().Begin()
for _, perm := range perms {
var rolePerm models.RolePermission
res := c.GetGorm().Where("role_id = ?", role.ID).Where("permission_id =?", perm.ID).First(&rolePerm)
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
cRes := tx.Create(&models.RolePermission{RoleID: role.ID, PermissionID: perm.ID})
if cRes.Error != nil {
tx.Rollback()
return cRes.Error
}
}
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
tx.Rollback()
return res.Error
}
if rolePerm != (models.RolePermission{}) {
tx.Rollback()
return errors.New(fmt.Sprintf("permission '%v' is aleady assigned to the role '%v'", perm.Name, role.Name))
}
rolePerm = models.RolePermission{}
}
return tx.Commit().Error
}
// Assigns a role to a given user
func (a *Authority) AssignRoleToUser(c *core.Context, userID uint, roleSlug string) error {
var role models.Role
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return ErrRoleNotFound
}
return res.Error
}
var userRole models.UserRole
res = c.GetGorm().Where("user_id = ?", userID).Where("role_id = ?", role.ID).First(&userRole)
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
c.GetGorm().Create(&models.UserRole{UserID: userID, RoleID: role.ID})
return nil
}
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
return res.Error
}
return errors.New(fmt.Sprintf("this role '%v' is aleady assigned to the user", roleSlug))
}
// Checks if a role is assigned to a user
func (a *Authority) CheckUserRole(c *core.Context, userID uint, roleSlug string) (bool, error) {
// find the role
var role models.Role
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return false, ErrRoleNotFound
}
return false, res.Error
}
// check if the role is a assigned
var userRole models.UserRole
res = c.GetGorm().Where("user_id = ?", userID).Where("role_id = ?", role.ID).First(&userRole)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return false, nil
}
return false, res.Error
}
return true, nil
}
// Checks if a permission is assigned to a user
func (a *Authority) CheckUserPermission(c *core.Context, userID uint, permSlug string) (bool, error) {
// the user role
var userRoles []models.UserRole
res := c.GetGorm().Where("user_id = ?", userID).Find(&userRoles)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return false, nil
}
return false, res.Error
}
//prepare an array of role ids
var roleIDs []interface{}
for _, r := range userRoles {
roleIDs = append(roleIDs, r.RoleID)
}
// find the permission
var perm models.Permission
res = c.GetGorm().Where("slug = ?", permSlug).First(&perm)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return false, ErrPermissionNotFound
}
return false, res.Error
}
// find the role permission
var rolePermission models.RolePermission
res = c.GetGorm().Where("role_id IN (?)", roleIDs).Where("permission_id = ?", perm.ID).First(&rolePermission)
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
return false, nil
}
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
return false, res.Error
}
return true, nil
}
// Checks if a permission is assigned to a role
func (a *Authority) CheckRolePermission(c *core.Context, roleSlug string, permSlug string) (bool, error) {
// find the role
var role models.Role
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return false, ErrRoleNotFound
}
return false, res.Error
}
// find the permission
var perm models.Permission
res = c.GetGorm().Where("slug = ?", permSlug).First(&perm)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return false, ErrPermissionNotFound
}
return false, res.Error
}
// find the rolePermission
var rolePermission models.RolePermission
res = c.GetGorm().Where("role_id = ?", role.ID).Where("permission_id = ?", perm.ID).First(&rolePermission)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return false, nil
}
return false, res.Error
}
return true, nil
}
// Revokes a roles's permission
func (a *Authority) RevokeRolePermission(c *core.Context, roleSlug string, permSlug string) error {
// find the role
var role models.Role
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return ErrRoleNotFound
}
return res.Error
}
// find the permission
var perm models.Permission
res = c.GetGorm().Where("slug = ?", permSlug).First(&perm)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return ErrPermissionNotFound
}
return res.Error
}
// revoke the permission
rRes := c.GetGorm().Where("role_id = ?", role.ID).Where("permission_id = ?", perm.ID).Delete(models.RolePermission{})
if rRes.Error != nil {
return rRes.Error
}
return nil
}
// Revokes a user's role
func (a *Authority) RevokeUserRole(c *core.Context, userID uint, roleSlug string) error {
// find the role
var role models.Role
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
if res.Error != nil {
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
return ErrRoleNotFound
}
return res.Error
}
// revoke the role
rRes := c.GetGorm().Where("user_id = ?", userID).Where("role_id = ?", role.ID).Delete(models.UserRole{})
if rRes.Error != nil {
return rRes.Error
}
return nil
}
// Revokes all user's role
func (a *Authority) RevokeAllUserRole(c *core.Context, userID uint) error {
// revoke the role
rRes := c.GetGorm().Where("user_id = ?", userID).Delete(models.UserRole{})
if rRes.Error != nil {
return rRes.Error
}
return nil
}
// Returns all stored roles
func (a *Authority) GetAllRoles(c *core.Context) ([]models.Role, error) {
var roles []models.Role
res := c.GetGorm().Find(&roles)
if res.Error != nil {
return nil, res.Error
}
return roles, nil
}
// Returns all user assigned roles
func (a *Authority) GetUserRoles(c *core.Context, userID uint) ([]models.Role, error) {
var userRoles []models.UserRole
res := c.GetGorm().Where("user_id = ?", userID).Find(&userRoles)
if res.Error != nil {
return nil, res.Error
}
var roleIDs []interface{}
for _, r := range userRoles {
roleIDs = append(roleIDs, r.RoleID)
}
var roles []models.Role
res = c.GetGorm().Where("id IN (?)", roleIDs).Find(&roles)
if res.Error != nil {
return nil, res.Error
}
return roles, nil
}
// Returns all role assigned permissions
func (a *Authority) GetRolePermissions(c *core.Context, roleSlug string) ([]models.Permission, error) {
var role models.Role
res := c.GetGorm().Where("slug = ?", roleSlug).Find(&role)
if res.Error != nil {
return nil, res.Error
}
var rolePerms []models.RolePermission
res = c.GetGorm().Where("role_id = ?", role.ID).Find(&rolePerms)
if res.Error != nil {
return nil, res.Error
}
var permIDs []interface{}
for _, rolePerm := range rolePerms {
permIDs = append(permIDs, rolePerm.PermissionID)
}
var perms []models.Permission
res = c.GetGorm().Where("id IN (?)", permIDs).Find(&perms)
if res.Error != nil {
return nil, res.Error
}
return perms, nil
}
// Returns all stored permissions
func (a *Authority) GetAllPermissions(c *core.Context) ([]models.Permission, error) {
var perms []models.Permission
res := c.GetGorm().Find(&perms)
if res.Error != nil {
return nil, res.Error
}
return perms, nil
}
// Deletes a given role even if it's has assigned permissions
func (a *Authority) DeleteRole(c *core.Context, roleSlug string) error {
// find the role
var role models.Role
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
if res.Error != nil {
return res.Error
}
// check if the role is assigned to a user
var ca int64
res = c.GetGorm().Model(models.UserRole{}).Where("role_id = ?", role.ID).Count(&ca)
if res.Error != nil {
return res.Error
}
if ca != 0 {
// role is assigned
return ErrRoleInUse
}
tx := c.GetGorm().Begin()
// revoke the assignment of permissions before deleting the role
dRes := tx.Where("role_id = ?", role.ID).Delete(models.RolePermission{})
if dRes.Error != nil {
tx.Rollback()
return dRes.Error
}
// delete the role
dRes = c.GetGorm().Where("slug = ?", roleSlug).Delete(models.Role{})
if dRes.Error != nil {
tx.Rollback()
return dRes.Error
}
return tx.Commit().Error
}
// Deletes a given permission
func (a *Authority) DeletePermission(c *core.Context, permSlug string) error {
// find the permission
var perm models.Permission
res := c.GetGorm().Where("slug = ?", permSlug).First(&perm)
if res.Error != nil {
return res.Error
}
// check if the permission is assigned to a role
var rolePermission models.RolePermission
res = c.GetGorm().Where("permission_id = ?", perm.ID).First(&rolePermission)
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
return res.Error
}
if res.Error == nil {
return ErrPermissionInUse
}
// delete the permission
dRes := c.GetGorm().Where("slug = ?", permSlug).Delete(models.Permission{})
if dRes.Error != nil {
return dRes.Error
}
return nil
}

View file

@ -8,69 +8,9 @@ package utils
import (
"crypto/md5"
"fmt"
"log"
"time"
"git.smarteching.com/goffee/core"
"git.smarteching.com/goffee/cup/models"
)
func CreateSeedData() {
db := core.ResolveGorm()
var hashing = new(core.Hashing)
var role models.Role
// seed user
password := "goffee"
name := "admin"
fullname := "Goffee administrator"
email := "change@me.com"
passwordHashed, _ := hashing.HashPassword(password)
user := models.User{
Name: name,
Fullname: fullname,
Email: email,
Password: passwordHashed,
}
result := db.Create(&user)
if result.Error != nil {
log.Fatal("Can't create seed user in database")
}
// seed roles
roles := []models.Role{
{Name: "Administrator", Slug: "admin"},
{Name: "Authenticated", Slug: "authenticated"},
}
for _, role := range roles {
result = db.Create(&role)
if result.Error != nil {
log.Fatal("Can't create seed role in database")
}
}
// seed permission
permission := models.Permission{Name: "Users administration", Slug: "admin-users"}
result = db.Create(&permission)
if result.Error != nil {
log.Fatal("Can't create seed permission in database")
}
result = db.Where("slug = ?", "admin").First(&role)
if result.Error != nil {
log.Fatal("Can't find user admin in database")
}
result = db.Create(&models.RolePermission{RoleID: role.ID, PermissionID: permission.ID})
if result.Error != nil {
log.Fatal("Can't register permission role in database")
}
result = db.Create(&models.UserRole{UserID: user.ID, RoleID: role.ID})
if result.Error != nil {
log.Fatal("Can't assign role administrator to user in database")
}
}
// generate a hashed string to be used as key for caching auth jwt token
func CreateAuthTokenHashedCacheKey(userID uint, userAgent string) string {
cacheKey := fmt.Sprintf("userid:_%v_useragent:_%v_jwt_token", userID, userAgent)

View file

@ -1,39 +0,0 @@
package workers
import (
"context"
"encoding/json"
"log"
"github.com/hibiken/asynq"
)
// A list of task types.
const (
TypeWelcomeEmail = "email:welcome"
TypeReminderEmail = "email:reminder"
)
// Task payload for any email related tasks.
type EmailTaskPayload struct {
// ID for the email recipient.
UserID int
}
func HandleWelcomeEmailTask(ctx context.Context, t *asynq.Task) error {
var p EmailTaskPayload
if err := json.Unmarshal(t.Payload(), &p); err != nil {
return err
}
log.Printf(" [*] Send Welcome Email to User %d", p.UserID)
return nil
}
func HandleReminderEmailTask(ctx context.Context, t *asynq.Task) error {
var p EmailTaskPayload
if err := json.Unmarshal(t.Payload(), &p); err != nil {
return err
}
log.Printf(" [*] Send Reminder Email to User %d", p.UserID)
return nil
}