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/
dist/
test.db-journal
logs/

1
go.mod
View file

@ -29,6 +29,7 @@ require (
github.com/spf13/viper v1.12.0
golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
gopkg.in/natefinch/lumberjack.v2 v2.0.0
)
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/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/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
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/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/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/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/go.mod h1:KUWAqbDteYGS/CiXftomQsKjtEOifXsJ36Ka0skYbmk=
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.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
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/yaml.v2 v2.2.1/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 (
"errors"
"context"
"github.com/Fishwaldo/mouthpiece/internal/db"
"github.com/Fishwaldo/mouthpiece/internal/errors"
@ -39,32 +40,32 @@ func InitializeApps() {
db.Db.AutoMigrate(&ApplicationFilters{})
}
func GetApps() []App {
func GetApps(ctx context.Context) []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
}
func FindApp(app_name string) (app *App, err error) {
tx := db.Db.Debug().Preload("AssociatedUsers").Preload("AssociatedUsers.TransportConfigs").Preload("Filters").First(&app, "app_name = ?", app_name)
func FindApp(ctx context.Context, app_name string) (app *App, err error) {
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)
return app, tx.Error
}
func AppExists(app_name string) bool {
func AppExists(ctx context.Context, app_name string) bool {
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
}
func CreateApp(app AppDetails) (newapp *App, err error) {
newapp, err = FindApp(app.AppName)
func CreateApp(ctx context.Context, app AppDetails) (newapp *App, err error) {
newapp, err = FindApp(ctx, app.AppName)
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
Log.Info("Creating New App", "App", app)
var 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)
normaluser, _ := user.GetUser("user@example.com")
normaluser, _ := user.GetUser(ctx, "user@example.com")
dbApp.AssociatedUsers = append(dbApp.AssociatedUsers, normaluser)
if filter.FindFilter("CopyShortMessage") != nil {
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 {
dbApp.Filters = append(dbApp.Filters, ApplicationFilters{Name: "FindSeverity"})
}
result := db.Db.Create(&dbApp)
result := db.Db.WithContext(ctx).Create(&dbApp)
if result.Error != nil {
return newapp, result.Error
}
return FindApp(app.AppName)
return FindApp(ctx, app.AppName)
}
Log.Error(err, "App Already Exists", "App", newapp)
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)
/* populate Message Fields with App Data */
msg.Body.Fields["app_description"] = app.Description
@ -92,7 +93,7 @@ func (app App) ProcessMessage(msg *msg.Message) error {
flt := filter.FindFilter(appfilter.Name)
if flt != nil {
Log.V(1).Info("App Processing Message with Filter", "Filter", appfilter)
ok, _ := flt.ProcessMessage(msg)
ok, _ := flt.ProcessMessage(ctx, msg)
if !ok {
Log.Info("App Filter Blocked Message", "App", app.AppName, "Filter", appfilter, "Message", msg)
return nil
@ -102,7 +103,7 @@ func (app App) ProcessMessage(msg *msg.Message) error {
}
}
for _, user := range app.AssociatedUsers {
user.ProcessMessage(*msg)
user.ProcessMessage(ctx, *msg)
}
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"
"github.com/Fishwaldo/mouthpiece/internal/db"
. "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/go-logr/logr"
"github.com/spf13/viper"
@ -41,14 +42,14 @@ type Auth struct {
}
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{}) {
Log.WithName("Auth").Info("Authentication", "message", fmt.Sprintf(format, args...))
func (AL authLogger) Logf(format string, args ...interface{}) {
llog.V(1).Info("Authentication", "message", fmt.Sprintf(format, args...))
}
type AuthConfig struct {
@ -100,7 +101,9 @@ func customGitHubProvider() (cred pkauth.Client, ch provider.CustomHandlerOpt) {
}
func InitAuth(Config AuthConfig) {
AL = &AuthLogger{}
llog = Log.WithName("Auth")
alog = &authLogger{}
AuthService = &Auth{}
var avatarcachedir string
@ -123,14 +126,14 @@ func InitAuth(Config AuthConfig) {
AvatarResizeLimit: 200, // resizes avatars to 200x200
ClaimsUpd: token.ClaimsUpdFunc(Config.MapClaimsToUser),
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
}
// create auth service
AuthService.Service = pkauth.NewService(options)
if viper.GetBool("auth.dev.enabled") {
Log.Info("Auth Dev Mode Enabled!")
llog.Info("Auth Dev Mode Enabled!")
AuthService.Service.AddProvider("dev", "", "")
// run dev/test oauth2 server on :8084
go func() {
@ -139,7 +142,7 @@ func InitAuth(Config AuthConfig) {
return "admin@example.com"
}
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())
@ -147,24 +150,24 @@ func InitAuth(Config AuthConfig) {
}
if viper.GetBool("auth.github.enabled") {
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 {
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 {
Log.Info("Auth Github Enabled!")
llog.Info("Auth Github Enabled!")
gcred, gch := customGitHubProvider()
AuthService.Service.AddCustomProvider("github", gcred, gch)
}
}
}
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"))
}
/* direct loging (username/password) is always handled */
dbprovider := dbauth.DirectHandler{
L: AL,
L: alog,
ProviderName: "direct",
Issuer: options.Issuer,
TokenService: AuthService.Service.TokenService(),
@ -174,7 +177,7 @@ func InitAuth(Config AuthConfig) {
AuthService.Service.AddCustomHandler(dbprovider)
if viper.GetBool("auth.email.enabled") {
Log.Info("Auth Email Enabled!")
llog.Info("Auth Email Enabled!")
AuthService.Service.AddVerifProvider("email",
"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
@ -186,14 +189,14 @@ func InitAuth(Config AuthConfig) {
if viper.GetBool("auth.telegram.enabled") {
if viper.IsSet("auth.telegram.token") {
Log.Info("Auth Telegram Enabled!")
llog.Info("Auth Telegram Enabled!")
// add telegram provider
telegram := telegramauth.TelegramHandler{
ProviderName: "telegram",
ErrorMsg: "❌ Invalid auth request. Please try clicking link again.",
SuccessMsg: "✅ You have successfully authenticated!",
Telegram: telegramauth.NewTelegramAPI(viper.GetString("auth.telegram.token"), http.DefaultClient),
L: AL,
L: alog,
TokenService: AuthService.Service.TokenService(),
AvatarSaver: AuthService.Service.AvatarProxy(),
}
@ -201,49 +204,49 @@ func InitAuth(Config AuthConfig) {
go func() {
err := telegram.Run(context.Background())
if err != nil {
Log.Error(err, "[PANIC] failed to start telegram")
llog.Error(err, "[PANIC] failed to start telegram")
}
}()
AuthService.Service.AddCustomHandler(&telegram)
} 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)
Log.Info("Auth service started")
llog.Info("Auth service started")
}
func InitCasbin(config AuthConfig) {
cdb, err := gormadapter.NewAdapterByDB(db.Db)
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")
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))
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)
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.EnableAutoSave(true)
AuthService.AuthEnforcer.SetRoleManager(defaultrolemanager.NewRoleManager(10))
//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 {
Log.Error(err, "Failed to Load Casbin Policy")
llog.Error(err, "Failed to Load Casbin Policy")
}
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:user", "apigroup:apps", "GET")
@ -259,21 +262,20 @@ func InitCasbin(config AuthConfig) {
// AuthService.AuthEnforcer.AddRoleForUser("admin", "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()
rm := AuthService.AuthEnforcer.GetPolicy()
Log.Info("Casbin Policy", "policy", rm)
Log.Info("Casbin User Roles", "Roles", AuthService.AuthEnforcer.GetGroupingPolicy())
Log.Info("Casbin API Groups", "API Groups", AuthService.AuthEnforcer.GetNamedGroupingPolicy("g2"))
llog.Info("Casbin Policy", "policy", rm)
llog.Info("Casbin User Roles", "Roles", AuthService.AuthEnforcer.GetGroupingPolicy())
llog.Info("Casbin API Groups", "API Groups", AuthService.AuthEnforcer.GetNamedGroupingPolicy("g2"))
}
func (a *Auth) AddResourceURL(url string, group string) bool {
ok, err := a.AuthEnforcer.AddNamedGroupingPolicy("g2", url, group)
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
}

View file

@ -4,15 +4,16 @@ import (
"io/fs"
"path/filepath"
"strings"
"context"
//"io/ioutil"
"embed"
"fmt"
"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/go-logr/logr"
"github.com/skx/evalfilter/v2"
"github.com/skx/evalfilter/v2/object"
"gorm.io/gorm"
@ -23,6 +24,9 @@ var ScriptFiles embed.FS
type FilterType int
var llog logr.Logger
const (
AppFilter = iota
UserFilter
@ -71,10 +75,10 @@ func loadScriptFiles(files []string, scripttype FilterType) {
var flt *Filter
tx := db.Db.Where("name = ? and type = ?", trimFileExtension(filepath.Base(script)), scripttype).First(&flt)
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)
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
}
//
@ -86,19 +90,20 @@ func loadScriptFiles(files []string, scripttype FilterType) {
Type: scripttype,
}
} 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 {
Log.Info("Loaded Filter Script ", "type", scripttype, "filter", flt.Name)
llog.Info("Loaded Filter Script ", "type", scripttype, "filter", flt.Name)
Filters = append(Filters, flt)
db.Db.Save(flt)
} 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() {
llog = log.Log.WithName("filter")
db.Db.AutoMigrate(&Filter{})
Filters = make([]*Filter, 0)
scripts := filterFiles("scripts/apps", ".scp")
@ -143,13 +148,13 @@ func (ev *Filter) fnPrintf(args []object.Object) object.Object {
// Call the helper
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{}
}
func (ev *Filter) fnPrint(args []object.Object) object.Object {
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{}
}
@ -170,7 +175,7 @@ func (ev *Filter) fnSetField(args []object.Object) object.Object {
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
return &object.Void{}
}
@ -184,11 +189,11 @@ func (ev *Filter) fnClearField(args []object.Object) object.Object {
return &object.Null{}
}
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 {
delete(ev.processedMessage.Body.Fields, fld)
} else {
Log.Info("Field Not Found", "filter", ev.Name, "field", fld)
llog.Info("Field Not Found", "filter", ev.Name, "field", fld)
}
return &object.Void{}
}
@ -202,7 +207,7 @@ func (ev *Filter) fnSetShortMessage(arg []object.Object) object.Object {
return &object.Null{}
}
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
return &object.Void{}
}
@ -216,32 +221,33 @@ func (ev *Filter) fnSetSeverity(arg []object.Object) object.Object {
return &object.Null{}
}
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
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 {
return true, nil
}
defer func() {
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 {
Log.Info("Filter Script Not ready", "filter", ev.Name)
llog.Info("Filter Script Not ready", "filter", ev.Name)
return true, nil
}
ev.processedMessage = msg
ev.script.SetContext(ctx)
ok, err := ev.script.Run(msg.Body)
ev.processedMessage = 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
}
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
}
@ -250,7 +256,7 @@ func (ev *Filter) SetupEvalFilter() error {
// Create an evaluator, with the script inside it.
//
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("print", ev.fnPrint)
ev.script.AddFunction("setfield", ev.fnSetField)
@ -258,11 +264,11 @@ func (ev *Filter) SetupEvalFilter() error {
ev.script.AddFunction("setshortmessage", ev.fnSetShortMessage)
ev.script.AddFunction("setseverity", ev.fnSetSeverity)
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
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
return nil
}

View file

@ -6,17 +6,17 @@ import (
"time"
"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"
httpCheck "github.com/hellofresh/health-go/v4/checks/http"
)
var llog logr.Logger
var HealthChecker health.Checker
func StartHealth() {
llog = log.Log.WithName("health")
HealthChecker = health.NewChecker(
health.WithTimeout(10*time.Second),
//health.WithInterceptors(interceptors.BasicLogger()),
@ -48,7 +48,7 @@ func BasicLogger() health.Interceptor {
return func(ctx context.Context, name string, state health.CheckState) health.CheckState {
now := time.Now()
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)
return result
}

View file

@ -2,21 +2,81 @@ package log
import (
"fmt"
"github.com/danielgtaylor/huma/middleware"
"time"
"path/filepath"
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
var Log logr.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() {
zapLog, err := middleware.NewDefaultLogger()
if err != nil {
panic(fmt.Sprintf("Initilize Logging Failed (%v)?", err))
var cfg zap.Config
var lvl zapcore.Level
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.Info("Logging Started")
zap.RedirectStdLog(zapLog)
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 (
"context"
// "fmt"
"strconv"
"net/http"
"github.com/Fishwaldo/mouthpiece/internal/auth"
@ -17,7 +17,7 @@ import (
type Middleware struct {
}
type CtxUserValue struct{}
// Update user info in request context from go-pkgz/auth token.User to mouthpiece.User
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
if tknuser, err := token.GetUserInfo(r); err == nil {
/* 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)
ctx := huma.ContextFromRequest(w, r)
/* do Something */
ctx.WriteError(http.StatusForbidden, "User not found", err)
ctx.WriteError(http.StatusUnauthorized, "User not found", err)
return
} else {
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)
return
}
r = r.WithContext(context.WithValue(r.Context(), CtxUserValue{}, tknuser))
r = r.WithContext(context.WithValue(r.Context(), user.CtxUserValue{}, tknuser))
}
h.ServeHTTP(w, r)
return
@ -52,3 +54,4 @@ func (a *Middleware) Update() func(http.Handler) http.Handler {
}
return f
}

View file

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

View file

@ -2,6 +2,7 @@ package stdout
import (
"fmt"
"context"
. "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/Fishwaldo/mouthpiece/internal/message"
"github.com/Fishwaldo/mouthpiece/internal/transport"
@ -23,11 +24,11 @@ func (t StdoutTransport) GetName() string {
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.Printf("Message: %s\n", msg.Body.Message)
fmt.Println("=========================================================")
transport.UpdateTransportStatus(t, msg, "sent")
transport.UpdateTransportStatus(ctx, t, msg, "sent")
return nil
}
@ -35,7 +36,7 @@ func (t StdoutTransport) Start() {
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{
// Transport: t.GetName(),
// Config: user.Username,

View file

@ -2,7 +2,7 @@ package telegram
import (
"fmt"
// "os"
"context"
. "github.com/Fishwaldo/mouthpiece/internal/log"
"github.com/Fishwaldo/mouthpiece/internal/message"
@ -73,17 +73,17 @@ func (t TelegramTransport) Start() {
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{
// Transport: t.GetName(),
// 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.Printf("Message: %s\n", msg.Body.Message)
fmt.Println("=========================================================")
transport.UpdateTransportStatus(t, msg, "sent")
transport.UpdateTransportStatus(ctx, t, msg, "sent")
return nil
}

View file

@ -2,6 +2,7 @@ package transport
import (
"errors"
"context"
"github.com/Fishwaldo/mouthpiece/internal/db"
. "github.com/Fishwaldo/mouthpiece/internal/log"
@ -19,8 +20,8 @@ type TransportConfig struct {
type ITransport interface {
GetName() string
Start()
SendMessage(config TransportConfig, message msg.Message) (err error)
NewTransportConfig()
SendMessage(ctx context.Context, config TransportConfig, message msg.Message) (err error)
NewTransportConfig(ctx context.Context)
}
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 {
return t, nil
}
return nil, errors.New("Transport Not Found")
}
func GetTransports() []string {
func GetTransports(ctx context.Context) []string {
var a []string
for k := range transports {
a = append(a, k)
@ -58,6 +59,6 @@ func GetTransports() []string {
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())
}

View file

@ -1,6 +1,9 @@
package user
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/Fishwaldo/mouthpiece/internal/errors"
@ -8,17 +11,20 @@ import (
"github.com/go-pkgz/auth/token"
)
type CtxUserValue struct{}
func dbAuthProvider(user, pass string) (ok bool, err error) {
user = strings.TrimSpace(user)
Log.Info("Direct Login", "user", user, "pass", pass)
dbUser, err := GetUser(user)
Log.Info("Direct Login", "user", user)
dbUser, err := GetUser(context.Background(), user)
Log.Info("User", "user", dbUser, "error", err)
if err == mperror.ErrUserNotFound {
Log.Info("User not found", "user", user)
return false, nil
}
if !dbUser.CheckPassword(pass) {
if !dbUser.CheckPassword(context.Background(), pass) {
Log.Info("Password Invalid", "user", user)
return false, nil
}
@ -27,7 +33,7 @@ func dbAuthProvider(user, pass string) (ok bool, err error) {
// Called when the Tokens are created/refreshed.
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 user, err := GetUser(claims.User.Name); err != nil {
// 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
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 {
return true
if user, _ := GetUser(context.Background(), claims.User.Name); user != nil {
claims.User.ID = fmt.Sprintf("%d", user.ID)
return true
}
}
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
import (
"context"
"fmt"
"golang.org/x/crypto/bcrypt"
"github.com/Fishwaldo/mouthpiece/internal/auth"
"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/message"
msg "github.com/Fishwaldo/mouthpiece/internal/message"
"github.com/Fishwaldo/mouthpiece/internal/transport"
"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()
if err := validate.Struct(user); err != nil {
Log.Info("User Validation Error", "Error", err)
return err
}
tx := db.Db.Omit("Password").Create(&user)
tx := db.Db.WithContext(ctx).Omit("Password").Create(&user)
if tx.Error != nil {
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 */
if err := dbuser.SetPassword(user.Password); err != nil {
if tx := db.Db.Delete(&dbuser); tx.Error != nil {
if err := dbuser.SetPassword(ctx, user.Password); err != nil {
if tx := db.Db.WithContext(ctx).Delete(&dbuser); tx.Error != nil {
Log.Info("Error Deleting User after failed Password", "Error", tx.Error)
return err
}
return err
}
/* New Users all Start with User Role */
if !dbuser.addUserRole("user") {
if !dbuser.addUserRole(ctx, "user") {
Log.Info("Error Adding User Role", "Error", err)
}
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))
if err != nil {
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
}
func (u *User) CheckPassword(password string) bool {
func (u *User) CheckPassword(ctx context.Context, password string) bool {
Log.Info("Checking Password", "email", u.Email)
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
if err != nil {
@ -84,14 +86,14 @@ func (u *User) CheckPassword(password string) bool {
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)
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
Log.Info("Error Generating SetPassword Hash", "Error", 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)
return tx.Error
}
@ -106,49 +108,49 @@ func InitializeUsers() {
if count == 0 {
Log.Info("Creating Default Users")
admin := &User{FirstName: "Admin", LastName: "User", Email: "admin@example.com", Password: "password"}
if err := CreateUser(admin); err == nil {
admin.addUserRole("admin")
if err := CreateUser(context.Background(), admin); err == nil {
admin.addUserRole(context.Background(), "admin")
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")
}
}
}
func GetUsers() []User {
func GetUsers(ctx context.Context) []User {
var users []User
db.Db.Find(&users)
db.Db.WithContext(ctx).Find(&users)
return users
}
func GetUser(email string) (user *User, err error) {
tx := db.Db.Preload(clause.Associations).First(&user, "email = ?", email)
func GetUser(ctx context.Context, email string) (user *User, err error) {
tx := db.Db.WithContext(ctx).Preload(clause.Associations).First(&user, "email = ?", email)
if tx.Error == gorm.ErrRecordNotFound {
return nil, mperror.ErrUserNotFound
}
return
}
func GetUserByID(id uint) (user *User, err error) {
tx := db.Db.Preload(clause.Associations).First(&user, "ID = ?", id)
func GetUserByID(ctx context.Context, id uint) (user *User, err error) {
tx := db.Db.WithContext(ctx).Preload(clause.Associations).First(&user, "ID = ?", id)
if tx.Error == gorm.ErrRecordNotFound {
return nil, mperror.ErrUserNotFound
}
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 */
msg.Body.Fields["first_name"] = u.FirstName
msg.Body.Fields["last_name"] = u.LastName
msg.Body.Fields["email"] = u.Email
Log.V(1).Info("User Processing Message", "Email", u.Email, "MessageID", msg.ID)
for _, tc := range u.TransportConfigs {
t, err := transport.GetTransport(tc.Transport)
t, err := transport.GetTransport(ctx, tc.Transport)
if err != nil {
Log.Info("Cant find Transport", "Transport", tc.Transport)
}
go t.SendMessage(tc, msg)
go t.SendMessage(ctx, tc, msg)
}
return
}

192
main.go
View file

@ -25,28 +25,25 @@ SOFTWARE.
package main
import (
//"fmt"
// "context"
"embed"
"fmt"
"io/fs"
"net/http"
// "reflect"
"strings"
// "unsafe"
"embed"
"encoding/json"
"os"
"runtime/debug"
"os/signal"
"strings"
"syscall"
"time"
"context"
"path/filepath"
"github.com/Fishwaldo/mouthpiece/frontend"
_ "github.com/Fishwaldo/mouthpiece/frontend"
mouthpiece "github.com/Fishwaldo/mouthpiece/internal"
"github.com/Fishwaldo/mouthpiece/internal/app"
"github.com/Fishwaldo/mouthpiece/internal/auth"
"github.com/Fishwaldo/mouthpiece/internal/db"
"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"
"github.com/Fishwaldo/mouthpiece/internal/middleware"
"github.com/Fishwaldo/mouthpiece/internal/transport"
@ -61,14 +58,50 @@ import (
"github.com/go-chi/chi"
"github.com/danielgtaylor/huma"
"github.com/danielgtaylor/huma/cli"
hmw "github.com/danielgtaylor/huma/middleware"
"github.com/danielgtaylor/huma/responses"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
//go:embed config
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() {
viper.SetDefault("frontend.path", "frontend/dist")
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.")
}
//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))
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() {
viper.SetEnvPrefix("MP")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
viper.AutomaticEnv()
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
@ -139,21 +163,51 @@ func main() {
fmt.Println(bi.String())
// 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()
humucli := cli.NewRouter(bi.Name, bi.GitVersion)
humucli.DisableSchemaProperty()
humucli.PreStart(transport.InitializeTransports)
humucli.PreStart(msg.InitializeMessage)
humucli.PreStart(user.InitializeUsers)
humucli.PreStart(app.InitializeApps)
humucli.PreStart(transport.StartTransports)
humucli.PreStart(filter.InitFilter)
// app.PreStart()
humucli.PreStart(healthChecker.StartHealth)
humucli.GatewayClientCredentials("mouthpiece", "/oauth2/token", nil)
humucli.GatewayAuthCode("mouthpiece2", "/oauth2/token", "/oauth2/token", nil)
humucli.GatewayBasicAuth("basic")
hmw.NewLogger = log.GetZapLogger
Server.DisableSchemaProperty()
Server.PreStart(transport.InitializeTransports)
Server.PreStart(msg.InitializeMessage)
Server.PreStart(user.InitializeUsers)
Server.PreStart(app.InitializeApps)
Server.PreStart(transport.StartTransports)
Server.PreStart(filter.InitFilter)
Server.PreStart(healthChecker.StartHealth)
Server.GatewayClientCredentials("mouthpiece", "/oauth2/token", nil)
Server.GatewayAuthCode("mouthpiece2", "/oauth2/token", "/oauth2/token", nil)
Server.GatewayBasicAuth("basic")
user.AuthConfig.Host = fmt.Sprintf("http://arm64-1.dmz.dynam.ac:%v", viper.Get("Port"))
user.AuthConfig.ConfigDir = ConfigFiles
@ -162,26 +216,26 @@ func main() {
p := middleware.Middleware{}
authRoutes, avaRoutes := auth.AuthService.Service.Handlers()
mux := humucli.Resource("/").GetMux()
mux := Server.Resource("/").GetMux()
mux.Mount("/auth", authRoutes)
mux.Mount("/avatar", avaRoutes)
var httpfiles http.FileSystem
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"))
} else {
Log.Info("Serving frontend from Bundled Files")
log.Log.Info("Serving frontend from Bundled Files")
subdir, err := fs.Sub(frontend.FrontEndFiles, "dist")
if err != nil {
Log.Error(err, "Failed to get subdir")
log.Log.Error(err, "Failed to get subdir")
}
httpfiles = http.FS(subdir)
}
fileServer(mux, "/static", httpfiles)
// 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().Headers("Content-Type"),
responses.OK().Model(health.CheckerResult{}),
@ -197,7 +251,7 @@ func main() {
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().Headers("Content-Type"),
responses.OK().Model(&mouthpiece.FEConfig{}),
@ -205,7 +259,7 @@ func main() {
ctx.WriteModel(http.StatusOK, mouthpiece.GetFEConfig())
})
v1api := humucli.Resource("/v1")
v1api := Server.Resource("/v1")
v1api.Middleware(m.Trace)
v1api.Middleware(p.Update())
@ -215,10 +269,10 @@ func main() {
responses.OK().Model(&msg.MessageResult{}),
responses.NotFound().ContentType("application/json"),
).Run(func(ctx huma.Context, input msg.Message) {
Log.Info("Recieved Message", "message", input)
if app.AppExists(input.AppName) {
log.Log.Info("Recieved Message", "message", input)
if app.AppExists(ctx, input.AppName) {
if err := input.ProcessMessage(); err == nil {
mouthpiece.RouteMessage(&input)
mouthpiece.RouteMessage(ctx, &input)
ctx.WriteModel(http.StatusOK, input.Result)
} else {
ctx.WriteError(http.StatusInternalServerError, err.Error())
@ -228,30 +282,11 @@ func main() {
}
})
auth.AuthService.AddResourceURL("/v1/apps/", "apigroup:apps")
appapi := v1api.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.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)
}
})
if err := app.InitializeAppRestAPI(v1api); err != nil {
log.Log.Error(err, "Failed to initialize App Rest API")
}
auth.AuthService.AddResourceURL("/v1/users/", "apigroup:users")
userapi := v1api.SubResource("/users/")
@ -260,7 +295,7 @@ func main() {
responses.OK().Headers("Set-Cookie"),
responses.OK().Model([]user.User{}),
).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")
@ -273,7 +308,7 @@ func main() {
).Run(func(ctx huma.Context, input struct {
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)
} else {
var transport []string
@ -290,11 +325,12 @@ func main() {
responses.OK().Headers("Set-Cookie"),
responses.OK().Model(transport.TransportConfig{}),
responses.NotFound().ContentType("application/json"),
responses.NotFound().Headers("Set-Cookie"),
).Run(func(ctx huma.Context, input struct {
User uint `path:"userid"`
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)
} else {
ok := false
@ -316,9 +352,9 @@ func main() {
responses.OK().Headers("Set-Cookie"),
responses.OK().Model([]string{}),
).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.
humucli.Run()
Server.Run()
}