Replace Huma CLI, pass context down, logging updates

This commit is contained in:
Justin Hammond 2022-08-18 11:27:09 +08:00
parent 4ff78432bf
commit 7e7b90e537
17 changed files with 391 additions and 212 deletions

1
.gitignore vendored
View file

@ -22,3 +22,4 @@ frontend/node_modules/
frontend/dist/ frontend/dist/
dist/ dist/
test.db-journal test.db-journal
logs/

1
go.mod
View file

@ -29,6 +29,7 @@ require (
github.com/spf13/viper v1.12.0 github.com/spf13/viper v1.12.0
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
gopkg.in/natefinch/lumberjack.v2 v2.0.0
) )
require ( require (

4
go.sum
View file

@ -58,6 +58,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
@ -202,7 +203,6 @@ github.com/go-pkgz/auth v1.19.0 h1:TTfbQvlqsuEhRpYAiY/uF5UGP+HWMc5CN2RAhN06ik0=
github.com/go-pkgz/auth v1.19.0/go.mod h1:1pu95rx7tFfeIFq0pjQPwCeL6qGYH4+UxHJudhK0T7M= github.com/go-pkgz/auth v1.19.0/go.mod h1:1pu95rx7tFfeIFq0pjQPwCeL6qGYH4+UxHJudhK0T7M=
github.com/go-pkgz/repeater v1.1.3 h1:q6+JQF14ESSy28Dd7F+wRelY4F+41HJ0LEy/szNnMiE= github.com/go-pkgz/repeater v1.1.3 h1:q6+JQF14ESSy28Dd7F+wRelY4F+41HJ0LEy/szNnMiE=
github.com/go-pkgz/repeater v1.1.3/go.mod h1:hVTavuO5x3Gxnu8zW7d6sQBfAneKV8X2FjU48kGfpKw= github.com/go-pkgz/repeater v1.1.3/go.mod h1:hVTavuO5x3Gxnu8zW7d6sQBfAneKV8X2FjU48kGfpKw=
github.com/go-pkgz/rest v1.12.2/go.mod h1:KUWAqbDteYGS/CiXftomQsKjtEOifXsJ36Ka0skYbmk=
github.com/go-pkgz/rest v1.15.6 h1:8RgOuY/c00CD0el8KdmscOCgDH+ML0ZsK2qa1Rcxal4= github.com/go-pkgz/rest v1.15.6 h1:8RgOuY/c00CD0el8KdmscOCgDH+ML0ZsK2qa1Rcxal4=
github.com/go-pkgz/rest v1.15.6/go.mod h1:KUWAqbDteYGS/CiXftomQsKjtEOifXsJ36Ka0skYbmk= github.com/go-pkgz/rest v1.15.6/go.mod h1:KUWAqbDteYGS/CiXftomQsKjtEOifXsJ36Ka0skYbmk=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
@ -1231,6 +1231,8 @@ gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:a
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View file

@ -2,6 +2,7 @@ package app
import ( import (
"errors" "errors"
"context"
"github.com/Fishwaldo/mouthpiece/internal/db" "github.com/Fishwaldo/mouthpiece/internal/db"
"github.com/Fishwaldo/mouthpiece/internal/errors" "github.com/Fishwaldo/mouthpiece/internal/errors"
@ -39,32 +40,32 @@ func InitializeApps() {
db.Db.AutoMigrate(&ApplicationFilters{}) db.Db.AutoMigrate(&ApplicationFilters{})
} }
func GetApps() []App { func GetApps(ctx context.Context) []App {
var apps []App var apps []App
db.Db.Preload("AssociatedUsers").Preload("AssociatedUsers.TransportConfigs").Preload("Filters").Find(&apps) db.Db.WithContext(ctx).Preload("AssociatedUsers").Preload("AssociatedUsers.TransportConfigs").Preload("Filters").Find(&apps)
return apps return apps
} }
func FindApp(app_name string) (app *App, err error) { func FindApp(ctx context.Context, app_name string) (app *App, err error) {
tx := db.Db.Debug().Preload("AssociatedUsers").Preload("AssociatedUsers.TransportConfigs").Preload("Filters").First(&app, "app_name = ?", app_name) tx := db.Db.WithContext(ctx).Preload("AssociatedUsers").Preload("AssociatedUsers.TransportConfigs").Preload("Filters").First(&app, "app_name = ?", app_name)
Log.V(1).Info("Finding App", "App", app_name, "Result", tx, "app", app) Log.V(1).Info("Finding App", "App", app_name, "Result", tx, "app", app)
return app, tx.Error return app, tx.Error
} }
func AppExists(app_name string) bool { func AppExists(ctx context.Context, app_name string) bool {
var app App var app App
tx := db.Db.First(&app, "app_name = ?", app_name) tx := db.Db.WithContext(ctx).First(&app, "app_name = ?", app_name)
return tx.Error == nil return tx.Error == nil
} }
func CreateApp(app AppDetails) (newapp *App, err error) { func CreateApp(ctx context.Context, app AppDetails) (newapp *App, err error) {
newapp, err = FindApp(app.AppName) newapp, err = FindApp(ctx, app.AppName)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) { if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
Log.Info("Creating New App", "App", app) Log.Info("Creating New App", "App", app)
var dbApp App var dbApp App
copier.Copy(&dbApp, &app) copier.Copy(&dbApp, &app)
adminuser, _ := user.GetUser("admin@example.com") adminuser, _ := user.GetUser(ctx, "admin@example.com")
dbApp.AssociatedUsers = append(dbApp.AssociatedUsers, adminuser) dbApp.AssociatedUsers = append(dbApp.AssociatedUsers, adminuser)
normaluser, _ := user.GetUser("user@example.com") normaluser, _ := user.GetUser(ctx, "user@example.com")
dbApp.AssociatedUsers = append(dbApp.AssociatedUsers, normaluser) dbApp.AssociatedUsers = append(dbApp.AssociatedUsers, normaluser)
if filter.FindFilter("CopyShortMessage") != nil { if filter.FindFilter("CopyShortMessage") != nil {
dbApp.Filters = append(dbApp.Filters, ApplicationFilters{Name: "CopyShortMessage"}) dbApp.Filters = append(dbApp.Filters, ApplicationFilters{Name: "CopyShortMessage"})
@ -72,17 +73,17 @@ func CreateApp(app AppDetails) (newapp *App, err error) {
if filter.FindFilter("FindSeverity") != nil { if filter.FindFilter("FindSeverity") != nil {
dbApp.Filters = append(dbApp.Filters, ApplicationFilters{Name: "FindSeverity"}) dbApp.Filters = append(dbApp.Filters, ApplicationFilters{Name: "FindSeverity"})
} }
result := db.Db.Create(&dbApp) result := db.Db.WithContext(ctx).Create(&dbApp)
if result.Error != nil { if result.Error != nil {
return newapp, result.Error return newapp, result.Error
} }
return FindApp(app.AppName) return FindApp(ctx, app.AppName)
} }
Log.Error(err, "App Already Exists", "App", newapp) Log.Error(err, "App Already Exists", "App", newapp)
return newapp, mperror.ErrAppExists return newapp, mperror.ErrAppExists
} }
func (app App) ProcessMessage(msg *msg.Message) error { func (app App) ProcessMessage(ctx context.Context, msg *msg.Message) error {
Log.V(1).Info("App Processing Message", "App", app.AppName, "MessageID", msg.ID) Log.V(1).Info("App Processing Message", "App", app.AppName, "MessageID", msg.ID)
/* populate Message Fields with App Data */ /* populate Message Fields with App Data */
msg.Body.Fields["app_description"] = app.Description msg.Body.Fields["app_description"] = app.Description
@ -92,7 +93,7 @@ func (app App) ProcessMessage(msg *msg.Message) error {
flt := filter.FindFilter(appfilter.Name) flt := filter.FindFilter(appfilter.Name)
if flt != nil { if flt != nil {
Log.V(1).Info("App Processing Message with Filter", "Filter", appfilter) Log.V(1).Info("App Processing Message with Filter", "Filter", appfilter)
ok, _ := flt.ProcessMessage(msg) ok, _ := flt.ProcessMessage(ctx, msg)
if !ok { if !ok {
Log.Info("App Filter Blocked Message", "App", app.AppName, "Filter", appfilter, "Message", msg) Log.Info("App Filter Blocked Message", "App", app.AppName, "Filter", appfilter, "Message", msg)
return nil return nil
@ -102,7 +103,7 @@ func (app App) ProcessMessage(msg *msg.Message) error {
} }
} }
for _, user := range app.AssociatedUsers { for _, user := range app.AssociatedUsers {
user.ProcessMessage(*msg) user.ProcessMessage(ctx, *msg)
} }
return nil return nil
} }

42
internal/app/restapi.go Normal file
View file

@ -0,0 +1,42 @@
package app
import (
"net/http"
"github.com/Fishwaldo/mouthpiece/internal/auth"
"github.com/danielgtaylor/huma"
"github.com/danielgtaylor/huma/responses"
)
func InitializeAppRestAPI(res *huma.Resource) error {
auth.AuthService.AddResourceURL("/v1/apps/", "apigroup:apps")
appapi := res.SubResource("/apps/")
appapi.Get("get-apps", "Get A List of Applications",
responses.OK().ContentType("application/json"),
responses.OK().Headers("Set-Cookie"),
responses.OK().Model([]App{}),
).Run(func(ctx huma.Context) {
ctx.WriteModel(http.StatusOK, GetApps(ctx))
})
appapi.Put("create-app", "Create a Application",
responses.OK().ContentType("application/json"),
responses.OK().Headers("Set-Cookie"),
responses.OK().Model(&App{}),
responses.NotAcceptable().ContentType("application/json"),
responses.NotAcceptable().Headers("Set-Cookie"),
).Run(func(ctx huma.Context, input struct {
Body AppDetails
}) {
if app, err := CreateApp(ctx, input.Body); err != nil {
ctx.WriteError(http.StatusNotAcceptable, "Database Error", err)
} else {
ctx.WriteModel(http.StatusOK, app)
}
})
return nil
}

View file

@ -16,6 +16,7 @@ import (
telegramauth "github.com/Fishwaldo/mouthpiece/internal/auth/telegram" telegramauth "github.com/Fishwaldo/mouthpiece/internal/auth/telegram"
"github.com/Fishwaldo/mouthpiece/internal/db" "github.com/Fishwaldo/mouthpiece/internal/db"
. "github.com/Fishwaldo/mouthpiece/internal/log" . "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/go-logr/logr"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -41,14 +42,14 @@ type Auth struct {
} }
var AuthService *Auth var AuthService *Auth
var alog *authLogger
var llog logr.Logger
type AuthLogger struct { type authLogger struct {
} }
var AL *AuthLogger func (AL authLogger) Logf(format string, args ...interface{}) {
llog.V(1).Info("Authentication", "message", fmt.Sprintf(format, args...))
func (AL AuthLogger) Logf(format string, args ...interface{}) {
Log.WithName("Auth").Info("Authentication", "message", fmt.Sprintf(format, args...))
} }
type AuthConfig struct { type AuthConfig struct {
@ -100,7 +101,9 @@ func customGitHubProvider() (cred pkauth.Client, ch provider.CustomHandlerOpt) {
} }
func InitAuth(Config AuthConfig) { func InitAuth(Config AuthConfig) {
AL = &AuthLogger{} llog = Log.WithName("Auth")
alog = &authLogger{}
AuthService = &Auth{} AuthService = &Auth{}
var avatarcachedir string var avatarcachedir string
@ -123,14 +126,14 @@ func InitAuth(Config AuthConfig) {
AvatarResizeLimit: 200, // resizes avatars to 200x200 AvatarResizeLimit: 200, // resizes avatars to 200x200
ClaimsUpd: token.ClaimsUpdFunc(Config.MapClaimsToUser), ClaimsUpd: token.ClaimsUpdFunc(Config.MapClaimsToUser),
Validator: Config.Validator, Validator: Config.Validator,
Logger: AL, // optional logger for auth library Logger: alog, // optional logger for auth library
UseGravatar: true, // for verified provider use gravatar service UseGravatar: true, // for verified provider use gravatar service
} }
// create auth service // create auth service
AuthService.Service = pkauth.NewService(options) AuthService.Service = pkauth.NewService(options)
if viper.GetBool("auth.dev.enabled") { if viper.GetBool("auth.dev.enabled") {
Log.Info("Auth Dev Mode Enabled!") llog.Info("Auth Dev Mode Enabled!")
AuthService.Service.AddProvider("dev", "", "") AuthService.Service.AddProvider("dev", "", "")
// run dev/test oauth2 server on :8084 // run dev/test oauth2 server on :8084
go func() { go func() {
@ -139,7 +142,7 @@ func InitAuth(Config AuthConfig) {
return "admin@example.com" return "admin@example.com"
} }
if err != nil { if err != nil {
Log.Error(err, "[PANIC] failed to start dev oauth2 server") llog.Error(err, "[PANIC] failed to start dev oauth2 server")
} }
devAuthServer.Run(context.Background()) devAuthServer.Run(context.Background())
@ -147,24 +150,24 @@ func InitAuth(Config AuthConfig) {
} }
if viper.GetBool("auth.github.enabled") { if viper.GetBool("auth.github.enabled") {
if !viper.IsSet("auth.github.client_id") { if !viper.IsSet("auth.github.client_id") {
Log.Error(nil, "Github auth is enabled but client_id is not set") llog.Error(nil, "Github auth is enabled but client_id is not set")
} else { } else {
if !viper.IsSet("auth.github.client_secret") { if !viper.IsSet("auth.github.client_secret") {
Log.Error(nil, "Github auth is enabled but client_secret is not set") llog.Error(nil, "Github auth is enabled but client_secret is not set")
} else { } else {
Log.Info("Auth Github Enabled!") llog.Info("Auth Github Enabled!")
gcred, gch := customGitHubProvider() gcred, gch := customGitHubProvider()
AuthService.Service.AddCustomProvider("github", gcred, gch) AuthService.Service.AddCustomProvider("github", gcred, gch)
} }
} }
} }
if viper.GetBool("auth.microsoft.enabled") { if viper.GetBool("auth.microsoft.enabled") {
Log.Info("Auth Microsoft Enabled!") llog.Info("Auth Microsoft Enabled!")
AuthService.Service.AddProvider("microsoft", os.Getenv("AEXMPL_MS_APIKEY"), os.Getenv("AEXMPL_MS_APISEC")) AuthService.Service.AddProvider("microsoft", os.Getenv("AEXMPL_MS_APIKEY"), os.Getenv("AEXMPL_MS_APISEC"))
} }
/* direct loging (username/password) is always handled */ /* direct loging (username/password) is always handled */
dbprovider := dbauth.DirectHandler{ dbprovider := dbauth.DirectHandler{
L: AL, L: alog,
ProviderName: "direct", ProviderName: "direct",
Issuer: options.Issuer, Issuer: options.Issuer,
TokenService: AuthService.Service.TokenService(), TokenService: AuthService.Service.TokenService(),
@ -174,7 +177,7 @@ func InitAuth(Config AuthConfig) {
AuthService.Service.AddCustomHandler(dbprovider) AuthService.Service.AddCustomHandler(dbprovider)
if viper.GetBool("auth.email.enabled") { if viper.GetBool("auth.email.enabled") {
Log.Info("Auth Email Enabled!") llog.Info("Auth Email Enabled!")
AuthService.Service.AddVerifProvider("email", AuthService.Service.AddVerifProvider("email",
"To confirm use {{.Token}}\nor follow http://arm64-1.dmz.dynam.ac:8888/auth/email/login?token={{.Token}}", "To confirm use {{.Token}}\nor follow http://arm64-1.dmz.dynam.ac:8888/auth/email/login?token={{.Token}}",
provider.SenderFunc(func(address string, text string) error { // sender just prints token provider.SenderFunc(func(address string, text string) error { // sender just prints token
@ -186,14 +189,14 @@ func InitAuth(Config AuthConfig) {
if viper.GetBool("auth.telegram.enabled") { if viper.GetBool("auth.telegram.enabled") {
if viper.IsSet("auth.telegram.token") { if viper.IsSet("auth.telegram.token") {
Log.Info("Auth Telegram Enabled!") llog.Info("Auth Telegram Enabled!")
// add telegram provider // add telegram provider
telegram := telegramauth.TelegramHandler{ telegram := telegramauth.TelegramHandler{
ProviderName: "telegram", ProviderName: "telegram",
ErrorMsg: "❌ Invalid auth request. Please try clicking link again.", ErrorMsg: "❌ Invalid auth request. Please try clicking link again.",
SuccessMsg: "✅ You have successfully authenticated!", SuccessMsg: "✅ You have successfully authenticated!",
Telegram: telegramauth.NewTelegramAPI(viper.GetString("auth.telegram.token"), http.DefaultClient), Telegram: telegramauth.NewTelegramAPI(viper.GetString("auth.telegram.token"), http.DefaultClient),
L: AL, L: alog,
TokenService: AuthService.Service.TokenService(), TokenService: AuthService.Service.TokenService(),
AvatarSaver: AuthService.Service.AvatarProxy(), AvatarSaver: AuthService.Service.AvatarProxy(),
} }
@ -201,49 +204,49 @@ func InitAuth(Config AuthConfig) {
go func() { go func() {
err := telegram.Run(context.Background()) err := telegram.Run(context.Background())
if err != nil { if err != nil {
Log.Error(err, "[PANIC] failed to start telegram") llog.Error(err, "[PANIC] failed to start telegram")
} }
}() }()
AuthService.Service.AddCustomHandler(&telegram) AuthService.Service.AddCustomHandler(&telegram)
} else { } else {
Log.Error(nil, "Telegram auth is enabled but token is not set") llog.Error(nil, "Telegram auth is enabled but token is not set")
} }
} }
InitCasbin(Config) InitCasbin(Config)
Log.Info("Auth service started") llog.Info("Auth service started")
} }
func InitCasbin(config AuthConfig) { func InitCasbin(config AuthConfig) {
cdb, err := gormadapter.NewAdapterByDB(db.Db) cdb, err := gormadapter.NewAdapterByDB(db.Db)
if err != nil { if err != nil {
Log.Error(err, "Failed to Setup Casbin Auth Adapter") llog.Error(err, "Failed to Setup Casbin Auth Adapter")
} }
casbinmodel, err := fs.ReadFile(config.ConfigDir, "config/auth_model.conf") casbinmodel, err := fs.ReadFile(config.ConfigDir, "config/auth_model.conf")
if err != nil { if err != nil {
Log.Error(err, "Failed to read casbin model") llog.Error(err, "Failed to read casbin model")
} }
m, err := model.NewModelFromString(string(casbinmodel)) m, err := model.NewModelFromString(string(casbinmodel))
if err != nil { if err != nil {
Log.Error(err, "Failed to parse casbin model") llog.Error(err, "Failed to parse casbin model")
} }
AuthService.AuthEnforcer, err = casbin.NewEnforcer(m, cdb) AuthService.AuthEnforcer, err = casbin.NewEnforcer(m, cdb)
if err != nil { if err != nil {
Log.Error(err, "Failed to setup Casbin") llog.Error(err, "Failed to setup Casbin")
} }
AuthService.AuthEnforcer.EnableLog(viper.GetBool("auth.debug")) AuthService.AuthEnforcer.EnableLog(viper.GetBool("auth.debug"))
AuthService.AuthEnforcer.EnableAutoSave(true) AuthService.AuthEnforcer.EnableAutoSave(true)
AuthService.AuthEnforcer.SetRoleManager(defaultrolemanager.NewRoleManager(10)) AuthService.AuthEnforcer.SetRoleManager(defaultrolemanager.NewRoleManager(10))
//if err := AuthService.AuthEnforcer.LoadModel(); err != nil { //if err := AuthService.AuthEnforcer.LoadModel(); err != nil {
// Log.Error(err, "Failed to load Casbin model") // llog.Error(err, "Failed to load Casbin model")
//} //}
if err := AuthService.AuthEnforcer.LoadPolicy(); err != nil { if err := AuthService.AuthEnforcer.LoadPolicy(); err != nil {
Log.Error(err, "Failed to Load Casbin Policy") llog.Error(err, "Failed to Load Casbin Policy")
} }
if !AuthService.AuthEnforcer.AddNamedMatchingFunc("g2", "KeyMatch3", util.KeyMatch3) { if !AuthService.AuthEnforcer.AddNamedMatchingFunc("g2", "KeyMatch3", util.KeyMatch3) {
Log.Error(nil, "Failed to add g2 matching function") llog.Error(nil, "Failed to add g2 matching function")
} }
AuthService.AuthEnforcer.AddPolicy("role:admin", "apigroup:apps", "PUT") AuthService.AuthEnforcer.AddPolicy("role:admin", "apigroup:apps", "PUT")
AuthService.AuthEnforcer.AddPolicy("role:user", "apigroup:apps", "GET") AuthService.AuthEnforcer.AddPolicy("role:user", "apigroup:apps", "GET")
@ -259,21 +262,20 @@ func InitCasbin(config AuthConfig) {
// AuthService.AuthEnforcer.AddRoleForUser("admin", "role:admin") // AuthService.AuthEnforcer.AddRoleForUser("admin", "role:admin")
// AuthService.AuthEnforcer.AddRoleForUser("dev_user", "role:admin") // AuthService.AuthEnforcer.AddRoleForUser("dev_user", "role:admin")
p, _ := AuthService.AuthEnforcer.GetImplicitPermissionsForUser("admin@example.com")
fmt.Printf("Admin Permissions: %+v\n", p)
AuthService.AuthEnforcer.SavePolicy() AuthService.AuthEnforcer.SavePolicy()
rm := AuthService.AuthEnforcer.GetPolicy() rm := AuthService.AuthEnforcer.GetPolicy()
Log.Info("Casbin Policy", "policy", rm) llog.Info("Casbin Policy", "policy", rm)
Log.Info("Casbin User Roles", "Roles", AuthService.AuthEnforcer.GetGroupingPolicy()) llog.Info("Casbin User Roles", "Roles", AuthService.AuthEnforcer.GetGroupingPolicy())
Log.Info("Casbin API Groups", "API Groups", AuthService.AuthEnforcer.GetNamedGroupingPolicy("g2")) llog.Info("Casbin API Groups", "API Groups", AuthService.AuthEnforcer.GetNamedGroupingPolicy("g2"))
} }
func (a *Auth) AddResourceURL(url string, group string) bool { func (a *Auth) AddResourceURL(url string, group string) bool {
ok, err := a.AuthEnforcer.AddNamedGroupingPolicy("g2", url, group) ok, err := a.AuthEnforcer.AddNamedGroupingPolicy("g2", url, group)
if err != nil { if err != nil {
Log.Error(err, "Failed to add g2 policy", "url", url, "group", group) llog.Error(err, "Failed to add g2 policy", "url", url, "group", group)
} }
return ok return ok
} }

View file

@ -4,15 +4,16 @@ import (
"io/fs" "io/fs"
"path/filepath" "path/filepath"
"strings" "strings"
"context"
//"io/ioutil" //"io/ioutil"
"embed" "embed"
"fmt" "fmt"
"github.com/Fishwaldo/mouthpiece/internal/db" "github.com/Fishwaldo/mouthpiece/internal/db"
. "github.com/Fishwaldo/mouthpiece/internal/log" "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/Fishwaldo/mouthpiece/internal/message" "github.com/Fishwaldo/mouthpiece/internal/message"
"github.com/go-logr/logr"
"github.com/skx/evalfilter/v2" "github.com/skx/evalfilter/v2"
"github.com/skx/evalfilter/v2/object" "github.com/skx/evalfilter/v2/object"
"gorm.io/gorm" "gorm.io/gorm"
@ -23,6 +24,9 @@ var ScriptFiles embed.FS
type FilterType int type FilterType int
var llog logr.Logger
const ( const (
AppFilter = iota AppFilter = iota
UserFilter UserFilter
@ -71,10 +75,10 @@ func loadScriptFiles(files []string, scripttype FilterType) {
var flt *Filter var flt *Filter
tx := db.Db.Where("name = ? and type = ?", trimFileExtension(filepath.Base(script)), scripttype).First(&flt) tx := db.Db.Where("name = ? and type = ?", trimFileExtension(filepath.Base(script)), scripttype).First(&flt)
if tx.RowsAffected == 0 { if tx.RowsAffected == 0 {
Log.Info("Reading Filter Script from Filesystem", "type", scripttype, "filter", trimFileExtension(filepath.Base(script))) llog.V(1).Info("Reading Filter Script from Filesystem", "type", scripttype, "filter", trimFileExtension(filepath.Base(script)))
content, err := fs.ReadFile(ScriptFiles, script) content, err := fs.ReadFile(ScriptFiles, script)
if err != nil { if err != nil {
Log.Error(err, "Failed to read Filter Script File", "filename", script) llog.Error(err, "Failed to read Filter Script File", "filename", script)
continue continue
} }
// //
@ -86,19 +90,20 @@ func loadScriptFiles(files []string, scripttype FilterType) {
Type: scripttype, Type: scripttype,
} }
} else { } else {
Log.Info("Loading Filter Script from Databse", "type", scripttype, "filter", flt.Name) llog.V(1).Info("Loading Filter Script from Databse", "type", scripttype, "filter", flt.Name)
} }
if err := flt.SetupEvalFilter(); err == nil { if err := flt.SetupEvalFilter(); err == nil {
Log.Info("Loaded Filter Script ", "type", scripttype, "filter", flt.Name) llog.Info("Loaded Filter Script ", "type", scripttype, "filter", flt.Name)
Filters = append(Filters, flt) Filters = append(Filters, flt)
db.Db.Save(flt) db.Db.Save(flt)
} else { } else {
Log.Error(err, "Failed to load Filter Script", "type", scripttype, "filter", flt.Name) llog.Error(err, "Failed to load Filter Script", "type", scripttype, "filter", flt.Name)
} }
} }
} }
func InitFilter() { func InitFilter() {
llog = log.Log.WithName("filter")
db.Db.AutoMigrate(&Filter{}) db.Db.AutoMigrate(&Filter{})
Filters = make([]*Filter, 0) Filters = make([]*Filter, 0)
scripts := filterFiles("scripts/apps", ".scp") scripts := filterFiles("scripts/apps", ".scp")
@ -143,13 +148,13 @@ func (ev *Filter) fnPrintf(args []object.Object) object.Object {
// Call the helper // Call the helper
out := fmt.Sprintf(fs, fmtArgs...) out := fmt.Sprintf(fs, fmtArgs...)
Log.Info("Filter Script Output", "filter", ev.Name, "output", out) llog.Info("Filter Script Output", "filter", ev.Name, "output", out)
return &object.Void{} return &object.Void{}
} }
func (ev *Filter) fnPrint(args []object.Object) object.Object { func (ev *Filter) fnPrint(args []object.Object) object.Object {
for _, e := range args { for _, e := range args {
Log.Info("Filter Script Output", "filter", ev.Name, "Output", e.Inspect()) llog.Info("Filter Script Output", "filter", ev.Name, "Output", e.Inspect())
} }
return &object.Void{} return &object.Void{}
} }
@ -170,7 +175,7 @@ func (ev *Filter) fnSetField(args []object.Object) object.Object {
arg := args[0].ToInterface() arg := args[0].ToInterface()
Log.Info("Setting Field Value", "filter", ev.Name, "field", fld, "value", arg) llog.Info("Setting Field Value", "filter", ev.Name, "field", fld, "value", arg)
ev.processedMessage.Body.Fields[fld] = arg ev.processedMessage.Body.Fields[fld] = arg
return &object.Void{} return &object.Void{}
} }
@ -184,11 +189,11 @@ func (ev *Filter) fnClearField(args []object.Object) object.Object {
return &object.Null{} return &object.Null{}
} }
fld := args[0].(*object.String).Value fld := args[0].(*object.String).Value
Log.Info("Clearing Field Value", "filter", ev.Name, "field", fld) llog.Info("Clearing Field Value", "filter", ev.Name, "field", fld)
if _, ok := ev.processedMessage.Body.Fields[fld]; ok { if _, ok := ev.processedMessage.Body.Fields[fld]; ok {
delete(ev.processedMessage.Body.Fields, fld) delete(ev.processedMessage.Body.Fields, fld)
} else { } else {
Log.Info("Field Not Found", "filter", ev.Name, "field", fld) llog.Info("Field Not Found", "filter", ev.Name, "field", fld)
} }
return &object.Void{} return &object.Void{}
} }
@ -202,7 +207,7 @@ func (ev *Filter) fnSetShortMessage(arg []object.Object) object.Object {
return &object.Null{} return &object.Null{}
} }
msg := arg[0].(*object.String).Value msg := arg[0].(*object.String).Value
Log.Info("Setting Short Message", "filter", ev.Name, "message", msg) llog.Info("Setting Short Message", "filter", ev.Name, "message", msg)
ev.processedMessage.Body.ShortMsg = msg ev.processedMessage.Body.ShortMsg = msg
return &object.Void{} return &object.Void{}
} }
@ -216,32 +221,33 @@ func (ev *Filter) fnSetSeverity(arg []object.Object) object.Object {
return &object.Null{} return &object.Null{}
} }
msg := arg[0].(*object.String).Value msg := arg[0].(*object.String).Value
Log.Info("Setting Severity", "filter", ev.Name, "Severity", msg) llog.Info("Setting Severity", "filter", ev.Name, "Severity", msg)
ev.processedMessage.Body.Severity = msg ev.processedMessage.Body.Severity = msg
return &object.Void{} return &object.Void{}
} }
func (ev *Filter) ProcessMessage(msg *msg.Message) (bool, error) { func (ev *Filter) ProcessMessage(ctx context.Context, msg *msg.Message) (bool, error) {
if !ev.Enabled { if !ev.Enabled {
return true, nil return true, nil
} }
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
Log.Error(err.(error), "Filter Script Error", "filter", ev.Name) llog.Error(err.(error), "Filter Script Error", "filter", ev.Name)
} }
}() }()
if !ev.ok { if !ev.ok {
Log.Info("Filter Script Not ready", "filter", ev.Name) llog.Info("Filter Script Not ready", "filter", ev.Name)
return true, nil return true, nil
} }
ev.processedMessage = msg ev.processedMessage = msg
ev.script.SetContext(ctx)
ok, err := ev.script.Run(msg.Body) ok, err := ev.script.Run(msg.Body)
ev.processedMessage = nil ev.processedMessage = nil
if err != nil { if err != nil {
Log.Info("Filter Run Failed", "filter", ev.Name, "result", ok, "Error", err) llog.Info("Filter Run Failed", "filter", ev.Name, "result", ok, "Error", err)
return true, err return true, err
} }
Log.V(1).Info("Filter Run Success", "filter", ev.Name, "result", ok) llog.V(1).Info("Filter Run Success", "filter", ev.Name, "result", ok)
return ok, nil return ok, nil
} }
@ -250,7 +256,7 @@ func (ev *Filter) SetupEvalFilter() error {
// Create an evaluator, with the script inside it. // Create an evaluator, with the script inside it.
// //
ev.script = evalfilter.New(ev.Content) ev.script = evalfilter.New(ev.Content)
Log.Info("Filter Script Content", "filter", ev.Name, "content", ev.Content) llog.V(1).Info("Filter Script Content", "filter", ev.Name, "content", ev.Content)
ev.script.AddFunction("printf", ev.fnPrintf) ev.script.AddFunction("printf", ev.fnPrintf)
ev.script.AddFunction("print", ev.fnPrint) ev.script.AddFunction("print", ev.fnPrint)
ev.script.AddFunction("setfield", ev.fnSetField) ev.script.AddFunction("setfield", ev.fnSetField)
@ -258,11 +264,11 @@ func (ev *Filter) SetupEvalFilter() error {
ev.script.AddFunction("setshortmessage", ev.fnSetShortMessage) ev.script.AddFunction("setshortmessage", ev.fnSetShortMessage)
ev.script.AddFunction("setseverity", ev.fnSetSeverity) ev.script.AddFunction("setseverity", ev.fnSetSeverity)
if err := ev.script.Prepare(); err != nil { if err := ev.script.Prepare(); err != nil {
Log.Info("Compile Filter Script Failed", "filter", ev.Name, "error", err) llog.Error(err, "Compile Filter Script Failed", "filter", ev.Name)
ev.ok = false ev.ok = false
return err return err
} }
Log.Info("Compile Filter Script Success", "filter", ev.Name) llog.V(1).Info("Compile Filter Script Success", "filter", ev.Name)
ev.ok = true ev.ok = true
return nil return nil
} }

View file

@ -6,17 +6,17 @@ import (
"time" "time"
"github.com/Fishwaldo/mouthpiece/internal/db" "github.com/Fishwaldo/mouthpiece/internal/db"
. "github.com/Fishwaldo/mouthpiece/internal/log" "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/go-logr/logr"
"github.com/alexliesenfeld/health" "github.com/alexliesenfeld/health"
httpCheck "github.com/hellofresh/health-go/v4/checks/http" httpCheck "github.com/hellofresh/health-go/v4/checks/http"
) )
var llog logr.Logger
var HealthChecker health.Checker var HealthChecker health.Checker
func StartHealth() { func StartHealth() {
llog = log.Log.WithName("health")
HealthChecker = health.NewChecker( HealthChecker = health.NewChecker(
health.WithTimeout(10*time.Second), health.WithTimeout(10*time.Second),
//health.WithInterceptors(interceptors.BasicLogger()), //health.WithInterceptors(interceptors.BasicLogger()),
@ -48,7 +48,7 @@ func BasicLogger() health.Interceptor {
return func(ctx context.Context, name string, state health.CheckState) health.CheckState { return func(ctx context.Context, name string, state health.CheckState) health.CheckState {
now := time.Now() now := time.Now()
result := next(ctx, name, state) result := next(ctx, name, state)
Log.V(1).Info("processed health check request", llog.V(1).Info("processed health check request",
"check", name, "seconds", time.Now().Sub(now).Seconds(), "result", result.Status) "check", name, "seconds", time.Now().Sub(now).Seconds(), "result", result.Status)
return result return result
} }

View file

@ -2,21 +2,81 @@ package log
import ( import (
"fmt" "fmt"
"github.com/danielgtaylor/huma/middleware" "time"
"path/filepath"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"github.com/go-logr/zapr" "github.com/go-logr/zapr"
"github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
) )
var Log logr.Logger var Log logr.Logger
var zapLog *zap.Logger var zapLog *zap.Logger
func init() {
viper.SetDefault("debug", false)
viper.SetDefault("log.dir", "logs")
viper.SetDefault("log.maxsize", 1)
viper.SetDefault("log.maxbackups", 3)
viper.SetDefault("log.maxage", 7)
viper.SetDefault("log.compress", true)
viper.SetDefault("log.level", "info")
}
func InitLogger() { func InitLogger() {
zapLog, err := middleware.NewDefaultLogger() var cfg zap.Config
if err != nil { var lvl zapcore.Level
panic(fmt.Sprintf("Initilize Logging Failed (%v)?", err)) var err error
if lvl, err = zapcore.ParseLevel(viper.GetString("log.level")); err != nil {
panic(err)
}
if viper.GetBool("debug") {
fmt.Printf("Debug Enabled at %s level\n", viper.GetString("log.level"))
cfg = zap.NewDevelopmentConfig()
cfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
cfg.OutputPaths = []string{"stdout"}
cfg.EncoderConfig.EncodeTime = iso8601UTCTimeEncoder
cfg.Level = zap.NewAtomicLevelAt(lvl)
var err error
if zapLog, err = cfg.Build(); err != nil {
panic(err)
}
} else {
fmt.Printf("Debug Disabled. Logging to file %s at %s level\n", filepath.Join(viper.GetString("log.dir"), "mouthpiece.log"), viper.GetString("log.level"))
lumberJackLogger := &lumberjack.Logger{
Filename: filepath.Join(viper.GetString("log.dir"), "mouthpiece.log"),
MaxSize: viper.GetInt("log.maxsize"), // megabytes
MaxBackups: viper.GetInt("log.maxbackups"),
MaxAge: viper.GetInt("log.maxage"), //days
Compress: viper.GetBool("log.compress"),
}
ws := zapcore.AddSync(lumberJackLogger)
enccfg := zap.NewProductionEncoderConfig()
enccfg.EncodeTime = iso8601UTCTimeEncoder
core := zapcore.NewCore(
zapcore.NewJSONEncoder(enccfg),
ws,
zap.NewAtomicLevelAt(lvl),
)
zapLog = zap.New(core)
zap.ReplaceGlobals(zapLog)
} }
Log = zapr.NewLogger(zapLog) Log = zapr.NewLogger(zapLog)
zap.RedirectStdLog(zapLog)
Log.Info("Logging Started") Log.Info("Logging Started", "level", viper.GetString("log.level"))
} }
// A UTC variation of ZapCore.ISO8601TimeEncoder with millisecond precision
func iso8601UTCTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.UTC().Format("2006-01-02T15:04:05.000Z"))
}
func GetZapLogger() (*zap.Logger, error) {
return zapLog.Named("huma"), nil
}

View file

@ -2,7 +2,7 @@ package middleware
import ( import (
"context" "context"
// "fmt" "strconv"
"net/http" "net/http"
"github.com/Fishwaldo/mouthpiece/internal/auth" "github.com/Fishwaldo/mouthpiece/internal/auth"
@ -17,7 +17,7 @@ import (
type Middleware struct { type Middleware struct {
} }
type CtxUserValue struct{}
// Update user info in request context from go-pkgz/auth token.User to mouthpiece.User // Update user info in request context from go-pkgz/auth token.User to mouthpiece.User
func (a *Middleware) Update() func(http.Handler) http.Handler { func (a *Middleware) Update() func(http.Handler) http.Handler {
@ -26,11 +26,13 @@ func (a *Middleware) Update() func(http.Handler) http.Handler {
// call update only if user info exists, otherwise do nothing // call update only if user info exists, otherwise do nothing
if tknuser, err := token.GetUserInfo(r); err == nil { if tknuser, err := token.GetUserInfo(r); err == nil {
/* find out DB User */ /* find out DB User */
if dbUser, err := user.GetUser(tknuser.Email); err != nil { id, _ := strconv.Atoi(tknuser.ID)
ctx := huma.ContextFromRequest(w, r)
if dbUser, err := user.GetUserByID(ctx, uint(id)); err != nil {
Log.Info("DBUser Not Found", "token", tknuser, "error", err) Log.Info("DBUser Not Found", "token", tknuser, "error", err)
ctx := huma.ContextFromRequest(w, r)
/* do Something */ /* do Something */
ctx.WriteError(http.StatusForbidden, "User not found", err) ctx.WriteError(http.StatusUnauthorized, "User not found", err)
return return
} else { } else {
ok, res, err := auth.AuthService.AuthEnforcer.EnforceEx(dbUser.Email, r.URL.Path, r.Method) ok, res, err := auth.AuthService.AuthEnforcer.EnforceEx(dbUser.Email, r.URL.Path, r.Method)
@ -39,7 +41,7 @@ func (a *Middleware) Update() func(http.Handler) http.Handler {
huma.ContextFromRequest(w, r).WriteError(http.StatusForbidden, "Access Denied", err) huma.ContextFromRequest(w, r).WriteError(http.StatusForbidden, "Access Denied", err)
return return
} }
r = r.WithContext(context.WithValue(r.Context(), CtxUserValue{}, tknuser)) r = r.WithContext(context.WithValue(r.Context(), user.CtxUserValue{}, tknuser))
} }
h.ServeHTTP(w, r) h.ServeHTTP(w, r)
return return
@ -52,3 +54,4 @@ func (a *Middleware) Update() func(http.Handler) http.Handler {
} }
return f return f
} }

View file

@ -1,15 +1,17 @@
package mouthpiece package mouthpiece
import ( import (
"context"
"github.com/Fishwaldo/mouthpiece/internal/app" "github.com/Fishwaldo/mouthpiece/internal/app"
"github.com/Fishwaldo/mouthpiece/internal/errors" "github.com/Fishwaldo/mouthpiece/internal/errors"
. "github.com/Fishwaldo/mouthpiece/internal/log" . "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/Fishwaldo/mouthpiece/internal/message" "github.com/Fishwaldo/mouthpiece/internal/message"
) )
func RouteMessage(msg *msg.Message) { func RouteMessage(ctx context.Context, msg *msg.Message) {
if app, err := app.FindApp(msg.AppName); err == nil { if app, err := app.FindApp(ctx, msg.AppName); err == nil {
app.ProcessMessage(msg) app.ProcessMessage(ctx, msg)
} else { } else {
Log.Error(mperror.ErrAppNotFound, "App Not Found", "App", msg.AppName) Log.Error(mperror.ErrAppNotFound, "App Not Found", "App", msg.AppName)
} }

View file

@ -2,6 +2,7 @@ package stdout
import ( import (
"fmt" "fmt"
"context"
. "github.com/Fishwaldo/mouthpiece/internal/log" . "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/Fishwaldo/mouthpiece/internal/message" "github.com/Fishwaldo/mouthpiece/internal/message"
"github.com/Fishwaldo/mouthpiece/internal/transport" "github.com/Fishwaldo/mouthpiece/internal/transport"
@ -23,11 +24,11 @@ func (t StdoutTransport) GetName() string {
return "stdout" return "stdout"
} }
func (t StdoutTransport) SendMessage(config transport.TransportConfig, msg msg.Message) (err error) { func (t StdoutTransport) SendMessage(ctx context.Context, config transport.TransportConfig, msg msg.Message) (err error) {
fmt.Println("=========================================================") fmt.Println("=========================================================")
fmt.Printf("Message: %s\n", msg.Body.Message) fmt.Printf("Message: %s\n", msg.Body.Message)
fmt.Println("=========================================================") fmt.Println("=========================================================")
transport.UpdateTransportStatus(t, msg, "sent") transport.UpdateTransportStatus(ctx, t, msg, "sent")
return nil return nil
} }
@ -35,7 +36,7 @@ func (t StdoutTransport) Start() {
Log.Info("Transport Started", "name", t.GetName()) Log.Info("Transport Started", "name", t.GetName())
} }
func (t StdoutTransport) NewTransportConfig() { func (t StdoutTransport) NewTransportConfig(ctx context.Context) {
// user.TransportConfigs = append(user.TransportConfigs, mouthpiece.TransportConfig{ // user.TransportConfigs = append(user.TransportConfigs, mouthpiece.TransportConfig{
// Transport: t.GetName(), // Transport: t.GetName(),
// Config: user.Username, // Config: user.Username,

View file

@ -2,7 +2,7 @@ package telegram
import ( import (
"fmt" "fmt"
// "os" "context"
. "github.com/Fishwaldo/mouthpiece/internal/log" . "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/Fishwaldo/mouthpiece/internal/message" "github.com/Fishwaldo/mouthpiece/internal/message"
@ -73,17 +73,17 @@ func (t TelegramTransport) Start() {
Log.Info("Transport Started", "name", t.GetName()) Log.Info("Transport Started", "name", t.GetName())
} }
func (t TelegramTransport) NewTransportConfig() { func (t TelegramTransport) NewTransportConfig(ctx context.Context) {
// user.TransportConfigs = append(user.TransportConfigs, mouthpiece.TransportConfig{ // user.TransportConfigs = append(user.TransportConfigs, mouthpiece.TransportConfig{
// Transport: t.GetName(), // Transport: t.GetName(),
// Config: user.Username, // Config: user.Username,
// }) // })
} }
func (t TelegramTransport) SendMessage(config transport.TransportConfig, msg msg.Message) (err error) { func (t TelegramTransport) SendMessage(ctx context.Context, config transport.TransportConfig, msg msg.Message) (err error) {
fmt.Println("=========================================================") fmt.Println("=========================================================")
fmt.Printf("Message: %s\n", msg.Body.Message) fmt.Printf("Message: %s\n", msg.Body.Message)
fmt.Println("=========================================================") fmt.Println("=========================================================")
transport.UpdateTransportStatus(t, msg, "sent") transport.UpdateTransportStatus(ctx, t, msg, "sent")
return nil return nil
} }

View file

@ -2,6 +2,7 @@ package transport
import ( import (
"errors" "errors"
"context"
"github.com/Fishwaldo/mouthpiece/internal/db" "github.com/Fishwaldo/mouthpiece/internal/db"
. "github.com/Fishwaldo/mouthpiece/internal/log" . "github.com/Fishwaldo/mouthpiece/internal/log"
@ -19,8 +20,8 @@ type TransportConfig struct {
type ITransport interface { type ITransport interface {
GetName() string GetName() string
Start() Start()
SendMessage(config TransportConfig, message msg.Message) (err error) SendMessage(ctx context.Context, config TransportConfig, message msg.Message) (err error)
NewTransportConfig() NewTransportConfig(ctx context.Context)
} }
var transports map[string]ITransport var transports map[string]ITransport
@ -43,14 +44,14 @@ func StartTransports() {
} }
} }
func GetTransport(name string) (ITransport, error) { func GetTransport(ctx context.Context, name string) (ITransport, error) {
if t, ok := transports[name]; ok { if t, ok := transports[name]; ok {
return t, nil return t, nil
} }
return nil, errors.New("Transport Not Found") return nil, errors.New("Transport Not Found")
} }
func GetTransports() []string { func GetTransports(ctx context.Context) []string {
var a []string var a []string
for k := range transports { for k := range transports {
a = append(a, k) a = append(a, k)
@ -58,6 +59,6 @@ func GetTransports() []string {
return a return a
} }
func UpdateTransportStatus(t ITransport, m msg.Message, status string) { func UpdateTransportStatus(ctx context.Context, t ITransport, m msg.Message, status string) {
Log.Info("Transport Status", "status", status, "MessageID", m.ID, "Transport", t.GetName()) Log.Info("Transport Status", "status", status, "MessageID", m.ID, "Transport", t.GetName())
} }

View file

@ -1,6 +1,9 @@
package user package user
import ( import (
"context"
"fmt"
"strconv"
"strings" "strings"
"github.com/Fishwaldo/mouthpiece/internal/errors" "github.com/Fishwaldo/mouthpiece/internal/errors"
@ -8,17 +11,20 @@ import (
"github.com/go-pkgz/auth/token" "github.com/go-pkgz/auth/token"
) )
type CtxUserValue struct{}
func dbAuthProvider(user, pass string) (ok bool, err error) { func dbAuthProvider(user, pass string) (ok bool, err error) {
user = strings.TrimSpace(user) user = strings.TrimSpace(user)
Log.Info("Direct Login", "user", user, "pass", pass) Log.Info("Direct Login", "user", user)
dbUser, err := GetUser(user) dbUser, err := GetUser(context.Background(), user)
Log.Info("User", "user", dbUser, "error", err) Log.Info("User", "user", dbUser, "error", err)
if err == mperror.ErrUserNotFound { if err == mperror.ErrUserNotFound {
Log.Info("User not found", "user", user) Log.Info("User not found", "user", user)
return false, nil return false, nil
} }
if !dbUser.CheckPassword(pass) { if !dbUser.CheckPassword(context.Background(), pass) {
Log.Info("Password Invalid", "user", user) Log.Info("Password Invalid", "user", user)
return false, nil return false, nil
} }
@ -27,7 +33,7 @@ func dbAuthProvider(user, pass string) (ok bool, err error) {
// Called when the Tokens are created/refreshed. // Called when the Tokens are created/refreshed.
func MapClaimsToUser(claims token.Claims) token.Claims { func MapClaimsToUser(claims token.Claims) token.Claims {
Log.Info("Map Claims To User", "claims", claims) //Log.Info("Map Claims To User", "claims", claims)
// if claims.User != nil { // if claims.User != nil {
// if user, err := GetUser(claims.User.Name); err != nil { // if user, err := GetUser(claims.User.Name); err != nil {
// Log.Info("User not found", "user", claims.User.Name) // Log.Info("User not found", "user", claims.User.Name)
@ -42,9 +48,22 @@ func MapClaimsToUser(claims token.Claims) token.Claims {
// called on every access to the API // called on every access to the API
func UserValidator(token string, claims token.Claims) bool { func UserValidator(token string, claims token.Claims) bool {
Log.Info("User Validator", "token", token, "claims", claims) //Log.Info("User Validator", "user", claims.User.Name)
if claims.User != nil { if claims.User != nil {
return true if user, _ := GetUser(context.Background(), claims.User.Name); user != nil {
} claims.User.ID = fmt.Sprintf("%d", user.ID)
return true
}
}
return false return false
} }
func GetUserFromContext(ctx context.Context) (bool, *User) {
v := ctx.Value(CtxUserValue{}).(token.User)
if id, _ := strconv.Atoi(v.ID); id > 0 {
if user, _ := GetUserByID(ctx, uint(id)); user != nil {
return true, user
}
}
return false, nil
}

View file

@ -1,14 +1,16 @@
package user package user
import ( import (
"context"
"fmt" "fmt"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"github.com/Fishwaldo/mouthpiece/internal/auth" "github.com/Fishwaldo/mouthpiece/internal/auth"
"github.com/Fishwaldo/mouthpiece/internal/db" "github.com/Fishwaldo/mouthpiece/internal/db"
"github.com/Fishwaldo/mouthpiece/internal/errors" mperror "github.com/Fishwaldo/mouthpiece/internal/errors"
. "github.com/Fishwaldo/mouthpiece/internal/log" . "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/Fishwaldo/mouthpiece/internal/message" msg "github.com/Fishwaldo/mouthpiece/internal/message"
"github.com/Fishwaldo/mouthpiece/internal/transport" "github.com/Fishwaldo/mouthpiece/internal/transport"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
@ -36,27 +38,27 @@ func init() {
} }
} }
func CreateUser(user *User) error { func CreateUser(ctx context.Context, user *User) error {
validate := validator.New() validate := validator.New()
if err := validate.Struct(user); err != nil { if err := validate.Struct(user); err != nil {
Log.Info("User Validation Error", "Error", err) Log.Info("User Validation Error", "Error", err)
return err return err
} }
tx := db.Db.Omit("Password").Create(&user) tx := db.Db.WithContext(ctx).Omit("Password").Create(&user)
if tx.Error != nil { if tx.Error != nil {
return tx.Error return tx.Error
} }
if dbuser, err := GetUser(user.Email); err == nil { if dbuser, err := GetUser(ctx, user.Email); err == nil {
/* Set the Users Initial Password */ /* Set the Users Initial Password */
if err := dbuser.SetPassword(user.Password); err != nil { if err := dbuser.SetPassword(ctx, user.Password); err != nil {
if tx := db.Db.Delete(&dbuser); tx.Error != nil { if tx := db.Db.WithContext(ctx).Delete(&dbuser); tx.Error != nil {
Log.Info("Error Deleting User after failed Password", "Error", tx.Error) Log.Info("Error Deleting User after failed Password", "Error", tx.Error)
return err return err
} }
return err return err
} }
/* New Users all Start with User Role */ /* New Users all Start with User Role */
if !dbuser.addUserRole("user") { if !dbuser.addUserRole(ctx, "user") {
Log.Info("Error Adding User Role", "Error", err) Log.Info("Error Adding User Role", "Error", err)
} }
return nil return nil
@ -65,7 +67,7 @@ func CreateUser(user *User) error {
} }
} }
func (u *User) addUserRole(role string) bool { func (u *User) addUserRole(ctx context.Context, role string) bool {
_, err := auth.AuthService.AuthEnforcer.AddRoleForUser(u.Email, fmt.Sprintf("role:%s", role)) _, err := auth.AuthService.AuthEnforcer.AddRoleForUser(u.Email, fmt.Sprintf("role:%s", role))
if err != nil { if err != nil {
Log.Info("Failed to add role for user", "email", u.Email, "role", role, "error", err) Log.Info("Failed to add role for user", "email", u.Email, "role", role, "error", err)
@ -74,7 +76,7 @@ func (u *User) addUserRole(role string) bool {
return true return true
} }
func (u *User) CheckPassword(password string) bool { func (u *User) CheckPassword(ctx context.Context, password string) bool {
Log.Info("Checking Password", "email", u.Email) Log.Info("Checking Password", "email", u.Email)
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
if err != nil { if err != nil {
@ -84,14 +86,14 @@ func (u *User) CheckPassword(password string) bool {
return true return true
} }
func (u *User) SetPassword(password string) error { func (u *User) SetPassword(ctx context.Context, password string) error {
Log.Info("Setting Password", "Email", u.Email) Log.Info("Setting Password", "Email", u.Email)
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil { if err != nil {
Log.Info("Error Generating SetPassword Hash", "Error", err) Log.Info("Error Generating SetPassword Hash", "Error", err)
return err return err
} }
if tx := db.Db.Model(&u).Update("password", string(hashedPassword)); tx.Error != nil { if tx := db.Db.WithContext(ctx).Model(&u).Update("password", string(hashedPassword)); tx.Error != nil {
Log.Info("Error Setting Password", "Error", tx.Error) Log.Info("Error Setting Password", "Error", tx.Error)
return tx.Error return tx.Error
} }
@ -106,49 +108,49 @@ func InitializeUsers() {
if count == 0 { if count == 0 {
Log.Info("Creating Default Users") Log.Info("Creating Default Users")
admin := &User{FirstName: "Admin", LastName: "User", Email: "admin@example.com", Password: "password"} admin := &User{FirstName: "Admin", LastName: "User", Email: "admin@example.com", Password: "password"}
if err := CreateUser(admin); err == nil { if err := CreateUser(context.Background(), admin); err == nil {
admin.addUserRole("admin") admin.addUserRole(context.Background(), "admin")
Log.Info("Created Default Admin admin@example.com") Log.Info("Created Default Admin admin@example.com")
} }
if err := CreateUser(&User{FirstName: "User", LastName: "User", Email: "user@example.com", Password: "password"}); err == nil { if err := CreateUser(context.Background(), &User{FirstName: "User", LastName: "User", Email: "user@example.com", Password: "password"}); err == nil {
Log.Info("Created Default User user@example.com") Log.Info("Created Default User user@example.com")
} }
} }
} }
func GetUsers() []User { func GetUsers(ctx context.Context) []User {
var users []User var users []User
db.Db.Find(&users) db.Db.WithContext(ctx).Find(&users)
return users return users
} }
func GetUser(email string) (user *User, err error) { func GetUser(ctx context.Context, email string) (user *User, err error) {
tx := db.Db.Preload(clause.Associations).First(&user, "email = ?", email) tx := db.Db.WithContext(ctx).Preload(clause.Associations).First(&user, "email = ?", email)
if tx.Error == gorm.ErrRecordNotFound { if tx.Error == gorm.ErrRecordNotFound {
return nil, mperror.ErrUserNotFound return nil, mperror.ErrUserNotFound
} }
return return
} }
func GetUserByID(id uint) (user *User, err error) { func GetUserByID(ctx context.Context, id uint) (user *User, err error) {
tx := db.Db.Preload(clause.Associations).First(&user, "ID = ?", id) tx := db.Db.WithContext(ctx).Preload(clause.Associations).First(&user, "ID = ?", id)
if tx.Error == gorm.ErrRecordNotFound { if tx.Error == gorm.ErrRecordNotFound {
return nil, mperror.ErrUserNotFound return nil, mperror.ErrUserNotFound
} }
return return
} }
func (u User) ProcessMessage(msg msg.Message) (err error) { func (u User) ProcessMessage(ctx context.Context, msg msg.Message) (err error) {
/* add User Fields to Message */ /* add User Fields to Message */
msg.Body.Fields["first_name"] = u.FirstName msg.Body.Fields["first_name"] = u.FirstName
msg.Body.Fields["last_name"] = u.LastName msg.Body.Fields["last_name"] = u.LastName
msg.Body.Fields["email"] = u.Email msg.Body.Fields["email"] = u.Email
Log.V(1).Info("User Processing Message", "Email", u.Email, "MessageID", msg.ID) Log.V(1).Info("User Processing Message", "Email", u.Email, "MessageID", msg.ID)
for _, tc := range u.TransportConfigs { for _, tc := range u.TransportConfigs {
t, err := transport.GetTransport(tc.Transport) t, err := transport.GetTransport(ctx, tc.Transport)
if err != nil { if err != nil {
Log.Info("Cant find Transport", "Transport", tc.Transport) Log.Info("Cant find Transport", "Transport", tc.Transport)
} }
go t.SendMessage(tc, msg) go t.SendMessage(ctx, tc, msg)
} }
return return
} }

192
main.go
View file

@ -25,28 +25,25 @@ SOFTWARE.
package main package main
import ( import (
//"fmt" "embed"
// "context"
"fmt" "fmt"
"io/fs" "io/fs"
"net/http" "net/http"
// "reflect"
"strings"
// "unsafe"
"embed"
"encoding/json"
"os" "os"
"runtime/debug" "os/signal"
"strings"
"syscall"
"time"
"context"
"path/filepath"
"github.com/Fishwaldo/mouthpiece/frontend" "github.com/Fishwaldo/mouthpiece/frontend"
_ "github.com/Fishwaldo/mouthpiece/frontend"
mouthpiece "github.com/Fishwaldo/mouthpiece/internal" mouthpiece "github.com/Fishwaldo/mouthpiece/internal"
"github.com/Fishwaldo/mouthpiece/internal/app" "github.com/Fishwaldo/mouthpiece/internal/app"
"github.com/Fishwaldo/mouthpiece/internal/auth" "github.com/Fishwaldo/mouthpiece/internal/auth"
"github.com/Fishwaldo/mouthpiece/internal/db" "github.com/Fishwaldo/mouthpiece/internal/db"
"github.com/Fishwaldo/mouthpiece/internal/filter" "github.com/Fishwaldo/mouthpiece/internal/filter"
. "github.com/Fishwaldo/mouthpiece/internal/log" "github.com/Fishwaldo/mouthpiece/internal/log"
msg "github.com/Fishwaldo/mouthpiece/internal/message" msg "github.com/Fishwaldo/mouthpiece/internal/message"
"github.com/Fishwaldo/mouthpiece/internal/middleware" "github.com/Fishwaldo/mouthpiece/internal/middleware"
"github.com/Fishwaldo/mouthpiece/internal/transport" "github.com/Fishwaldo/mouthpiece/internal/transport"
@ -61,14 +58,50 @@ import (
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/danielgtaylor/huma" "github.com/danielgtaylor/huma"
"github.com/danielgtaylor/huma/cli" hmw "github.com/danielgtaylor/huma/middleware"
"github.com/danielgtaylor/huma/responses" "github.com/danielgtaylor/huma/responses"
"github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
//go:embed config //go:embed config
var ConfigFiles embed.FS var ConfigFiles embed.FS
type mpserver struct {
*huma.Router
root *cobra.Command
prestart []func()
}
var Server mpserver
func (c *mpserver) Flag(name, short, description string, defaultValue interface{}) {
viper.SetDefault(name, defaultValue)
flags := c.root.PersistentFlags()
switch v := defaultValue.(type) {
case bool:
flags.BoolP(name, short, viper.GetBool(name), description)
case int, int16, int32, int64, uint16, uint32, uint64:
flags.IntP(name, short, viper.GetInt(name), description)
case float32, float64:
flags.Float64P(name, short, viper.GetFloat64(name), description)
default:
flags.StringP(name, short, fmt.Sprintf("%v", v), description)
}
viper.BindPFlag(name, flags.Lookup(name))
}
func (c *mpserver) PreStart(f func()) {
c.prestart = append(c.prestart, f)
}
func (c *mpserver) Run() {
if err := c.root.Execute(); err != nil {
panic(err)
}
}
func init() { func init() {
viper.SetDefault("frontend.path", "frontend/dist") viper.SetDefault("frontend.path", "frontend/dist")
viper.SetDefault("frontend.external", false) viper.SetDefault("frontend.external", false)
@ -81,7 +114,7 @@ func fileServer(r chi.Router, path string, root http.FileSystem) {
panic("FileServer does not permit URL parameters.") panic("FileServer does not permit URL parameters.")
} }
//log.Printf("[INFO] serving static files from %v", root) //log.Log.Printf("[INFO] serving static files from %v", root)
fs := http.StripPrefix(path, http.FileServer(root)) fs := http.StripPrefix(path, http.FileServer(root))
if path != "/" && path[len(path)-1] != '/' { if path != "/" && path[len(path)-1] != '/' {
@ -95,21 +128,12 @@ func fileServer(r chi.Router, path string, root http.FileSystem) {
}) })
} }
func printBuildInfo() {
bi, ok := debug.ReadBuildInfo()
if !ok {
fmt.Println("Getting build info failed (not in module mode?)!")
return
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
if err := enc.Encode(bi); err != nil {
panic(err)
}
}
func main() { func main() {
viper.SetEnvPrefix("MP")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
viper.AutomaticEnv()
viper.SetConfigName("config") viper.SetConfigName("config")
viper.SetConfigType("yaml") viper.SetConfigType("yaml")
viper.AddConfigPath(".") viper.AddConfigPath(".")
@ -139,21 +163,51 @@ func main() {
fmt.Println(bi.String()) fmt.Println(bi.String())
// Create a new router & CLI with default middleware. // Create a new router & CLI with default middleware.
InitLogger()
Server = mpserver{
Router: huma.New(bi.Name, bi.GitVersion),
}
hmw.Defaults(Server.Router)
Server.root = &cobra.Command{
Use: filepath.Base(os.Args[0]),
Version: bi.GitVersion,
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Starting %s (%s)\n", bi.Name, bi.GitVersion)
for _, f := range Server.prestart {
f()
}
go func() {
if err := Server.Listen(fmt.Sprintf("%s:%v", viper.Get("host"), viper.Get("port"))); err != nil && err != http.ErrServerClosed {
panic(err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
fmt.Println("Shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
Server.Shutdown(ctx)
},
}
Server.Flag("host", "", "Hostname", "0.0.0.0")
Server.Flag("port", "p", "Port", 8888)
log.InitLogger()
db.InitializeDB() db.InitializeDB()
humucli := cli.NewRouter(bi.Name, bi.GitVersion) hmw.NewLogger = log.GetZapLogger
humucli.DisableSchemaProperty() Server.DisableSchemaProperty()
humucli.PreStart(transport.InitializeTransports) Server.PreStart(transport.InitializeTransports)
humucli.PreStart(msg.InitializeMessage) Server.PreStart(msg.InitializeMessage)
humucli.PreStart(user.InitializeUsers) Server.PreStart(user.InitializeUsers)
humucli.PreStart(app.InitializeApps) Server.PreStart(app.InitializeApps)
humucli.PreStart(transport.StartTransports) Server.PreStart(transport.StartTransports)
humucli.PreStart(filter.InitFilter) Server.PreStart(filter.InitFilter)
// app.PreStart() Server.PreStart(healthChecker.StartHealth)
humucli.PreStart(healthChecker.StartHealth) Server.GatewayClientCredentials("mouthpiece", "/oauth2/token", nil)
humucli.GatewayClientCredentials("mouthpiece", "/oauth2/token", nil) Server.GatewayAuthCode("mouthpiece2", "/oauth2/token", "/oauth2/token", nil)
humucli.GatewayAuthCode("mouthpiece2", "/oauth2/token", "/oauth2/token", nil) Server.GatewayBasicAuth("basic")
humucli.GatewayBasicAuth("basic")
user.AuthConfig.Host = fmt.Sprintf("http://arm64-1.dmz.dynam.ac:%v", viper.Get("Port")) user.AuthConfig.Host = fmt.Sprintf("http://arm64-1.dmz.dynam.ac:%v", viper.Get("Port"))
user.AuthConfig.ConfigDir = ConfigFiles user.AuthConfig.ConfigDir = ConfigFiles
@ -162,26 +216,26 @@ func main() {
p := middleware.Middleware{} p := middleware.Middleware{}
authRoutes, avaRoutes := auth.AuthService.Service.Handlers() authRoutes, avaRoutes := auth.AuthService.Service.Handlers()
mux := humucli.Resource("/").GetMux() mux := Server.Resource("/").GetMux()
mux.Mount("/auth", authRoutes) mux.Mount("/auth", authRoutes)
mux.Mount("/avatar", avaRoutes) mux.Mount("/avatar", avaRoutes)
var httpfiles http.FileSystem var httpfiles http.FileSystem
if viper.GetBool("frontend.external") { if viper.GetBool("frontend.external") {
Log.Info("Serving frontend from external location", "path", viper.GetString("frontend.path")) log.Log.Info("Serving frontend from external location", "path", viper.GetString("frontend.path"))
httpfiles = http.Dir(viper.GetString("frontend.path")) httpfiles = http.Dir(viper.GetString("frontend.path"))
} else { } else {
Log.Info("Serving frontend from Bundled Files") log.Log.Info("Serving frontend from Bundled Files")
subdir, err := fs.Sub(frontend.FrontEndFiles, "dist") subdir, err := fs.Sub(frontend.FrontEndFiles, "dist")
if err != nil { if err != nil {
Log.Error(err, "Failed to get subdir") log.Log.Error(err, "Failed to get subdir")
} }
httpfiles = http.FS(subdir) httpfiles = http.FS(subdir)
} }
fileServer(mux, "/static", httpfiles) fileServer(mux, "/static", httpfiles)
// Declare the root resource and a GET operation on it. // Declare the root resource and a GET operation on it.
humucli.Resource("/health").Get("get-health", "Get Health of the Service", Server.Resource("/health").Get("get-health", "Get Health of the Service",
responses.OK().ContentType("application/json"), responses.OK().ContentType("application/json"),
responses.OK().Headers("Content-Type"), responses.OK().Headers("Content-Type"),
responses.OK().Model(health.CheckerResult{}), responses.OK().Model(health.CheckerResult{}),
@ -197,7 +251,7 @@ func main() {
ctx.WriteModel(status, test) ctx.WriteModel(status, test)
}) })
humucli.Resource("/config/frontend").Get("get-config", "Get Config of the Service", Server.Resource("/config/frontend").Get("get-config", "Get Config of the Service",
responses.OK().ContentType("application/json"), responses.OK().ContentType("application/json"),
responses.OK().Headers("Content-Type"), responses.OK().Headers("Content-Type"),
responses.OK().Model(&mouthpiece.FEConfig{}), responses.OK().Model(&mouthpiece.FEConfig{}),
@ -205,7 +259,7 @@ func main() {
ctx.WriteModel(http.StatusOK, mouthpiece.GetFEConfig()) ctx.WriteModel(http.StatusOK, mouthpiece.GetFEConfig())
}) })
v1api := humucli.Resource("/v1") v1api := Server.Resource("/v1")
v1api.Middleware(m.Trace) v1api.Middleware(m.Trace)
v1api.Middleware(p.Update()) v1api.Middleware(p.Update())
@ -215,10 +269,10 @@ func main() {
responses.OK().Model(&msg.MessageResult{}), responses.OK().Model(&msg.MessageResult{}),
responses.NotFound().ContentType("application/json"), responses.NotFound().ContentType("application/json"),
).Run(func(ctx huma.Context, input msg.Message) { ).Run(func(ctx huma.Context, input msg.Message) {
Log.Info("Recieved Message", "message", input) log.Log.Info("Recieved Message", "message", input)
if app.AppExists(input.AppName) { if app.AppExists(ctx, input.AppName) {
if err := input.ProcessMessage(); err == nil { if err := input.ProcessMessage(); err == nil {
mouthpiece.RouteMessage(&input) mouthpiece.RouteMessage(ctx, &input)
ctx.WriteModel(http.StatusOK, input.Result) ctx.WriteModel(http.StatusOK, input.Result)
} else { } else {
ctx.WriteError(http.StatusInternalServerError, err.Error()) ctx.WriteError(http.StatusInternalServerError, err.Error())
@ -228,30 +282,11 @@ func main() {
} }
}) })
auth.AuthService.AddResourceURL("/v1/apps/", "apigroup:apps")
appapi := v1api.SubResource("/apps/") if err := app.InitializeAppRestAPI(v1api); err != nil {
appapi.Get("get-apps", "Get A List of Applications", log.Log.Error(err, "Failed to initialize App Rest API")
responses.OK().ContentType("application/json"), }
responses.OK().Headers("Set-Cookie"),
responses.OK().Model([]app.App{}),
).Run(func(ctx huma.Context) {
ctx.WriteModel(http.StatusOK, app.GetApps())
})
appapi.Put("create-app", "Create a Application",
responses.OK().ContentType("application/json"),
responses.OK().Headers("Set-Cookie"),
responses.OK().Model(&app.App{}),
responses.NotAcceptable().ContentType("application/json"),
responses.NotAcceptable().Headers("Set-Cookie"),
).Run(func(ctx huma.Context, input struct {
Body app.AppDetails
}) {
if app, err := app.CreateApp(input.Body); err != nil {
ctx.WriteError(http.StatusNotAcceptable, "Database Error", err)
} else {
ctx.WriteModel(http.StatusOK, app)
}
})
auth.AuthService.AddResourceURL("/v1/users/", "apigroup:users") auth.AuthService.AddResourceURL("/v1/users/", "apigroup:users")
userapi := v1api.SubResource("/users/") userapi := v1api.SubResource("/users/")
@ -260,7 +295,7 @@ func main() {
responses.OK().Headers("Set-Cookie"), responses.OK().Headers("Set-Cookie"),
responses.OK().Model([]user.User{}), responses.OK().Model([]user.User{}),
).Run(func(ctx huma.Context) { ).Run(func(ctx huma.Context) {
ctx.WriteModel(http.StatusOK, user.GetUsers()) ctx.WriteModel(http.StatusOK, user.GetUsers(ctx))
}) })
auth.AuthService.AddResourceURL("/v1/users/{userid}/transports/", "apigroup:users") auth.AuthService.AddResourceURL("/v1/users/{userid}/transports/", "apigroup:users")
@ -273,7 +308,7 @@ func main() {
).Run(func(ctx huma.Context, input struct { ).Run(func(ctx huma.Context, input struct {
User uint `path:"userid"` User uint `path:"userid"`
}) { }) {
if user, err := user.GetUserByID(input.User); err != nil { if user, err := user.GetUserByID(ctx, input.User); err != nil {
ctx.WriteError(http.StatusNotFound, "User Not Found", err) ctx.WriteError(http.StatusNotFound, "User Not Found", err)
} else { } else {
var transport []string var transport []string
@ -290,11 +325,12 @@ func main() {
responses.OK().Headers("Set-Cookie"), responses.OK().Headers("Set-Cookie"),
responses.OK().Model(transport.TransportConfig{}), responses.OK().Model(transport.TransportConfig{}),
responses.NotFound().ContentType("application/json"), responses.NotFound().ContentType("application/json"),
responses.NotFound().Headers("Set-Cookie"),
).Run(func(ctx huma.Context, input struct { ).Run(func(ctx huma.Context, input struct {
User uint `path:"userid"` User uint `path:"userid"`
Transport string `path:"transportid"` Transport string `path:"transportid"`
}) { }) {
if user, err := user.GetUserByID(input.User); err != nil { if user, err := user.GetUserByID(ctx, input.User); err != nil {
ctx.WriteError(http.StatusNotFound, "User Not Found", err) ctx.WriteError(http.StatusNotFound, "User Not Found", err)
} else { } else {
ok := false ok := false
@ -316,9 +352,9 @@ func main() {
responses.OK().Headers("Set-Cookie"), responses.OK().Headers("Set-Cookie"),
responses.OK().Model([]string{}), responses.OK().Model([]string{}),
).Run(func(ctx huma.Context) { ).Run(func(ctx huma.Context) {
ctx.WriteModel(http.StatusOK, transport.GetTransports()) ctx.WriteModel(http.StatusOK, transport.GetTransports(ctx))
}) })
// Run the CLI. When passed no arguments, it starts the server. // Run the CLI. When passed no arguments, it starts the server.
humucli.Run() Server.Run()
} }