diff --git a/Makefile b/Makefile index 67f19d017..d74d8fe9d 100644 --- a/Makefile +++ b/Makefile @@ -51,8 +51,8 @@ test_system: go test -v ./tests/plugins/cgminer go test -v ./tests/plugins/email go test -v ./tests/plugins/messagebird - #go test -v ./tests/plugins/modbus_rtu - #go test -v ./tests/plugins/modbus_tcp + go test -v ./tests/plugins/modbus_rtu + go test -v ./tests/plugins/modbus_tcp go test -v ./tests/plugins/moon go test -v ./tests/plugins/node go test -v ./tests/plugins/scene diff --git a/Makefile.local b/Makefile.local index 110e45440..9262a73f7 100644 --- a/Makefile.local +++ b/Makefile.local @@ -53,8 +53,8 @@ test_system: go test -v ./tests/plugins/cgminer go test -v ./tests/plugins/email go test -v ./tests/plugins/messagebird - #go test -v ./tests/plugins/modbus_rtu - #go test -v ./tests/plugins/modbus_tcp + go test -v ./tests/plugins/modbus_rtu + go test -v ./tests/plugins/modbus_tcp go test -v ./tests/plugins/moon go test -v ./tests/plugins/node go test -v ./tests/plugins/scene diff --git a/adaptors/action.go b/adaptors/action.go index 1c16786ee..b7ff9162a 100644 --- a/adaptors/action.go +++ b/adaptors/action.go @@ -36,7 +36,7 @@ type IAction interface { GetById(ctx context.Context, id int64) (metric *m.Action, err error) Update(ctx context.Context, ver *m.Action) error Delete(ctx context.Context, deviceId int64) (err error) - List(ctx context.Context, limit, offset int64, orderBy, sort string) (list []*m.Action, total int64, err error) + List(ctx context.Context, limit, offset int64, orderBy, sort string, ids *[]uint64) (list []*m.Action, total int64, err error) Search(ctx context.Context, query string, limit, offset int) (list []*m.Action, total int64, err error) fromDb(dbVer *db.Action) (ver *m.Action) toDb(ver *m.Action) (dbVer *db.Action) @@ -97,9 +97,9 @@ func (n *Action) Delete(ctx context.Context, deviceId int64) (err error) { } // List ... -func (n *Action) List(ctx context.Context, limit, offset int64, orderBy, sort string) (list []*m.Action, total int64, err error) { +func (n *Action) List(ctx context.Context, limit, offset int64, orderBy, sort string, ids *[]uint64) (list []*m.Action, total int64, err error) { var dbList []*db.Action - if dbList, total, err = n.table.List(ctx, int(limit), int(offset), orderBy, sort); err != nil { + if dbList, total, err = n.table.List(ctx, int(limit), int(offset), orderBy, sort, ids); err != nil { return } diff --git a/adaptors/condition.go b/adaptors/condition.go index 905eab614..ff74c7dc9 100644 --- a/adaptors/condition.go +++ b/adaptors/condition.go @@ -35,7 +35,7 @@ type ICondition interface { GetById(ctx context.Context, id int64) (metric *m.Condition, err error) Update(ctx context.Context, ver *m.Condition) error Delete(ctx context.Context, deviceId int64) (err error) - List(ctx context.Context, limit, offset int64, orderBy, sort string) (list []*m.Condition, total int64, err error) + List(ctx context.Context, limit, offset int64, orderBy, sort string, ids *[]uint64) (list []*m.Condition, total int64, err error) Search(ctx context.Context, query string, limit, offset int) (list []*m.Condition, total int64, err error) fromDb(dbVer *db.Condition) (ver *m.Condition) toDb(ver *m.Condition) (dbVer *db.Condition) @@ -94,9 +94,9 @@ func (n *Condition) Delete(ctx context.Context, deviceId int64) (err error) { } // List ... -func (n *Condition) List(ctx context.Context, limit, offset int64, orderBy, sort string) (list []*m.Condition, total int64, err error) { +func (n *Condition) List(ctx context.Context, limit, offset int64, orderBy, sort string, ids *[]uint64) (list []*m.Condition, total int64, err error) { var dbList []*db.Condition - if dbList, total, err = n.table.List(ctx, int(limit), int(offset), orderBy, sort); err != nil { + if dbList, total, err = n.table.List(ctx, int(limit), int(offset), orderBy, sort, ids); err != nil { return } diff --git a/adaptors/entity.go b/adaptors/entity.go index 4dc7c1dfb..b7f682da6 100644 --- a/adaptors/entity.go +++ b/adaptors/entity.go @@ -504,6 +504,10 @@ func (n *Entity) fromDb(dbVer *db.Entity) (ver *m.Entity) { data := map[string]interface{}{} _ = json.Unmarshal(dbVer.Storage[0].Attributes, &data) _, _ = ver.Attributes.Deserialize(data) + storageAdaptor := GetEntityStorageAdaptor(n.db) + for _, store := range dbVer.Storage { + ver.Storage = append(ver.Storage, storageAdaptor.fromDb(store)) + } } return diff --git a/adaptors/entity_storage.go b/adaptors/entity_storage.go index c924dc824..77b8988bd 100644 --- a/adaptors/entity_storage.go +++ b/adaptors/entity_storage.go @@ -33,10 +33,12 @@ import ( type IEntityStorage interface { Add(ctx context.Context, ver *m.EntityStorage) (id int64, err error) GetLastByEntityId(ctx context.Context, entityId common.EntityId) (ver *m.EntityStorage, err error) - List(ctx context.Context, limit, offset int64, orderBy, sort string, entityIds []common.EntityId, startDate, endDate *time.Time) (list []*m.EntityStorage, total int64, err error) + List(ctx context.Context, limit, offset int64, orderBy, sort string, + entityIds []common.EntityId, + startDate, endDate *time.Time) (list []*m.EntityStorage, total int64, err error) DeleteOldest(ctx context.Context, days int) (err error) - fromDb(dbVer db.EntityStorage) (ver *m.EntityStorage) - toDb(ver *m.EntityStorage) (dbVer db.EntityStorage) + fromDb(dbVer *db.EntityStorage) (ver *m.EntityStorage) + toDb(ver *m.EntityStorage) (dbVer *db.EntityStorage) } // EntityStorage ... @@ -62,7 +64,7 @@ func (n *EntityStorage) Add(ctx context.Context, ver *m.EntityStorage) (id int64 // GetLastByEntityId ... func (n *EntityStorage) GetLastByEntityId(ctx context.Context, entityId common.EntityId) (ver *m.EntityStorage, err error) { - var dbVer db.EntityStorage + var dbVer *db.EntityStorage if dbVer, err = n.table.GetLastByEntityId(ctx, entityId); err != nil { return } @@ -72,7 +74,7 @@ func (n *EntityStorage) GetLastByEntityId(ctx context.Context, entityId common.E // ListByEntityId ... func (n *EntityStorage) List(ctx context.Context, limit, offset int64, orderBy, sort string, entityIds []common.EntityId, startDate, endDate *time.Time) (list []*m.EntityStorage, total int64, err error) { - var dbList []db.EntityStorage + var dbList []*db.EntityStorage if dbList, total, err = n.table.List(ctx, int(limit), int(offset), orderBy, sort, entityIds, startDate, endDate); err != nil { return } @@ -90,7 +92,7 @@ func (n *EntityStorage) DeleteOldest(ctx context.Context, days int) (err error) return } -func (n *EntityStorage) fromDb(dbVer db.EntityStorage) (ver *m.EntityStorage) { +func (n *EntityStorage) fromDb(dbVer *db.EntityStorage) (ver *m.EntityStorage) { ver = &m.EntityStorage{ Id: dbVer.Id, EntityId: dbVer.EntityId, @@ -106,8 +108,8 @@ func (n *EntityStorage) fromDb(dbVer db.EntityStorage) (ver *m.EntityStorage) { return } -func (n *EntityStorage) toDb(ver *m.EntityStorage) (dbVer db.EntityStorage) { - dbVer = db.EntityStorage{ +func (n *EntityStorage) toDb(ver *m.EntityStorage) (dbVer *db.EntityStorage) { + dbVer = &db.EntityStorage{ Id: ver.Id, EntityId: ver.EntityId, State: ver.State, diff --git a/adaptors/script.go b/adaptors/script.go index 93372271d..9c3049985 100644 --- a/adaptors/script.go +++ b/adaptors/script.go @@ -35,7 +35,7 @@ type IScript interface { GetByName(ctx context.Context, name string) (script *m.Script, err error) Update(ctx context.Context, script *m.Script) (err error) Delete(ctx context.Context, scriptId int64) (err error) - List(ctx context.Context, limit, offset int64, orderBy, sort string, query *string) (list []*m.Script, total int64, err error) + List(ctx context.Context, limit, offset int64, orderBy, sort string, query *string, ids *[]uint64) (list []*m.Script, total int64, err error) Search(ctx context.Context, query string, limit, offset int64) (list []*m.Script, total int64, err error) Statistic(ctx context.Context) (statistic *m.ScriptsStatistic, err error) fromDb(dbScript *db.Script) (script *m.Script, err error) @@ -104,7 +104,7 @@ func (n *Script) Delete(ctx context.Context, scriptId int64) (err error) { } // List ... -func (n *Script) List(ctx context.Context, limit, offset int64, orderBy, sort string, query *string) (list []*m.Script, total int64, err error) { +func (n *Script) List(ctx context.Context, limit, offset int64, orderBy, sort string, query *string, ids *[]uint64) (list []*m.Script, total int64, err error) { if sort == "" { sort = "id" @@ -114,7 +114,7 @@ func (n *Script) List(ctx context.Context, limit, offset int64, orderBy, sort st } var dbList []*db.Script - if dbList, total, err = n.table.List(ctx, int(limit), int(offset), orderBy, sort, query); err != nil { + if dbList, total, err = n.table.List(ctx, int(limit), int(offset), orderBy, sort, query, ids); err != nil { return } diff --git a/adaptors/trigger.go b/adaptors/trigger.go index fbdcf7730..00b444203 100644 --- a/adaptors/trigger.go +++ b/adaptors/trigger.go @@ -38,7 +38,7 @@ type ITrigger interface { Update(ctx context.Context, ver *m.UpdateTrigger) error Delete(ctx context.Context, deviceId int64) (err error) List(ctx context.Context, limit, offset int64, orderBy, sort string, onlyEnabled bool) (list []*m.Trigger, total int64, err error) - ListPlain(ctx context.Context, limit, offset int64, orderBy, sort string, onlyEnabled bool) (list []*m.Trigger, total int64, err error) + ListPlain(ctx context.Context, limit, offset int64, orderBy, sort string, onlyEnabled bool, ids *[]uint64) (list []*m.Trigger, total int64, err error) Search(ctx context.Context, query string, limit, offset int) (list []*m.Trigger, total int64, err error) Enable(ctx context.Context, id int64) (err error) Disable(ctx context.Context, id int64) (err error) @@ -184,9 +184,9 @@ func (n *Trigger) List(ctx context.Context, limit, offset int64, orderBy, sort s } // ListPlain ... -func (n *Trigger) ListPlain(ctx context.Context, limit, offset int64, orderBy, sort string, onlyEnabled bool) (list []*m.Trigger, total int64, err error) { +func (n *Trigger) ListPlain(ctx context.Context, limit, offset int64, orderBy, sort string, onlyEnabled bool, ids *[]uint64) (list []*m.Trigger, total int64, err error) { var dbList []*db.Trigger - if dbList, total, err = n.table.ListPlain(ctx, int(limit), int(offset), orderBy, sort, onlyEnabled); err != nil { + if dbList, total, err = n.table.ListPlain(ctx, int(limit), int(offset), orderBy, sort, onlyEnabled, ids); err != nil { return } diff --git a/adaptors/user.go b/adaptors/user.go index b52c796f3..a3a12e76e 100644 --- a/adaptors/user.go +++ b/adaptors/user.go @@ -25,12 +25,12 @@ import ( "time" "unicode/utf8" - "github.com/e154/smart-home/common/apperr" + "gorm.io/gorm" "github.com/e154/smart-home/common" + "github.com/e154/smart-home/common/apperr" "github.com/e154/smart-home/db" m "github.com/e154/smart-home/models" - "gorm.io/gorm" ) // IUser ... @@ -229,10 +229,10 @@ func (n *User) SignIn(ctx context.Context, u *m.User, ipv4 string) (err error) { // update time lastT := u.CurrentSignInAt - currentT := time.Now() + now := time.Now() u.LastSignInAt = lastT - u.CurrentSignInAt = ¤tT + u.CurrentSignInAt = &now // update ipv4 lastIp := u.CurrentSignInIp @@ -240,7 +240,7 @@ func (n *User) SignIn(ctx context.Context, u *m.User, ipv4 string) (err error) { u.LastSignInIp = lastIp u.CurrentSignInIp = currentIp - u.UpdateHistory(currentT, currentIp) + u.UpdateHistory(now, currentIp) dbUser := n.toDb(u) err = n.table.Update(ctx, dbUser) @@ -320,7 +320,7 @@ func (n *User) fromDb(dbUser *db.User) (user *m.User) { } } - // history + // deserialize history user.History = make([]*m.UserHistory, 0) data, _ := dbUser.History.MarshalJSON() _ = json.Unmarshal(data, &user.History) @@ -373,5 +373,9 @@ func (n *User) toDb(user *m.User) (dbUser *db.User) { _ = dbUser.UserId.Scan(user.UserId.Int64) } + // serialize history + b, _ := json.Marshal(user.History) + _ = dbUser.History.UnmarshalJSON(b) + return } diff --git a/api/api.go b/api/api.go index d46b50b45..04297975c 100644 --- a/api/api.go +++ b/api/api.go @@ -73,6 +73,7 @@ func (a *Api) Start() (err error) { Skipper: middleware.DefaultSkipper, Limit: "128M", })) + a.echo.Use(controllers.NewMiddlewareContextValue) a.echo.Use(middleware.Recover()) if a.cfg.Debug { @@ -120,7 +121,6 @@ func (a *Api) Start() (err error) { a.eventBus.Publish("system/services/api", events.EventServiceStarted{Service: "Api"}) - //return group.Wait() return nil } @@ -154,9 +154,6 @@ func (a *Api) registerHandlers() { a.echo.GET("/api.swagger3.yaml", contentHandler) } - // base api - //a.echo.Any("/v1/*", echo.WrapHandler(grpcHandlerFunc(a.grpcServer, mux))) - wrapper := stub.ServerInterfaceWrapper{ Handler: a.controllers, } @@ -312,7 +309,6 @@ func (a *Api) registerHandlers() { // static files a.echo.GET("/", echo.WrapHandler(a.controllers.Index(publicAssets.F))) - //a.echo.Any("/v1/image/upload", a.echoFilter.Auth(a.controllers.ImageServiceMuxUploadImage)) //Auth a.echo.GET("/public/*", echo.WrapHandler(http.StripPrefix("/", http.FileServer(http.FS(publicAssets.F))))) fileServer := http.FileServer(http.Dir("./data/file_storage")) a.echo.Any("/upload/*", echo.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -327,13 +323,19 @@ func (a *Api) registerHandlers() { staticServer.ServeHTTP(w, r) }))) snapshotServer := http.FileServer(http.Dir("./snapshots")) - //a.echo.Any("/v1/backup/upload", a.echo - //Filter.Auth(a.controllers.BackupServiceMuxUploadBackup)) //Auth a.echo.GET("/snapshots/*", a.echoFilter.Auth(echo.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r.RequestURI = strings.ReplaceAll(r.RequestURI, "/snapshots/", "/") r.URL, _ = r.URL.Parse(r.RequestURI) snapshotServer.ServeHTTP(w, r) })))) + // webdav + webdav := echo.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + //r.RequestURI = strings.ReplaceAll(r.RequestURI, "/webdav/", "/") + //r.URL, _ = r.URL.Parse(r.RequestURI) + a.controllers.Webdav(w, r) + })) + a.echo.Any("/webdav", webdav) + a.echo.Any("/webdav/*", webdav) // media a.echo.Any("/stream/:entity_id/channel/:channel/mse", a.echoFilter.Auth(a.controllers.StreamMSE)) //Auth diff --git a/api/api.swagger3.yaml b/api/api.swagger3.yaml index e7be9d2c1..7b0224463 100644 --- a/api/api.swagger3.yaml +++ b/api/api.swagger3.yaml @@ -198,6 +198,7 @@ paths: - $ref: '#/components/parameters/listSort' - $ref: '#/components/parameters/listPage' - $ref: '#/components/parameters/listLimit' + - $ref: '#/components/parameters/ids' responses: 200: description: A successful response. @@ -700,6 +701,7 @@ paths: - $ref: '#/components/parameters/listSort' - $ref: '#/components/parameters/listPage' - $ref: '#/components/parameters/listLimit' + - $ref: '#/components/parameters/ids' responses: 200: description: A successful response. @@ -1879,7 +1881,7 @@ paths: - $ref: '#/components/parameters/listLimit' - $ref: '#/components/parameters/startDate' - $ref: '#/components/parameters/endDate' - - $ref: '#/components/parameters/entityId' + - $ref: '#/components/parameters/entityIds' responses: 200: description: A successful response. @@ -2948,6 +2950,7 @@ paths: - $ref: '#/components/parameters/listSort' - $ref: '#/components/parameters/listPage' - $ref: '#/components/parameters/listLimit' + - $ref: '#/components/parameters/ids' - $ref: '#/components/parameters/query' responses: 200: @@ -3438,6 +3441,7 @@ paths: - $ref: '#/components/parameters/listSort' - $ref: '#/components/parameters/listPage' - $ref: '#/components/parameters/listLimit' + - $ref: '#/components/parameters/ids' responses: 200: description: A successful response. @@ -4287,6 +4291,16 @@ components: type: integer format: uint64 description: The number of results returned on a page + ids: + name: ids[] + in: query + required: false + schema: + type: array + items: + type: integer + format: uint64 + description: The number of results returned on a page startDate: name: startDate in: query @@ -4301,12 +4315,14 @@ components: schema: type: string format: date-time - entityId: - name: entityId + entityIds: + name: entityId[] in: query required: false schema: - type: string + type: array + items: + type: string query: name: query in: query diff --git a/api/controllers/action.go b/api/controllers/action.go index 3ffc079f2..98786cf67 100644 --- a/api/controllers/action.go +++ b/api/controllers/action.go @@ -83,7 +83,7 @@ func (c ControllerAction) ActionServiceGetActionById(ctx echo.Context, id int64) func (c ControllerAction) ActionServiceGetActionList(ctx echo.Context, params stub.ActionServiceGetActionListParams) error { pagination := c.Pagination(params.Page, params.Limit, params.Sort) - items, total, err := c.endpoint.Action.GetList(ctx.Request().Context(), pagination) + items, total, err := c.endpoint.Action.GetList(ctx.Request().Context(), pagination, params.Ids) if err != nil { return c.ERROR(ctx, err) } diff --git a/api/controllers/backup.go b/api/controllers/backup.go index 955cbb03c..4585a3030 100644 --- a/api/controllers/backup.go +++ b/api/controllers/backup.go @@ -97,7 +97,10 @@ func (c ControllerBackup) BackupServiceUploadBackup(ctx echo.Context, _ stub.Bac return c.ERROR(ctx, apperr.ErrInvalidRequest) } - list, errs := c.endpoint.Backup.Upload(r.Context(), form.File) + list, errs, err := c.endpoint.Backup.Upload(r.Context(), form.File) + if err != nil { + return c.ERROR(ctx, err) + } var resultBackups = make([]interface{}, 0) diff --git a/api/controllers/common.go b/api/controllers/common.go index 3feb224e8..0db02c642 100644 --- a/api/controllers/common.go +++ b/api/controllers/common.go @@ -1,6 +1,7 @@ package controllers import ( + "context" "encoding/base64" "encoding/json" "io" @@ -84,6 +85,20 @@ func (c ControllerCommon) HTTP401(ctx echo.Context, err error) error { })) } +// HTTP403 ... +func (c ControllerCommon) HTTP403(ctx echo.Context, err error) error { + e := apperr.GetError(err) + if e != nil { + return ctx.JSON(http.StatusForbidden, ResponseWithError(ctx, &ErrorBase{ + Code: common.String(e.Code()), + Message: common.String(e.Message()), + })) + } + return ctx.JSON(http.StatusForbidden, ResponseWithError(ctx, &ErrorBase{ + Code: common.String("ACCESS_FORBIDDEN"), + })) +} + // HTTP404 ... func (c ControllerCommon) HTTP404(ctx echo.Context, err error) error { code := common.String("NOT_FOUND") @@ -244,20 +259,22 @@ func (c ControllerCommon) Search(query *string, limit, offset *int64) (search co // ERROR ... func (c ControllerCommon) ERROR(ctx echo.Context, err error) error { switch { - case errors.Is(err, apperr.ErrAlreadyExists): - return c.HTTP409(ctx, err) - case errors.Is(err, apperr.ErrInternal): - return c.HTTP500(ctx, err) - case errors.Is(err, apperr.ErrInvalidRequest): - return c.HTTP422(ctx, err) - case errors.Is(err, apperr.ErrNotFound): - return c.HTTP404(ctx, err) - case errors.Is(err, apperr.ErrAccessDenied): - return c.HTTP401(ctx, err) case errors.Is(err, apperr.ErrUnknownField): return c.HTTP400(ctx, err) case errors.Is(err, apperr.ErrBadJSONRequest): return c.HTTP400(ctx, err) + case errors.Is(err, apperr.ErrAccessDenied): + return c.HTTP401(ctx, err) + case errors.Is(err, apperr.ErrAccessForbidden): + return c.HTTP403(ctx, err) + case errors.Is(err, apperr.ErrNotFound): + return c.HTTP404(ctx, err) + case errors.Is(err, apperr.ErrAlreadyExists): + return c.HTTP409(ctx, err) + case errors.Is(err, apperr.ErrInvalidRequest): + return c.HTTP422(ctx, err) + case errors.Is(err, apperr.ErrInternal): + return c.HTTP500(ctx, err) default: var bodyStr string body, _ := io.ReadAll(ctx.Request().Body) @@ -299,3 +316,28 @@ func (c ControllerCommon) parseBasicAuth(auth string) (username, password string return cs[:s], cs[s+1:], true } + +type contextValue struct { + echo.Context +} + +func NewMiddlewareContextValue(fn echo.HandlerFunc) echo.HandlerFunc { + return func(ctx echo.Context) error { + return fn(contextValue{ctx}) + } +} + +// Get retrieves data from the context. +func (ctx contextValue) Get(key string) interface{} { + // get old context value + val := ctx.Context.Get(key) + if val != nil { + return val + } + return ctx.Request().Context().Value(key) +} + +// Set saves data in the context. +func (ctx contextValue) Set(key string, val interface{}) { + ctx.SetRequest(ctx.Request().WithContext(context.WithValue(ctx.Request().Context(), key, val))) +} diff --git a/api/controllers/condition.go b/api/controllers/condition.go index edf982f95..332da1042 100644 --- a/api/controllers/condition.go +++ b/api/controllers/condition.go @@ -83,7 +83,7 @@ func (c ControllerCondition) ConditionServiceGetConditionById(ctx echo.Context, func (c ControllerCondition) ConditionServiceGetConditionList(ctx echo.Context, params stub.ConditionServiceGetConditionListParams) error { pagination := c.Pagination(params.Page, params.Limit, params.Sort) - items, total, err := c.endpoint.Condition.GetList(ctx.Request().Context(), pagination) + items, total, err := c.endpoint.Condition.GetList(ctx.Request().Context(), pagination, params.Ids) if err != nil { return c.ERROR(ctx, err) } diff --git a/api/controllers/controllers.go b/api/controllers/controllers.go index b99d8e9af..d379070a3 100644 --- a/api/controllers/controllers.go +++ b/api/controllers/controllers.go @@ -57,6 +57,7 @@ type Controllers struct { *ControllerIndex *ControllerMqtt *ControllerMedia + *ControllerWebdav } // NewControllers ... @@ -97,5 +98,6 @@ func NewControllers( ControllerIndex: NewControllerIndex(common), ControllerMqtt: NewControllerMqtt(common), ControllerMedia: NewControllerMedia(common), + ControllerWebdav: NewControllerWebdav(common), } } diff --git a/api/controllers/entity_storage.go b/api/controllers/entity_storage.go index 633cefec2..5c04245c0 100644 --- a/api/controllers/entity_storage.go +++ b/api/controllers/entity_storage.go @@ -19,8 +19,6 @@ package controllers import ( - "strings" - "github.com/e154/smart-home/api/stub" "github.com/labstack/echo/v4" @@ -46,8 +44,7 @@ func (c ControllerEntityStorage) EntityStorageServiceGetEntityStorageList(ctx ec var entityIds []common.EntityId if params.EntityId != nil { - arr := strings.Split(*params.EntityId, ",") - for _, item := range arr { + for _, item := range *params.EntityId { entityIds = append(entityIds, common.EntityId(item)) } } diff --git a/api/controllers/script.go b/api/controllers/script.go index 05df9f258..3a7b5daac 100644 --- a/api/controllers/script.go +++ b/api/controllers/script.go @@ -84,7 +84,7 @@ func (c ControllerScript) ScriptServiceUpdateScriptById(ctx echo.Context, id int func (c ControllerScript) ScriptServiceGetScriptList(ctx echo.Context, params stub.ScriptServiceGetScriptListParams) error { pagination := c.Pagination(params.Page, params.Limit, params.Sort) - items, total, err := c.endpoint.Script.GetList(ctx.Request().Context(), pagination, params.Query) + items, total, err := c.endpoint.Script.GetList(ctx.Request().Context(), pagination, params.Query, params.Ids) if err != nil { return c.ERROR(ctx, err) } diff --git a/api/controllers/trigger.go b/api/controllers/trigger.go index 6359b4f10..58ae38b52 100644 --- a/api/controllers/trigger.go +++ b/api/controllers/trigger.go @@ -83,7 +83,7 @@ func (c ControllerTrigger) TriggerServiceGetTriggerById(ctx echo.Context, id int func (c ControllerTrigger) TriggerServiceGetTriggerList(ctx echo.Context, params stub.TriggerServiceGetTriggerListParams) error { pagination := c.Pagination(params.Page, params.Limit, params.Sort) - items, total, err := c.endpoint.Trigger.GetList(ctx.Request().Context(), pagination) + items, total, err := c.endpoint.Trigger.GetList(ctx.Request().Context(), pagination, params.Ids) if err != nil { return c.ERROR(ctx, err) } diff --git a/api/controllers/webdav.go b/api/controllers/webdav.go new file mode 100644 index 000000000..2b14dd977 --- /dev/null +++ b/api/controllers/webdav.go @@ -0,0 +1,40 @@ +// This file is part of the Smart Home +// Program complex distribution https://github.com/e154/smart-home +// Copyright (C) 2024, Filippov Alex +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library. If not, see +// . + +package controllers + +import ( + "net/http" +) + +// ControllerWebdav ... +type ControllerWebdav struct { + *ControllerCommon +} + +// NewControllerWebdav ... +func NewControllerWebdav(common *ControllerCommon) *ControllerWebdav { + return &ControllerWebdav{ + ControllerCommon: common, + } +} + +// Webdav ... +func (c ControllerWebdav) Webdav(w http.ResponseWriter, r *http.Request) error { + return c.endpoint.Webdav.Webdav(w, r) +} diff --git a/api/dto/script.go b/api/dto/script.go index 88771bef3..b9368478d 100644 --- a/api/dto/script.go +++ b/api/dto/script.go @@ -83,8 +83,12 @@ func (s Script) ToSearchResult(list []*m.Script) *stub.ApiSearchScriptListResult items := make([]stub.ApiScript, 0, len(list)) - for _, i := range list { - items = append(items, *s.GetStubScript(i)) + for _, script := range list { + items = append(items, stub.ApiScript{ + Id: script.Id, + Lang: string(script.Lang), + Name: script.Name, + }) } return &stub.ApiSearchScriptListResult{ diff --git a/api/stub/server.go b/api/stub/server.go index f1a8b90aa..4e65b4d99 100644 --- a/api/stub/server.go +++ b/api/stub/server.go @@ -606,6 +606,13 @@ func (w *ServerInterfaceWrapper) ActionServiceGetActionList(ctx echo.Context) er return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } + // ------------- Optional query parameter "ids[]" ------------- + + err = runtime.BindQueryParameter("form", true, false, "ids[]", ctx.QueryParams(), ¶ms.Ids) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter ids[]: %s", err)) + } + // Invoke the callback with all the unmarshalled arguments err = w.Handler.ActionServiceGetActionList(ctx, params) return err @@ -1166,6 +1173,13 @@ func (w *ServerInterfaceWrapper) ConditionServiceGetConditionList(ctx echo.Conte return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } + // ------------- Optional query parameter "ids[]" ------------- + + err = runtime.BindQueryParameter("form", true, false, "ids[]", ctx.QueryParams(), ¶ms.Ids) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter ids[]: %s", err)) + } + // Invoke the callback with all the unmarshalled arguments err = w.Handler.ConditionServiceGetConditionList(ctx, params) return err @@ -2319,11 +2333,11 @@ func (w *ServerInterfaceWrapper) EntityStorageServiceGetEntityStorageList(ctx ec return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter endDate: %s", err)) } - // ------------- Optional query parameter "entityId" ------------- + // ------------- Optional query parameter "entityId[]" ------------- - err = runtime.BindQueryParameter("form", true, false, "entityId", ctx.QueryParams(), ¶ms.EntityId) + err = runtime.BindQueryParameter("form", true, false, "entityId[]", ctx.QueryParams(), ¶ms.EntityId) if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter entityId: %s", err)) + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter entityId[]: %s", err)) } // Invoke the callback with all the unmarshalled arguments @@ -3474,6 +3488,13 @@ func (w *ServerInterfaceWrapper) ScriptServiceGetScriptList(ctx echo.Context) er return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } + // ------------- Optional query parameter "ids[]" ------------- + + err = runtime.BindQueryParameter("form", true, false, "ids[]", ctx.QueryParams(), ¶ms.Ids) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter ids[]: %s", err)) + } + // ------------- Optional query parameter "query" ------------- err = runtime.BindQueryParameter("form", true, false, "query", ctx.QueryParams(), ¶ms.Query) @@ -3893,6 +3914,13 @@ func (w *ServerInterfaceWrapper) TriggerServiceGetTriggerList(ctx echo.Context) return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) } + // ------------- Optional query parameter "ids[]" ------------- + + err = runtime.BindQueryParameter("form", true, false, "ids[]", ctx.QueryParams(), ¶ms.Ids) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter ids[]: %s", err)) + } + // Invoke the callback with all the unmarshalled arguments err = w.Handler.TriggerServiceGetTriggerList(ctx, params) return err diff --git a/api/stub/types.go b/api/stub/types.go index f9d1b1642..986935503 100644 --- a/api/stub/types.go +++ b/api/stub/types.go @@ -1314,8 +1314,11 @@ type AcceptJSON = string // EndDate defines model for endDate. type EndDate = time.Time -// EntityId defines model for entityId. -type EntityId = string +// EntityIds defines model for entityIds. +type EntityIds = []string + +// Ids defines model for ids. +type Ids = []uint64 // ListLimit defines model for listLimit. type ListLimit = uint64 @@ -1405,6 +1408,9 @@ type ActionServiceGetActionListParams struct { // Limit The number of results returned on a page Limit *ListLimit `form:"limit,omitempty" json:"limit,omitempty"` + + // Ids The number of results returned on a page + Ids *Ids `form:"ids[],omitempty" json:"ids[],omitempty"` } // ActionServiceSearchActionParams defines parameters for ActionServiceSearchAction. @@ -1526,6 +1532,9 @@ type ConditionServiceGetConditionListParams struct { // Limit The number of results returned on a page Limit *ListLimit `form:"limit,omitempty" json:"limit,omitempty"` + + // Ids The number of results returned on a page + Ids *Ids `form:"ids[],omitempty" json:"ids[],omitempty"` } // ConditionServiceSearchConditionParams defines parameters for ConditionServiceSearchCondition. @@ -1788,7 +1797,7 @@ type EntityStorageServiceGetEntityStorageListParams struct { Limit *ListLimit `form:"limit,omitempty" json:"limit,omitempty"` StartDate *StartDate `form:"startDate,omitempty" json:"startDate,omitempty"` EndDate *EndDate `form:"endDate,omitempty" json:"endDate,omitempty"` - EntityId *EntityId `form:"entityId,omitempty" json:"entityId,omitempty"` + EntityId *EntityIds `form:"entityId[],omitempty" json:"entityId[],omitempty"` } // ImageServiceAddImageParams defines parameters for ImageServiceAddImage. @@ -2027,7 +2036,10 @@ type ScriptServiceGetScriptListParams struct { // Limit The number of results returned on a page Limit *ListLimit `form:"limit,omitempty" json:"limit,omitempty"` - Query *Query `form:"query,omitempty" json:"query,omitempty"` + + // Ids The number of results returned on a page + Ids *Ids `form:"ids[],omitempty" json:"ids[],omitempty"` + Query *Query `form:"query,omitempty" json:"query,omitempty"` } // ScriptServiceSearchScriptParams defines parameters for ScriptServiceSearchScript. @@ -2109,6 +2121,9 @@ type TriggerServiceGetTriggerListParams struct { // Limit The number of results returned on a page Limit *ListLimit `form:"limit,omitempty" json:"limit,omitempty"` + + // Ids The number of results returned on a page + Ids *Ids `form:"ids[],omitempty" json:"ids[],omitempty"` } // TriggerServiceSearchTriggerParams defines parameters for TriggerServiceSearchTrigger. diff --git a/bin/docker/Dockerfile b/bin/docker/Dockerfile index 4d76fe9c9..570386257 100644 --- a/bin/docker/Dockerfile +++ b/bin/docker/Dockerfile @@ -51,10 +51,11 @@ ENV API_HTTP_PORT="3001" ENV API_SWAGGER="true" ENV API_GZIP="false" ENV LANG="EN" -ENV GOD_MODE="false" +ENV ROOT_MODE="false" ENV DOMAIN="localhost" ENV PPROF="false" ENV HTTPS="false" +ENV ROOT_SECRET="" ENV GATE_API_HTTP_PORT="8080" ENV GATE_API_HTTPS_PORT="8443" diff --git a/cmd/server/container/container.go b/cmd/server/container/container.go index c00c20f3e..c05d86f7c 100644 --- a/cmd/server/container/container.go +++ b/cmd/server/container/container.go @@ -47,6 +47,7 @@ import ( "github.com/e154/smart-home/system/stream" "github.com/e154/smart-home/system/stream/handlers" "github.com/e154/smart-home/system/supervisor" + "github.com/e154/smart-home/system/terminal" "github.com/e154/smart-home/system/validation" "github.com/e154/smart-home/system/zigbee2mqtt" "go.uber.org/fx" @@ -72,6 +73,8 @@ func BuildContainer(opt fx.Option) (app *fx.App) { logging.NewLogger, logging_db.NewLogDbSaver, logging_ws.NewLogWsSaver, + terminal.GetTerminalCommands, + terminal.NewTerminal, scripts.NewScriptService, MigrationList, localMigrations.NewMigrations, diff --git a/cmd/server/container/init.go b/cmd/server/container/init.go index 3365f0dfe..b4ce7d861 100644 --- a/cmd/server/container/init.go +++ b/cmd/server/container/init.go @@ -56,5 +56,6 @@ func MigrationList(adaptors *adaptors.Adaptors, local_migrations.NewMigrationGate(adaptors), local_migrations.NewMigrationAddVar1(adaptors), local_migrations.NewMigrationUpdatePermissions(adaptors, accessList, orm), + local_migrations.NewMigrationWebdav(adaptors), } } diff --git a/common/apperr/types.go b/common/apperr/types.go index 18ae45ee2..444c26a0d 100644 --- a/common/apperr/types.go +++ b/common/apperr/types.go @@ -33,6 +33,7 @@ var ( ErrUnknownField = errors.New("unknown field") ErrBadJSONRequest = errors.New("bad JSON request") ErrAccessDenied = errors.New("access denied") + ErrAccessForbidden = errors.New("access forbidden") ) var ( @@ -202,13 +203,15 @@ var ( ErrPluginIsUnloaded = ErrorWithCode("PLUGIN_IS_UNLOADED", "plugin is unloaded", ErrInvalidRequest) ErrPluginNotLoaded = ErrorWithCode("PLUGIN_NOT_LOADED", "plugin not loaded", ErrInvalidRequest) - ErrRoleAdd = ErrorWithCode("ROLE_ADD_ERROR", "failed to add role", ErrInternal) - ErrRoleGet = ErrorWithCode("ROLE_GET_ERROR", "failed to get role", ErrInternal) - ErrRoleUpdate = ErrorWithCode("ROLE_UPDATE_ERROR", "failed to update role", ErrInternal) - ErrRoleList = ErrorWithCode("ROLE_LIST_ERROR", "failed to list role", ErrInternal) - ErrRoleNotFound = ErrorWithCode("ROLE_NOT_FOUND_ERROR", "role is not found", ErrNotFound) - ErrRoleDelete = ErrorWithCode("ROLE_DELETE_ERROR", "failed to delete role", ErrInternal) - ErrRoleSearch = ErrorWithCode("ROLE_SEARCH_ERROR", "failed to search role", ErrInternal) + ErrRoleAdd = ErrorWithCode("ROLE_ADD_ERROR", "failed to add role", ErrInternal) + ErrRoleGet = ErrorWithCode("ROLE_GET_ERROR", "failed to get role", ErrInternal) + ErrRoleUpdate = ErrorWithCode("ROLE_UPDATE_ERROR", "failed to update role", ErrInternal) + ErrRoleUpdateForbidden = ErrorWithCode("ROLE_UPDATE_ERROR", "failed to update role", ErrAccessForbidden) + ErrRoleList = ErrorWithCode("ROLE_LIST_ERROR", "failed to list role", ErrInternal) + ErrRoleNotFound = ErrorWithCode("ROLE_NOT_FOUND_ERROR", "role is not found", ErrNotFound) + ErrRoleDelete = ErrorWithCode("ROLE_DELETE_ERROR", "failed to delete role", ErrInternal) + ErrRoleDeleteForbidden = ErrorWithCode("ROLE_DELETE_ERROR", "failed to delete role", ErrAccessForbidden) + ErrRoleSearch = ErrorWithCode("ROLE_SEARCH_ERROR", "failed to search role", ErrInternal) ErrRunStoryAdd = ErrorWithCode("RUN_STORY_ADD_ERROR", "failed to add run story", ErrInternal) ErrRunStoryUpdate = ErrorWithCode("RUN_STORY_UPDATE_ERROR", "failed to update run story", ErrInternal) @@ -258,20 +261,23 @@ var ( ErrTriggerSearch = ErrorWithCode("TRIGGER_SEARCH_ERROR", "failed to search trigger", ErrInternal) ErrTriggerDeleteEntity = ErrorWithCode("TRIGGER_DELETE_ENTITY_ERROR", "trigger delete entity failed", ErrInternal) - ErrUserAdd = ErrorWithCode("USER_ADD_ERROR", "failed to add user", ErrInternal) - ErrUserMetaAdd = ErrorWithCode("USER_META_ADD_ERROR", "failed to add user meta", ErrInternal) - ErrUserGet = ErrorWithCode("USER_GET_ERROR", "failed to get user", ErrInternal) - ErrUserUpdate = ErrorWithCode("USER_UPDATE_ERROR", "failed to update user", ErrInternal) - ErrUserList = ErrorWithCode("USER_LIST_ERROR", "failed to list user", ErrInternal) - ErrUserNotFound = ErrorWithCode("USER_NOT_FOUND_ERROR", "user is not found", ErrNotFound) - ErrUserDelete = ErrorWithCode("USER_DELETE_ERROR", "failed to delete user", ErrInternal) - - ErrVariableAdd = ErrorWithCode("VARIABLE_ADD_ERROR", "failed to add variable", ErrInternal) - ErrVariableGet = ErrorWithCode("VARIABLE_GET_ERROR", "failed to get variable", ErrInternal) - ErrVariableUpdate = ErrorWithCode("VARIABLE_UPDATE_ERROR", "failed to update variable", ErrInternal) - ErrVariableList = ErrorWithCode("VARIABLE_LIST_ERROR", "failed to list variable", ErrInternal) - ErrVariableNotFound = ErrorWithCode("VARIABLE_NOT_FOUND_ERROR", "variable is not found", ErrNotFound) - ErrVariableDelete = ErrorWithCode("VARIABLE_DELETE_ERROR", "failed to delete variable", ErrInternal) + ErrUserAdd = ErrorWithCode("USER_ADD_ERROR", "failed to add user", ErrInternal) + ErrUserMetaAdd = ErrorWithCode("USER_META_ADD_ERROR", "failed to add user meta", ErrInternal) + ErrUserGet = ErrorWithCode("USER_GET_ERROR", "failed to get user", ErrInternal) + ErrUserUpdate = ErrorWithCode("USER_UPDATE_ERROR", "failed to update user", ErrInternal) + ErrUserUpdateForbidden = ErrorWithCode("USER_UPDATE_ERROR", "failed to update user", ErrAccessForbidden) + ErrUserList = ErrorWithCode("USER_LIST_ERROR", "failed to list user", ErrInternal) + ErrUserNotFound = ErrorWithCode("USER_NOT_FOUND_ERROR", "user is not found", ErrNotFound) + ErrUserDelete = ErrorWithCode("USER_DELETE_ERROR", "failed to delete user", ErrInternal) + ErrUserDeleteForbidden = ErrorWithCode("USER_DELETE_ERROR", "failed to delete user", ErrAccessForbidden) + + ErrVariableAdd = ErrorWithCode("VARIABLE_ADD_ERROR", "failed to add variable", ErrInternal) + ErrVariableGet = ErrorWithCode("VARIABLE_GET_ERROR", "failed to get variable", ErrInternal) + ErrVariableUpdate = ErrorWithCode("VARIABLE_UPDATE_ERROR", "failed to update variable", ErrInternal) + ErrVariableList = ErrorWithCode("VARIABLE_LIST_ERROR", "failed to list variable", ErrInternal) + ErrVariableNotFound = ErrorWithCode("VARIABLE_NOT_FOUND_ERROR", "variable is not found", ErrNotFound) + ErrVariableDelete = ErrorWithCode("VARIABLE_DELETE_ERROR", "failed to delete variable", ErrInternal) + ErrVariableUpdateForbidden = ErrorWithCode("VARIABLE_UPDATE_ERROR", "unable to update system variable", ErrAccessForbidden) ErrZigbee2mqttAdd = ErrorWithCode("ZIGBEE2MQTT_ADD_ERROR", "failed to add zigbee2mqtt", ErrInternal) ErrZigbee2mqttGet = ErrorWithCode("ZIGBEE2MQTT_GET_ERROR", "failed to get zigbee2mqtt", ErrInternal) @@ -292,8 +298,13 @@ var ( ErrUserDeviceDelete = ErrorWithCode("USER_DEVICE_DELETE_ERROR", "failed to delete user device", ErrInternal) ErrUserDeviceAdd = ErrorWithCode("USER_DEVICE_ADD_ERROR", "failed to add user device", ErrInternal) - ErrBackupNotFound = ErrorWithCode("BACKUP_NOT_FOUND_ERROR", "backup not found", ErrNotFound) - ErrBackupNameNotUnique = ErrorWithCode("BACKUP_NAME_NOT_UNIQUE", "backup name not unique", ErrInvalidRequest) + ErrBackupNotFound = ErrorWithCode("BACKUP_NOT_FOUND_ERROR", "backup not found", ErrNotFound) + ErrBackupNameNotUnique = ErrorWithCode("BACKUP_NAME_NOT_UNIQUE_ERROR", "backup name not unique", ErrInvalidRequest) + ErrBackupRestoreForbidden = ErrorWithCode("BACKUP_RESTORE_ERROR", "failed to restore backup", ErrAccessForbidden) + ErrBackupApplyForbidden = ErrorWithCode("BACKUP_APPLY_ERROR", "failed to apply backup", ErrAccessForbidden) + ErrBackupRollbackForbidden = ErrorWithCode("BACKUP_ROLLBACK_ERROR", "failed to rollback backup", ErrAccessForbidden) + ErrBackupCreateNewForbidden = ErrorWithCode("BACKUP_CREATE_ERROR", "failed to create new backup", ErrAccessForbidden) + ErrBackupUploadForbidden = ErrorWithCode("BACKUP_UPLOAD_ERROR", "failed to upload backup", ErrAccessForbidden) ErrScriptVersionAdd = ErrorWithCode("SCRIPT_VERSION_ADD_ERROR", "failed to add script version", ErrInternal) ErrScriptVersionList = ErrorWithCode("SCRIPT_VERSION_LIST_ERROR", "failed to list script version", ErrInternal) diff --git a/common/events/entities.go b/common/events/entities.go index 68933ef1a..2cec032af 100644 --- a/common/events/entities.go +++ b/common/events/entities.go @@ -21,25 +21,24 @@ package events import ( "github.com/e154/smart-home/common" m "github.com/e154/smart-home/models" - "github.com/e154/smart-home/system/bus" ) // EventStateChanged ... type EventStateChanged struct { - StorageSave bool `json:"storage_save"` - DoNotSaveMetric bool `json:"do_not_save_metric"` - PluginName string `json:"plugin_name"` - EntityId common.EntityId `json:"entity_id"` - OldState bus.EventEntityState `json:"old_state"` - NewState bus.EventEntityState `json:"new_state"` + StorageSave bool `json:"storage_save"` + DoNotSaveMetric bool `json:"do_not_save_metric"` + PluginName string `json:"plugin_name"` + EntityId common.EntityId `json:"entity_id"` + OldState EventEntityState `json:"old_state"` + NewState EventEntityState `json:"new_state"` } // EventLastStateChanged ... type EventLastStateChanged struct { - PluginName string `json:"plugin_name"` - EntityId common.EntityId `json:"entity_id"` - OldState bus.EventEntityState `json:"old_state"` - NewState bus.EventEntityState `json:"new_state"` + PluginName string `json:"plugin_name"` + EntityId common.EntityId `json:"entity_id"` + OldState EventEntityState `json:"old_state"` + NewState EventEntityState `json:"new_state"` } // EventGetLastState ... diff --git a/system/bus/entity_state.go b/common/events/entity_state.go similarity index 96% rename from system/bus/entity_state.go rename to common/events/entity_state.go index 95a2c6b91..9ea2dd61f 100644 --- a/system/bus/entity_state.go +++ b/common/events/entity_state.go @@ -1,6 +1,6 @@ // This file is part of the Smart Home // Program complex distribution https://github.com/e154/smart-home -// Copyright (C) 2023, Filippov Alex +// Copyright (C) 2023-2024, Filippov Alex // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -16,7 +16,7 @@ // License along with this library. If not, see // . -package bus +package events import ( "time" diff --git a/common/events/events.go b/common/events/events.go index 40d07f52f..4473e13fe 100644 --- a/common/events/events.go +++ b/common/events/events.go @@ -24,6 +24,17 @@ import ( "github.com/iancoleman/strcase" ) +type OwnerType string + +const ( + OwnerUser = OwnerType("user") + OwnerSystem = OwnerType("system") +) + +type Common struct { + Owner OwnerType `json:"owner"` +} + func EventName(event interface{}) string { if t := reflect.TypeOf(event); t.Kind() == reflect.Ptr { return strcase.ToSnake(t.Elem().Name()) diff --git a/common/events/scripts.go b/common/events/scripts.go index b128a231b..ed3640094 100644 --- a/common/events/scripts.go +++ b/common/events/scripts.go @@ -22,17 +22,21 @@ import m "github.com/e154/smart-home/models" // EventCreatedScriptModel ... type EventCreatedScriptModel struct { + Common ScriptId int64 `json:"script_id"` Script *m.Script `json:"script"` } // EventUpdatedScriptModel ... type EventUpdatedScriptModel struct { + Common ScriptId int64 `json:"script_id"` Script *m.Script `json:"script"` } // EventRemovedScriptModel ... type EventRemovedScriptModel struct { - ScriptId int64 `json:"script_id"` + Common + ScriptId int64 `json:"script_id"` + Script *m.Script `json:"script"` } diff --git a/system/bus/stat.go b/common/events/terminal.go similarity index 67% rename from system/bus/stat.go rename to common/events/terminal.go index d41cf1a02..09377db2a 100644 --- a/system/bus/stat.go +++ b/common/events/terminal.go @@ -1,6 +1,6 @@ // This file is part of the Smart Home // Program complex distribution https://github.com/e154/smart-home -// Copyright (C) 2023, Filippov Alex +// Copyright (C) 2024, Filippov Alex // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public @@ -16,19 +16,13 @@ // License along with this library. If not, see // . -package bus +package events -import "strings" +import m "github.com/e154/smart-home/models" -// Stat ... -type Stat struct { - Topic string - Subscribers int +// CommandTerminal ... +type CommandTerminal struct { + SessionID string `json:"session_id"` + User *m.User `json:"user"` + Text string `json:"text"` } - -// Stats ... -type Stats []Stat - -func (s Stats) Len() int { return len(s) } -func (s Stats) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s Stats) Less(i, j int) bool { return strings.Compare(s[i].Topic, s[j].Topic) == -1 } diff --git a/common/events/user.go b/common/events/user.go index 7e76311cc..1694c1384 100644 --- a/common/events/user.go +++ b/common/events/user.go @@ -27,7 +27,8 @@ type EventUpdateUserLocation struct { } type EventDirectMessage struct { - UserID int64 `json:"user_id"` - Query string `json:"query"` - Message interface{} `json:"message"` + UserID int64 `json:"user_id"` + SessionID string `json:"session_id"` + Query string `json:"query"` + Message interface{} `json:"message"` } diff --git a/common/types.go b/common/types.go index 1cc20715f..1ef02abb7 100644 --- a/common/types.go +++ b/common/types.go @@ -220,6 +220,8 @@ const ( DebugMode = RunMode("debug") // ReleaseMode ... ReleaseMode = RunMode("release") + // DemoMode + DemoMode = RunMode("demo") ) // PageParams ... diff --git a/conf/config.dev.json b/conf/config.dev.json index 445986896..c450d5a9a 100644 --- a/conf/config.dev.json +++ b/conf/config.dev.json @@ -30,5 +30,6 @@ "api_gzip": false, "pprof": false, "domain": "localhost", - "https": false + "https": false, + "root_secret": "" } diff --git a/db/action.go b/db/action.go index 562177f79..b0b58cd0c 100644 --- a/db/action.go +++ b/db/action.go @@ -111,7 +111,7 @@ func (t Actions) Delete(ctx context.Context, id int64) (err error) { } // List ... -func (t *Actions) List(ctx context.Context, limit, offset int, orderBy, sort string) (list []*Action, total int64, err error) { +func (t *Actions) List(ctx context.Context, limit, offset int, orderBy, sort string, ids *[]uint64) (list []*Action, total int64, err error) { if err = t.Db.WithContext(ctx).Model(Action{}).Count(&total).Error; err != nil { err = errors.Wrap(apperr.ErrActionList, err.Error()) @@ -128,7 +128,9 @@ func (t *Actions) List(ctx context.Context, limit, offset int, orderBy, sort str Preload("Area"). Order(fmt.Sprintf("%s %s", sort, orderBy)) } - + if ids != nil { + q = q.Where("id IN (?)", *ids) + } if err = q.Find(&list).Error; err != nil { err = errors.Wrap(apperr.ErrActionList, err.Error()) } diff --git a/db/condition.go b/db/condition.go index eb7b81b5b..a2458313b 100644 --- a/db/condition.go +++ b/db/condition.go @@ -105,7 +105,7 @@ func (t Conditions) Delete(ctx context.Context, id int64) (err error) { } // List ... -func (t *Conditions) List(ctx context.Context, limit, offset int, orderBy, sort string) (list []*Condition, total int64, err error) { +func (t *Conditions) List(ctx context.Context, limit, offset int, orderBy, sort string, ids *[]uint64) (list []*Condition, total int64, err error) { if err = t.Db.WithContext(ctx).Model(Condition{}).Count(&total).Error; err != nil { err = errors.Wrap(apperr.ErrConditionList, err.Error()) @@ -122,7 +122,9 @@ func (t *Conditions) List(ctx context.Context, limit, offset int, orderBy, sort q = q. Order(fmt.Sprintf("%s %s", sort, orderBy)) } - + if ids != nil { + q = q.Where("id IN (?)", *ids) + } if err = q.Find(&list).Error; err != nil { err = errors.Wrap(apperr.ErrConditionList, err.Error()) } diff --git a/db/entity.go b/db/entity.go index 7b0547f76..0c34e879d 100644 --- a/db/entity.go +++ b/db/entity.go @@ -327,6 +327,7 @@ func (n *Entities) GetByType(ctx context.Context, t string, limit, offset int) ( return } + // todo: remove if err = n.PreloadStorage(ctx, list); err != nil { err = errors.Wrap(apperr.ErrEntityGet, err.Error()) return @@ -387,7 +388,7 @@ func (n Entities) PreloadStorage(ctx context.Context, list []*Entity) (err error for _, item := range list { err = n.Db.WithContext(ctx).Model(&EntityStorage{}). Order("created_at desc"). - Limit(1). + Limit(2). Find(&item.Storage, "entity_id = ?", item.Id). Error if err != nil { diff --git a/db/entity_storage.go b/db/entity_storage.go index 4ae50e540..412fc723c 100644 --- a/db/entity_storage.go +++ b/db/entity_storage.go @@ -51,7 +51,7 @@ func (d *EntityStorage) TableName() string { } // Add ... -func (n *EntityStorages) Add(ctx context.Context, v EntityStorage) (id int64, err error) { +func (n *EntityStorages) Add(ctx context.Context, v *EntityStorage) (id int64, err error) { if err = n.Db.WithContext(ctx).Create(&v).Error; err != nil { err = errors.Wrap(apperr.ErrEntityStorageAdd, err.Error()) return @@ -61,8 +61,8 @@ func (n *EntityStorages) Add(ctx context.Context, v EntityStorage) (id int64, er } // GetLastByEntityId ... -func (n *EntityStorages) GetLastByEntityId(ctx context.Context, entityId common.EntityId) (v EntityStorage, err error) { - v = EntityStorage{} +func (n *EntityStorages) GetLastByEntityId(ctx context.Context, entityId common.EntityId) (v *EntityStorage, err error) { + v = &EntityStorage{} err = n.Db.WithContext(ctx).Model(&EntityStorage{}). Order("created_at desc"). First(&v, "entity_id = ?", entityId). @@ -80,16 +80,12 @@ func (n *EntityStorages) GetLastByEntityId(ctx context.Context, entityId common. } // List ... -func (n *EntityStorages) List(ctx context.Context, limit, offset int, orderBy, sort string, entityIds []common.EntityId, startDate, endDate *time.Time) (list []EntityStorage, total int64, err error) { +func (n *EntityStorages) List(ctx context.Context, limit, offset int, orderBy, sort string, entityIds []common.EntityId, startDate, endDate *time.Time) (list []*EntityStorage, total int64, err error) { q := n.Db.WithContext(ctx).Model(&EntityStorage{}) if len(entityIds) > 0 { - var ids = make([]string, 0, len(entityIds)) - for _, id := range entityIds { - ids = append(ids, id.String()) - } - q = q.Where("entity_id in (?)", ids) + q = q.Where("entity_id in (?)", entityIds) } if startDate != nil { @@ -104,7 +100,7 @@ func (n *EntityStorages) List(ctx context.Context, limit, offset int, orderBy, s return } - list = make([]EntityStorage, 0) + list = make([]*EntityStorage, 0) q = q. Limit(limit). Offset(offset) diff --git a/db/script.go b/db/script.go index 94bf0849d..60e346239 100644 --- a/db/script.go +++ b/db/script.go @@ -224,13 +224,16 @@ func (n Scripts) Delete(ctx context.Context, scriptId int64) (err error) { } // List ... -func (n *Scripts) List(ctx context.Context, limit, offset int, orderBy, sort string, query *string) (list []*Script, total int64, err error) { +func (n *Scripts) List(ctx context.Context, limit, offset int, orderBy, sort string, query *string, ids *[]uint64) (list []*Script, total int64, err error) { list = make([]*Script, 0) q := n.Db.WithContext(ctx).Model(Script{}) if query != nil { q = q.Where("name LIKE ? or source LIKE ?", "%"+*query+"%", "%"+*query+"%") } + if ids != nil { + q = q.Where("id IN (?)", *ids) + } if err = q.Count(&total).Error; err != nil { err = errors.Wrap(apperr.ErrScriptList, err.Error()) return diff --git a/db/trigger.go b/db/trigger.go index 610efe1b5..3888e746b 100644 --- a/db/trigger.go +++ b/db/trigger.go @@ -141,7 +141,7 @@ func (t Triggers) List(ctx context.Context, limit, offset int, orderBy, sort str } // ListPlain ... -func (t Triggers) ListPlain(ctx context.Context, limit, offset int, orderBy, sort string, onlyEnabled bool) (list []*Trigger, total int64, err error) { +func (t Triggers) ListPlain(ctx context.Context, limit, offset int, orderBy, sort string, onlyEnabled bool, ids *[]uint64) (list []*Trigger, total int64, err error) { if err = t.Db.WithContext(ctx).Model(Trigger{}).Count(&total).Error; err != nil { err = errors.Wrap(apperr.ErrTriggerList, err.Error()) @@ -164,7 +164,9 @@ func (t Triggers) ListPlain(ctx context.Context, limit, offset int, orderBy, sor q = q. Order(fmt.Sprintf("%s %s", sort, orderBy)) } - + if ids != nil { + q = q.Where("id IN (?)", *ids) + } if err = q.Find(&list).Error; err != nil { err = errors.Wrap(apperr.ErrTriggerList, err.Error()) } diff --git a/db/user.go b/db/user.go index 4d7de9419..48876cd3b 100644 --- a/db/user.go +++ b/db/user.go @@ -200,24 +200,25 @@ func (u *Users) GetByResetPassToken(ctx context.Context, token string) (user *Us func (u *Users) Update(ctx context.Context, user *User) (err error) { q := map[string]interface{}{ - "nickname": user.Nickname, - "first_name": user.FirstName, - "last_name": user.LastName, - "email": user.Email, - "status": user.Status, - "reset_password_token": user.ResetPasswordToken, - "authentication_token": user.AuthenticationToken, - "image_id": user.ImageId, - "sign_in_count": user.SignInCount, - "current_sign_in_ip": user.CurrentSignInIp, - "last_sign_in_ip": user.LastSignInIp, - "lang": user.Lang, - "user_id": user.UserId, - "role_name": user.RoleName, - //"meta": user.Meta, //todo fix + "nickname": user.Nickname, + "first_name": user.FirstName, + "last_name": user.LastName, + "email": user.Email, + "status": user.Status, + "reset_password_token": user.ResetPasswordToken, + "authentication_token": user.AuthenticationToken, + "image_id": user.ImageId, + "sign_in_count": user.SignInCount, + "current_sign_in_ip": user.CurrentSignInIp, + "last_sign_in_ip": user.LastSignInIp, + "lang": user.Lang, + "user_id": user.UserId, + "role_name": user.RoleName, "reset_password_sent_at": user.ResetPasswordSentAt, "current_sign_in_at": user.CurrentSignInAt, "last_sign_in_at": user.LastSignInAt, + "history": user.History, + //"meta": user.Meta, //todo fix } if user.EncryptedPassword != "" { q["encrypted_password"] = user.EncryptedPassword diff --git a/doc/content/en/docs/javascript/events.md b/doc/content/en/docs/javascript/events.md new file mode 100644 index 000000000..e31b162ac --- /dev/null +++ b/doc/content/en/docs/javascript/events.md @@ -0,0 +1,35 @@ +--- +title: "Events" +linkTitle: "events" +date: 2024-01-13 +description: > + +--- + +In the Smart Home system, there is a JavaScript function called **PushSystemEvent** that plays a crucial role in dynamic system management. This function accepts various commands for interacting with tasks, triggers, and other components of the system. + +| Command | Description | +|---------------------------|----------------------------------------------------------------| +| `command_enable_task` | Enables the execution of a task. | +| `command_disable_task` | Disables the execution of a task. | +| `command_enable_trigger` | Enables a trigger, activating the triggering capability. | +| `command_disable_trigger` | Disables a trigger, suspending the triggering capability. | +| `event_call_trigger` | Initiates a trigger call event. | +| `event_call_action` | Initiates an action call event. | +| `command_load_entity` | Loads an entity into the system. | +| `command_unload_entity` | Unloads an entity from the system. | + +#### Example of Usage: + +```javascript +// Example of enabling a task +PushSystemEvent('command_enable_task', { id: 1 }); + +// Example of calling a trigger +PushSystemEvent('event_call_trigger', { id: 1 }); + +// Example of loading an entity +PushSystemEvent('command_load_entity', { id: 'sensor.entity1' }); +``` + +These commands provide control over tasks, triggers, trigger call events, actions, as well as loading and unloading entities in the Smart Home system. Their use enables dynamic management of system functionality and interaction with its components. diff --git a/doc/content/en/docs/plugins/webdav.md b/doc/content/en/docs/plugins/webdav.md new file mode 100755 index 000000000..38edbb7f9 --- /dev/null +++ b/doc/content/en/docs/plugins/webdav.md @@ -0,0 +1,52 @@ +--- +title: "Webdav" +linkTitle: "webdav" +date: 2024-01-25 +description: > + +--- + +### Integration of WebDAV Plugin for Scripting in Smart Home System + +In the Smart Home system, the WebDAV plugin has been successfully added and integrated, providing convenient access to +scripts through any IDE that supports this protocol. This opens up new possibilities for script development and +configuration, making the process more flexible and convenient. + +#### Key Features of the WebDAV Plugin: + +1. **Access to Scripts from Any IDE:** + - The WebDAV plugin allows connecting to the Smart Home system from any IDE compatible with the WebDAV protocol. + This simplifies the process of creating, editing, and testing scripts. + +2. **Development Flexibility:** + - Developers can use their preferred tools and IDEs to work with JavaScript scripts in the Smart Home system. This + includes debugging, autocompletion, and other features provided by the chosen development environment. + +3. **Remote Script Management:** + - The WebDAV plugin enables remote management of scripts in the Smart Home system, including uploading new scripts, + updating existing ones, and performing other operations from the selected IDE. + +4. **Security and Data Protection:** + - All operations through the WebDAV protocol are secured, ensuring data integrity and preventing unauthorized + access. + +#### Example of Connection from a WebDAV-Supported IDE: + +1. **Connection Setup:** + - In the IDE, select the option to connect to a remote repository or file system via the WebDAV protocol. + +2. **Specify Address and Credentials:** + - Specify the address of the Smart Home system using the WebDAV protocol, and provide credentials for access. + +3. **View and Edit Scripts:** + - After a successful connection, scripts in the Smart Home system will be available for viewing and editing directly + from your IDE. + +4. **Unload and Load Scripts:** + - You can unload existing scripts, make changes, and upload them back to the Smart Home system without the need to + interact directly with the system's interface. + +#### Conclusion: + +The integration of the WebDAV plugin into the Smart Home system significantly improves the process of script development +and management, making it more convenient, flexible, and accessible for developers. diff --git a/doc/content/ru/docs/javascript/events.md b/doc/content/ru/docs/javascript/events.md new file mode 100644 index 000000000..e0f23f5be --- /dev/null +++ b/doc/content/ru/docs/javascript/events.md @@ -0,0 +1,35 @@ +--- +title: "Events" +linkTitle: "events" +date: 2024-01-13 +description: > + +--- + +В системе Smart Home предоставляется JavaScript функция **PushSystemEvent**, которая играет важную роль в динамическом управлении системой. Эта функция принимает различные команды для взаимодействия с задачами, триггерами и другими компонентами системы. + +| Команда | Описание | +|---------------------------|------------------------------------------------------------------------------------------------------| +| `command_enable_task` | Включение выполнения задачи. | +| `command_disable_task` | Отключение выполнения задачи. | +| `command_enable_trigger` | Включение триггера, активация возможности триггерирования. | +| `command_disable_trigger` | Отключение триггера, приостановка возможности триггерирования. | +| `event_call_trigger` | Инициирование события вызова триггера. | +| `event_call_action` | Инициирование события вызова действия. | +| `command_load_entity` | Загрузка сущности в систему. | +| `command_unload_entity` | Выгрузка сущности из системы. | + +#### Пример использования: + +```javascript +// Пример включения задачи +PushSystemEvent('command_enable_task', { id: 1 }); + +// Пример вызова триггера +PushSystemEvent('event_call_trigger', { id: 1 }); + +// Пример загрузки сущности +PushSystemEvent('command_load_entity', { id: 'sensor.entity1' }); +``` + +Эти команды предоставляют управление задачами, триггерами, вызов событий триггера и действия, а также загрузку и выгрузку сущностей в системе Smart Home. Их использование позволяет динамически управлять функциональностью системы и взаимодействовать с её компонентами. diff --git a/doc/content/ru/docs/plugins/webdav.md b/doc/content/ru/docs/plugins/webdav.md new file mode 100755 index 000000000..2a9a9d65b --- /dev/null +++ b/doc/content/ru/docs/plugins/webdav.md @@ -0,0 +1,53 @@ +--- +title: "Webdav" +linkTitle: "webdav" +date: 2024-01-25 +description: > + +--- + +### Интеграция Плагина WebDAV для Работы со Скриптами в Системе Smart Home + +В системе Smart Home был успешно добавлен и интегрирован плагин WebDAV, предоставляющий удобный доступ к скриптам через +любую удобную IDE, поддерживающую данный протокол. Это открывает новые возможности для разработки и настройки скриптов, +делая процесс более гибким и удобным. + +#### Основные возможности плагина WebDAV: + +1. **Доступ к Скриптам из Любой IDE:** + - Плагин WebDAV позволяет подключаться к системе Smart Home из любой совместимой с протоколом WebDAV интегрированной + среды разработки (IDE). Это упрощает процесс создания, редактирования и тестирования скриптов. + +2. **Гибкость Разработки:** + - Разработчики могут использовать предпочитаемые инструменты и IDE для работы с JavaScript-скриптами системы Smart + Home. Это включает в себя возможность отладки, автодополнения и другие функции, предоставляемые выбранной средой + разработки. + +3. **Управление Скриптами извне:** + - Плагин WebDAV позволяет управлять скриптами в системе Smart Home, загружать новые скрипты, обновлять существующие + и выполнять другие операции удаленно из выбранной IDE. + +4. **Безопасность и Защита Данных:** + - Все операции через протокол WebDAV обеспечиваются средствами безопасности, что гарантирует сохранность данных и + предотвращает несанкционированный доступ. + +#### Пример Подключения из IDE с Поддержкой WebDAV: + +1. **Настройка Подключения:** + - В IDE выберите опцию подключения к удаленному репозиторию или файловой системе через протокол WebDAV. + +2. **Указание Адреса и Учетных Данных:** + - Укажите адрес системы Smart Home с учетом протокола WebDAV, а также предоставьте учетные данные для доступа. + +3. **Просмотр и Редактирование Скриптов:** + - После успешного подключения, скрипты системы Smart Home будут доступны для просмотра и редактирования + непосредственно из вашей IDE. + +4. **Выгрузка и Загрузка Скриптов:** + - Вы можете выгружать уже существующие скрипты, вносить изменения и загружать их обратно в систему Smart Home без + необходимости напрямую взаимодействовать с интерфейсом системы. + +#### Заключение: + +Интеграция плагина WebDAV в систему Smart Home значительно улучшает процесс разработки и управления скриптами, делая его +более удобным, гибким и доступным для разработчиков. diff --git a/doc/schemes/entity state version.svg b/doc/schemes/entity state version.svg new file mode 100644 index 000000000..cc7f6f461 --- /dev/null +++ b/doc/schemes/entity state version.svg @@ -0,0 +1,4 @@ + + + +
V6
V6
V3
V3
V2
V2
snapshot 3
snapshot 3
V1
V1
snapshot 1
snapshot 1
snapshot 2
snapshot 2
last_changed: 2024-01-21T10:02:38.168073+05:30
last_changed: 2024-01-21T10:02:38.168073+05:30
New state
New state
last_changed: 2024-01-21T10:02:37.665454+05:30
last_changed: 2024-01-21T10:02:37.665454+05:30
last_updated: 2024-01-21T10:02:38.168073+05:30
last_updated: 2024-01-21T10:02:38.168073+05:30
Old state
Old state
last_changed: 2024-01-21T10:02:37.665454+05:30
last_changed: 2024-01-21T10:02:37.665454+05:30
last_updated: 2024-01-21T10:02:38.168073+05:30
last_updated: 2024-01-21T10:02:38.168073+05:30
New state 
New state 
last_changed: null
last_changed: null
last_updated: 2024-01-21T10:02:37.665454+05:30
last_updated: 2024-01-21T10:02:37.665454+05:30
Old state
Old state
last_changed: 2024-01-21T10:02:38.670397+05:30
last_changed: 2024-01-21T10:02:38.670397+05:30
last_updated: 2024-01-21T10:02:43.677459+05:30
last_updated: 2024-01-21T10:02:43.677459+05:30
New state
New state
last_changed: 2024-01-21T10:02:38.168073+05:30
last_changed: 2024-01-21T10:02:38.168073+05:30
Old state
Old state
last_updated: 2024-01-21T10:02:38.670397+05:30
last_updated: 2024-01-21T10:02:38.670397+05:30
last_updated: 2024-01-21T10:02:38.670397+05:30
last_updated: 2024-01-21T10:02:38.670397+05:30
Text is not SVG - cannot display
\ No newline at end of file diff --git a/endpoint/action.go b/endpoint/action.go index 9030c7996..f6298cbd7 100644 --- a/endpoint/action.go +++ b/endpoint/action.go @@ -103,9 +103,9 @@ func (n *ActionEndpoint) Update(ctx context.Context, params *m.Action) (action * } // GetList ... -func (n *ActionEndpoint) GetList(ctx context.Context, pagination common.PageParams) (result []*m.Action, total int64, err error) { +func (n *ActionEndpoint) GetList(ctx context.Context, pagination common.PageParams, ids *[]uint64) (result []*m.Action, total int64, err error) { - result, total, err = n.adaptors.Action.List(ctx, pagination.Limit, pagination.Offset, pagination.Order, pagination.SortBy) + result, total, err = n.adaptors.Action.List(ctx, pagination.Limit, pagination.Offset, pagination.Order, pagination.SortBy, ids) return } diff --git a/endpoint/auth.go b/endpoint/auth.go index 8952c37c0..ede12ec60 100644 --- a/endpoint/auth.go +++ b/endpoint/auth.go @@ -64,7 +64,7 @@ func (a *AuthEndpoint) SignIn(ctx context.Context, email, password string, ip st return } - if accessToken, err = a.jwtManager.Generate(user); err != nil { + if accessToken, err = a.jwtManager.Generate(user, false); err != nil { err = errors.Wrap(apperr.ErrUnauthorized, err.Error()) return } diff --git a/endpoint/backup.go b/endpoint/backup.go index ed9fc4e1f..21246f990 100644 --- a/endpoint/backup.go +++ b/endpoint/backup.go @@ -45,12 +45,24 @@ func NewBackupEndpoint(common *CommonEndpoint, backup *backup.Backup) *BackupEnd // New ... func (b *BackupEndpoint) New(ctx context.Context) (err error) { + + if b.checkSuperUser(ctx) { + err = apperr.ErrBackupCreateNewForbidden + return + } + go b.backup.New(false) return } // Restore ... func (b *BackupEndpoint) Restore(ctx context.Context, name string) (err error) { + + if b.checkSuperUser(ctx) { + err = apperr.ErrBackupRestoreForbidden + return + } + err = b.backup.Restore(name) return } @@ -62,24 +74,29 @@ func (b *BackupEndpoint) GetList(ctx context.Context, pagination common.PagePara } // Upload ... -func (b *BackupEndpoint) Upload(ctx context.Context, files map[string][]*multipart.FileHeader) (fileList []*m.Backup, errs []error) { +func (b *BackupEndpoint) Upload(ctx context.Context, files map[string][]*multipart.FileHeader) (fileList []*m.Backup, errs []error, err error) { + + if b.checkSuperUser(ctx) { + err = apperr.ErrBackupUploadForbidden + return + } fileList = make([]*m.Backup, 0) errs = make([]error, 0) for _, fileHeader := range files { - file, err := fileHeader[0].Open() - if err != nil { - errs = append(errs, err) + file, _err := fileHeader[0].Open() + if _err != nil { + errs = append(errs, _err) continue } reader := bufio.NewReader(file) var newbackup *m.Backup - newbackup, err = b.backup.UploadBackup(ctx, reader, fileHeader[0].Filename) - if err != nil { - errs = append(errs, err) + newbackup, _err = b.backup.UploadBackup(ctx, reader, fileHeader[0].Filename) + if _err != nil { + errs = append(errs, _err) continue } fileList = append(fileList, newbackup) @@ -92,6 +109,11 @@ func (b *BackupEndpoint) Upload(ctx context.Context, files map[string][]*multipa func (b *BackupEndpoint) Delete(ctx context.Context, name string) (err error) { + if b.checkSuperUser(ctx) { + err = apperr.ErrBackupCreateNewForbidden + return + } + var list []*m.Backup if list, _, err = b.backup.List(ctx, 999, 0, "", ""); err != nil { return @@ -109,11 +131,23 @@ func (b *BackupEndpoint) Delete(ctx context.Context, name string) (err error) { } func (b *BackupEndpoint) ApplyChanges(ctx context.Context) (err error) { + + if b.checkSuperUser(ctx) { + err = apperr.ErrBackupApplyForbidden + return + } + err = b.backup.ApplyChanges() return } func (b *BackupEndpoint) RollbackChanges(ctx context.Context) (err error) { + + if b.checkSuperUser(ctx) { + err = apperr.ErrBackupRollbackForbidden + return + } + err = b.backup.RollbackChanges() return } diff --git a/endpoint/common.go b/endpoint/common.go index d7845698f..233d561d9 100644 --- a/endpoint/common.go +++ b/endpoint/common.go @@ -19,7 +19,10 @@ package endpoint import ( + "context" + "github.com/e154/smart-home/adaptors" + "github.com/e154/smart-home/common" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/access_list" "github.com/e154/smart-home/system/automation" @@ -78,3 +81,14 @@ func NewCommonEndpoint(adaptors *adaptors.Adaptors, cache: cache, } } + +func (c *CommonEndpoint) checkSuperUser(ctx context.Context) (decline bool) { + root, _ := ctx.Value("root").(bool) + //log.Debugf("root: %t, %t", root, ok) + + if root { + return + } + + return c.appConfig.Mode == common.DemoMode +} diff --git a/endpoint/condition.go b/endpoint/condition.go index 4d51848c3..d7376a58d 100644 --- a/endpoint/condition.go +++ b/endpoint/condition.go @@ -102,9 +102,9 @@ func (n *ConditionEndpoint) Update(ctx context.Context, params *m.Condition) (re } // GetList ... -func (n *ConditionEndpoint) GetList(ctx context.Context, pagination common.PageParams) (result []*m.Condition, total int64, err error) { +func (n *ConditionEndpoint) GetList(ctx context.Context, pagination common.PageParams, ids *[]uint64) (result []*m.Condition, total int64, err error) { - result, total, err = n.adaptors.Condition.List(ctx, pagination.Limit, pagination.Offset, pagination.Order, pagination.SortBy) + result, total, err = n.adaptors.Condition.List(ctx, pagination.Limit, pagination.Offset, pagination.Order, pagination.SortBy, ids) return } diff --git a/endpoint/endpoint.go b/endpoint/endpoint.go index 7aadeb8f6..ea13483dc 100644 --- a/endpoint/endpoint.go +++ b/endpoint/endpoint.go @@ -62,6 +62,7 @@ type Endpoint struct { Metric *MetricEndpoint Backup *BackupEndpoint Stream *StreamEndpoint + Webdav *WebdavEndpoint } // NewEndpoint ... @@ -99,5 +100,6 @@ func NewEndpoint(backup *backup.Backup, stream *stream.Stream, common *CommonEnd Metric: NewMetricEndpoint(common), Backup: NewBackupEndpoint(common, backup), Stream: NewStreamEndpoint(common, stream), + Webdav: NewWebdavEndpoint(common), } } diff --git a/endpoint/entity.go b/endpoint/entity.go index aaa879802..70c2bef33 100644 --- a/endpoint/entity.go +++ b/endpoint/entity.go @@ -20,7 +20,6 @@ package endpoint import ( "context" - "github.com/pkg/errors" "github.com/e154/smart-home/common" @@ -74,7 +73,7 @@ func (n *EntityEndpoint) Import(ctx context.Context, entity *m.Entity) (err erro if action.Script != nil { var engine *scripts.Engine if engine, err = n.scriptService.NewEngine(action.Script); err != nil { - err = errors.Wrap(apperr.ErrInternal, err.Error()) + err = errors.Wrap(apperr.ErrInvalidRequest, err.Error()) return } diff --git a/endpoint/plugin.go b/endpoint/plugin.go index e0472e9cc..38cc246e9 100644 --- a/endpoint/plugin.go +++ b/endpoint/plugin.go @@ -114,6 +114,10 @@ func (p *PluginEndpoint) UpdateSettings(ctx context.Context, name string, settin return } + if !p.supervisor.PluginIsLoaded(name) { + return + } + if err = p.supervisor.DisablePlugin(ctx, name); err != nil { return } diff --git a/endpoint/role.go b/endpoint/role.go index 276057561..e948195ff 100644 --- a/endpoint/role.go +++ b/endpoint/role.go @@ -76,6 +76,11 @@ func (n *RoleEndpoint) Update(ctx context.Context, params *m.Role) (result *m.Ro return } + if role.Name == "admin" && n.checkSuperUser(ctx) { + err = apperr.ErrRoleUpdateForbidden + return + } + if params.Parent.Name == "" { role.Parent = nil } else { @@ -115,7 +120,7 @@ func (n *RoleEndpoint) GetList(ctx context.Context, pagination common.PageParams func (n *RoleEndpoint) Delete(ctx context.Context, name string) (err error) { if name == "admin" { - err = apperr.ErrBadRequestParams + err = apperr.ErrRoleDeleteForbidden return } diff --git a/endpoint/script.go b/endpoint/script.go index e3ba17e6e..df134a0ec 100644 --- a/endpoint/script.go +++ b/endpoint/script.go @@ -76,6 +76,9 @@ func (n *ScriptEndpoint) Add(ctx context.Context, params *m.Script) (script *m.S } n.eventBus.Publish(fmt.Sprintf("system/models/scripts/%d", script.Id), events.EventCreatedScriptModel{ + Common: events.Common{ + Owner: events.OwnerUser, + }, ScriptId: script.Id, Script: script, }) @@ -119,6 +122,9 @@ func (n *ScriptEndpoint) Copy(ctx context.Context, scriptId int64) (script *m.Sc } n.eventBus.Publish(fmt.Sprintf("system/models/scripts/%d", script.Id), events.EventCreatedScriptModel{ + Common: events.Common{ + Owner: events.OwnerUser, + }, ScriptId: script.Id, Script: script, }) @@ -166,6 +172,9 @@ func (n *ScriptEndpoint) Update(ctx context.Context, params *m.Script) (result * } n.eventBus.Publish(fmt.Sprintf("system/models/scripts/%d", script.Id), events.EventUpdatedScriptModel{ + Common: events.Common{ + Owner: events.OwnerUser, + }, ScriptId: script.Id, Script: script, }) @@ -174,9 +183,9 @@ func (n *ScriptEndpoint) Update(ctx context.Context, params *m.Script) (result * } // GetList ... -func (n *ScriptEndpoint) GetList(ctx context.Context, pagination common.PageParams, query *string) (result []*m.Script, total int64, err error) { +func (n *ScriptEndpoint) GetList(ctx context.Context, pagination common.PageParams, query *string, ids *[]uint64) (result []*m.Script, total int64, err error) { - result, total, err = n.adaptors.Script.List(ctx, pagination.Limit, pagination.Offset, pagination.Order, pagination.SortBy, query) + result, total, err = n.adaptors.Script.List(ctx, pagination.Limit, pagination.Offset, pagination.Order, pagination.SortBy, query, ids) return } @@ -200,7 +209,11 @@ func (n *ScriptEndpoint) DeleteScriptById(ctx context.Context, scriptId int64) ( } n.eventBus.Publish(fmt.Sprintf("system/models/scripts/%d", script.Id), events.EventRemovedScriptModel{ + Common: events.Common{ + Owner: events.OwnerUser, + }, ScriptId: script.Id, + Script: script, }) return diff --git a/endpoint/trigger.go b/endpoint/trigger.go index 913c6c4e9..cff00908c 100644 --- a/endpoint/trigger.go +++ b/endpoint/trigger.go @@ -104,9 +104,9 @@ func (n *TriggerEndpoint) Update(ctx context.Context, params *m.UpdateTrigger) ( } // GetList ... -func (n *TriggerEndpoint) GetList(ctx context.Context, pagination common.PageParams) (result []*m.Trigger, total int64, err error) { +func (n *TriggerEndpoint) GetList(ctx context.Context, pagination common.PageParams, ids *[]uint64) (result []*m.Trigger, total int64, err error) { - if result, total, err = n.adaptors.Trigger.ListPlain(ctx, pagination.Limit, pagination.Offset, pagination.Order, pagination.SortBy, false); err != nil { + if result, total, err = n.adaptors.Trigger.ListPlain(ctx, pagination.Limit, pagination.Offset, pagination.Order, pagination.SortBy, false, ids); err != nil { return } diff --git a/endpoint/user.go b/endpoint/user.go index 9cd19c811..d39b4cf20 100644 --- a/endpoint/user.go +++ b/endpoint/user.go @@ -117,7 +117,7 @@ func (n *UserEndpoint) Delete(ctx context.Context, userId int64) (err error) { } if user.Role.Name == "admin" && user.Id == 1 { - err = apperr.ErrBadRequestParams + err = apperr.ErrUserDeleteForbidden return } @@ -143,6 +143,11 @@ func (n *UserEndpoint) Update(ctx context.Context, params *m.User) (result *m.Us return } + if user.Id == 1 && user.RoleName == "admin" && n.checkSuperUser(ctx) { + err = apperr.ErrUserUpdateForbidden + return + } + _ = common.Copy(&user, ¶ms, common.JsonEngine) if params.Image != nil && params.Image.Id != 0 { diff --git a/endpoint/variable.go b/endpoint/variable.go index ab8f0e573..1199114e4 100644 --- a/endpoint/variable.go +++ b/endpoint/variable.go @@ -70,14 +70,26 @@ func (v *VariableEndpoint) GetById(ctx context.Context, name string) (variable m } // Update ... -func (v *VariableEndpoint) Update(ctx context.Context, variable m.Variable) (err error) { +func (v *VariableEndpoint) Update(ctx context.Context, _variable m.Variable) (err error) { - if ok, errs := v.validation.Valid(variable); !ok { + if ok, errs := v.validation.Valid(_variable); !ok { err = apperr.ErrInvalidRequest apperr.SetValidationErrors(err, errs) return } + var variable m.Variable + if variable, err = v.adaptors.Variable.GetByName(ctx, _variable.Name); err == nil { + if variable.System && v.checkSuperUser(ctx) { + err = apperr.ErrVariableUpdateForbidden + return + } + variable.Value = _variable.Value + } else { + variable.Name = _variable.Name + variable.Value = _variable.Value + } + if err = v.adaptors.Variable.CreateOrUpdate(ctx, variable); err != nil { return } @@ -101,6 +113,15 @@ func (v *VariableEndpoint) GetList(ctx context.Context, pagination common.PagePa // Delete ... func (v *VariableEndpoint) Delete(ctx context.Context, name string) (err error) { + var variable m.Variable + if variable, err = v.adaptors.Variable.GetByName(ctx, name); err == nil { + if variable.System && v.checkSuperUser(ctx) { + err = apperr.ErrVariableUpdateForbidden + return + } + + } + if err = v.adaptors.Variable.Delete(ctx, name); err != nil { return } diff --git a/endpoint/webdav.go b/endpoint/webdav.go new file mode 100644 index 000000000..0dc4b40f6 --- /dev/null +++ b/endpoint/webdav.go @@ -0,0 +1,60 @@ +// This file is part of the Smart Home +// Program complex distribution https://github.com/e154/smart-home +// Copyright (C) 2024, Filippov Alex +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library. If not, see +// . + +package endpoint + +import ( + "github.com/e154/smart-home/common/apperr" + "github.com/pkg/errors" + "net/http" +) + +// WebdavEndpoint ... +type WebdavEndpoint struct { + *CommonEndpoint +} + +// NewWebdavEndpoint ... +func NewWebdavEndpoint(common *CommonEndpoint) *WebdavEndpoint { + return &WebdavEndpoint{ + CommonEndpoint: common, + } +} + +// Webdav ... +func (p *WebdavEndpoint) Webdav(w http.ResponseWriter, r *http.Request) (err error) { + + if isLoaded := p.supervisor.PluginIsLoaded("webdav"); !isLoaded { + err = errors.Wrap(apperr.ErrInternal, "plugin not loaded") + return + } + + var pl interface{} + if pl, err = p.supervisor.GetPlugin("webdav"); err != nil { + return + } + + plugin, ok := pl.(http.Handler) + if !ok { + return + } + + plugin.ServeHTTP(w, r) + + return +} diff --git a/go.mod b/go.mod index 4fbf6b014..5b86cfbd3 100644 --- a/go.mod +++ b/go.mod @@ -50,8 +50,8 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 golang.org/x/crypto v0.17.0 - golang.org/x/net v0.19.0 // indirect - golang.org/x/sync v0.4.0 // indirect + golang.org/x/net v0.19.0 + golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect @@ -72,7 +72,8 @@ require ( github.com/gobwas/ws v1.3.0 github.com/golang/geo v0.0.0-20230421003525-6adc56603217 github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 - github.com/google/uuid v1.3.1 + github.com/google/uuid v1.4.0 + github.com/gorilla/mux v1.8.0 github.com/hiko1129/echo-pprof v1.0.1 github.com/iancoleman/strcase v0.3.0 github.com/imdario/mergo v0.3.13 @@ -85,6 +86,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/shirou/gopsutil/v3 v3.23.12 github.com/showwin/speedtest-go v1.6.7 + github.com/spf13/afero v1.11.0 github.com/stretchr/testify v1.8.4 gopkg.in/telebot.v3 v3.1.3 gorm.io/driver/postgres v1.5.2 @@ -160,7 +162,6 @@ require ( github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/schollz/closestmatch v2.1.0+incompatible // indirect - github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -178,7 +179,7 @@ require ( github.com/yosssi/ace v0.0.5 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.5.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index bdbfa012d..2791c2ef1 100644 --- a/go.sum +++ b/go.sum @@ -536,7 +536,6 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -640,8 +639,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -670,8 +669,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= @@ -693,6 +692,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1 github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= @@ -857,7 +858,6 @@ github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liip/sheriff v0.11.1 h1:52YGzskXFPSEnwfEtXnbPiMKKXJGm5IP45s8Ogw0Wyk= github.com/liip/sheriff v0.11.1/go.mod h1:nVTQYHxfdIfOHnk5FREt4j6cnaSlJPUfXFVORfgGmTo= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0= github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= @@ -951,7 +951,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= @@ -1020,10 +1019,6 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm github.com/sevenNt/echo-pprof v0.1.0/go.mod h1:3B009ccno8WPXjh4Ut/B2+FOVt/ulHBV8w/cNdsodXA= github.com/sfreiberg/gotwilio v1.0.0 h1:wrI0vkXHiOIi3He4iVn9e8GNa7XWmqe88MwQkN1+9GM= github.com/sfreiberg/gotwilio v1.0.0/go.mod h1:BRG5BNMaZHiT3bYrtP9kHuUXL+sHNvec+ZMKNgvYWY8= -github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE= -github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ= github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -1072,6 +1067,8 @@ github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= @@ -1114,11 +1111,9 @@ github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvF github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM= github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0= github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= @@ -1374,8 +1369,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1477,7 +1472,6 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -1500,8 +1494,8 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/models/app_config.go b/models/app_config.go index b1855c696..17729462d 100644 --- a/models/app_config.go +++ b/models/app_config.go @@ -59,7 +59,8 @@ type AppConfig struct { ApiSwagger bool `json:"api_swagger" env:"API_SWAGGER"` ApiDebug bool `json:"api_debug" env:"API_DEBUG"` ApiGzip bool `json:"api_gzip" env:"API_GZIP"` - GodMode bool `json:"god_mode" env:"GOD_MODE"` + RootMode bool `json:"root_mode" env:"ROOT_MODE"` + RootSecret string `json:"root_secret" env:"ROOT_SECRET"` Pprof bool `json:"pprof" env:"PPROF"` Https bool `json:"https" env:"HTTPS"` GateClientId string `json:"gate_client_id" env:"GATE_CLIENT_ID"` diff --git a/models/entity.go b/models/entity.go index 06cc98b22..af6433a45 100644 --- a/models/entity.go +++ b/models/entity.go @@ -53,6 +53,7 @@ type Entity struct { Attributes Attributes `json:"attributes"` Settings Attributes `json:"settings"` ParentId *common.EntityId `json:"parent_id"` + Storage []*EntityStorage `json:"storage"` Hidden bool `json:"hidden"` AutoLoad bool `json:"auto_load"` IsLoaded bool `json:"is_loaded"` diff --git a/plugins/alexa/server.go b/plugins/alexa/server.go index 0761a1898..bc351ab46 100644 --- a/plugins/alexa/server.go +++ b/plugins/alexa/server.go @@ -70,10 +70,9 @@ func NewServer(adaptors *adaptors.Adaptors, // Start ... func (s *Server) Start() { - if s.isStarted.Load() { + if !s.isStarted.CompareAndSwap(false, true) { return } - s.isStarted.Store(true) s.init() @@ -119,10 +118,9 @@ func (s *Server) init() { // Stop ... func (s *Server) Stop() { - if !s.isStarted.Load() { + if !s.isStarted.CompareAndSwap(true, false) { return } - s.isStarted.Store(false) //todo fix //s.gate.SetAlexaApiEngine(nil) diff --git a/plugins/alexa/skill.go b/plugins/alexa/skill.go index bdf79b9fd..bba0ed7f6 100644 --- a/plugins/alexa/skill.go +++ b/plugins/alexa/skill.go @@ -19,7 +19,7 @@ package alexa import ( - "fmt" + "github.com/pkg/errors" "github.com/e154/smart-home/adaptors" m "github.com/e154/smart-home/models" @@ -75,7 +75,7 @@ func (h *Skill) OnLaunch(_ *gin.Context, req *Request, resp *Response) { } h.jsBind.update(req, resp) if _, err := h.engine.Engine().AssertFunction("skillOnLaunch"); err != nil { - log.Error(err.Error()) + log.Error(errors.Wrapf(err, "skill id: %d", h.model.Id).Error()) } } @@ -90,11 +90,11 @@ func (h *Skill) OnIntent(_ *gin.Context, req *Request, resp *Response) { h.jsBind.update(req, resp) if _, err := h.engine.Engine().EvalScript(intent.Script); err != nil { - log.Error(fmt.Sprintf("%+v", err)) + log.Error(errors.Wrapf(err, "skill id: %d", h.model.Id).Error()) return } if _, err := h.engine.Engine().AssertFunction("skillOnIntent"); err != nil { - log.Error(fmt.Sprintf("%+v", err)) + log.Error(errors.Wrapf(err, "skill id: %d", h.model.Id).Error()) return } } @@ -113,7 +113,7 @@ func (h *Skill) OnSessionEnded(_ *gin.Context, req *Request, resp *Response) { h.jsBind.update(req, resp) if _, err := h.engine.Engine().AssertFunction("skillOnSessionEnd"); err != nil { - log.Error(err.Error()) + log.Error(errors.Wrapf(err, "skill id: %d", h.model.Id).Error()) } } diff --git a/plugins/cgminer/actor.go b/plugins/cgminer/actor.go index 552268a4b..9d7c9cdcf 100644 --- a/plugins/cgminer/actor.go +++ b/plugins/cgminer/actor.go @@ -33,7 +33,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor miner IMiner actionPool chan events.EventCallEntityAction } @@ -138,15 +138,10 @@ func NewActor(entity *m.Entity, } } - for _, engine := range actor.ScriptEngines { - engine.Spawn(func(engine *scripts.Engine) { - if _, err = engine.EvalString(fmt.Sprintf("const ENTITY_ID = \"%s\";", entity.Id)); err != nil { - log.Error(err.Error()) - } - engine.PushFunction("Miner", actor.miner.Bind()) - engine.Do() - }) - } + actor.ScriptsEngine.Spawn(func(engine *scripts.Engine) { + engine.PushFunction("Miner", actor.miner.Bind()) + engine.Do() + }) // action worker go func() { @@ -170,27 +165,9 @@ func (e *Actor) Spawn() { // SetState ... func (e *Actor) SetState(params supervisor.EntityStateParams) error { - oldState := e.GetEventState() - - e.Now(oldState) - - if params.NewState != nil { - state := e.States[*params.NewState] - e.State = &state - e.State.ImageUrl = state.ImageUrl - } - - e.AttrMu.Lock() - _, _ = e.Attrs.Deserialize(params.AttributeValues) - e.AttrMu.Unlock() - - go e.SaveState(events.EventStateChanged{ - StorageSave: params.StorageSave, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SetActorState(params.NewState) + e.DeserializeAttr(params.AttributeValues) + e.SaveState(false, params.StorageSave) return nil } @@ -205,15 +182,17 @@ func (e *Actor) addAction(event events.EventCallEntityAction) { } func (e *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := e.Actions[msg.ActionName] - if !ok { - log.Warnf("action %s not found", msg.ActionName) - return - } - if action.ScriptEngine.Engine() == nil { - return + if action, ok := e.Actions[msg.ActionName]; ok { + if action.ScriptEngine != nil && action.ScriptEngine.Engine() != nil { + if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } + return + } } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } } } diff --git a/plugins/cpuspeed/actor.go b/plugins/cpuspeed/actor.go index d88d68942..98b34220e 100644 --- a/plugins/cpuspeed/actor.go +++ b/plugins/cpuspeed/actor.go @@ -25,14 +25,13 @@ import ( "github.com/shirou/gopsutil/v3/cpu" "github.com/e154/smart-home/common" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor cores int64 model string mhz float64 @@ -84,9 +83,6 @@ func (e *Actor) selfUpdate() { e.updateLock.Lock() defer e.updateLock.Unlock() - oldState := e.GetEventState() - e.Now(oldState) - // export CGO_ENABLED=1 timeStats, err := cpu.Times(false) if err != nil { @@ -123,11 +119,5 @@ func (e *Actor) selfUpdate() { // "all": common.Rounding32(e.all.Value(), 2), //}) - go e.SaveState(events.EventStateChanged{ - StorageSave: false, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, false) } diff --git a/plugins/email/actor.go b/plugins/email/actor.go index e9cd94956..1cbd0683f 100644 --- a/plugins/email/actor.go +++ b/plugins/email/actor.go @@ -27,7 +27,6 @@ import ( "github.com/e154/smart-home/adaptors" "github.com/e154/smart-home/common" "github.com/e154/smart-home/common/apperr" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/plugins/notify" notifyCommon "github.com/e154/smart-home/plugins/notify/common" @@ -36,7 +35,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor adaptors *adaptors.Adaptors notify *notify.Notify Auth string @@ -115,9 +114,6 @@ func (e *Actor) Send(address string, message *m.Message) error { // UpdateStatus ... func (e *Actor) UpdateStatus() (err error) { - oldState := e.GetEventState() - now := e.Now(oldState) - var attributeValues = make(m.AttributeValue) // ... @@ -127,25 +123,10 @@ func (e *Actor) UpdateStatus() (err error) { if err != nil { log.Warn(err.Error()) } - - if oldState.LastUpdated != nil { - delta := now.Sub(*oldState.LastUpdated).Milliseconds() - //fmt.Println("delta", delta) - if delta < 200 { - e.AttrMu.Unlock() - return - } - } } e.AttrMu.Unlock() - go e.SaveState(events.EventStateChanged{ - StorageSave: true, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, true) return } diff --git a/plugins/hdd/actor.go b/plugins/hdd/actor.go index 8c49bf7cf..9f4feb434 100644 --- a/plugins/hdd/actor.go +++ b/plugins/hdd/actor.go @@ -31,7 +31,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor cores int64 model string total metrics.Gauge @@ -93,9 +93,6 @@ func (e *Actor) selfUpdate() { e.updateLock.Lock() defer e.updateLock.Unlock() - oldState := e.GetEventState() - e.Now(oldState) - var mountPoint = "/" if e.MountPoint != "" { mountPoint = e.MountPoint @@ -114,11 +111,5 @@ func (e *Actor) selfUpdate() { e.Attrs[AttrInodesUsedPercent].Value = r.InodesUsedPercent e.AttrMu.Unlock() } - go e.SaveState(events.EventStateChanged{ - StorageSave: false, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, false) } diff --git a/plugins/html5_notify/plugin.go b/plugins/html5_notify/plugin.go index 7bd6412f5..24dd29912 100644 --- a/plugins/html5_notify/plugin.go +++ b/plugins/html5_notify/plugin.go @@ -132,7 +132,7 @@ func (p *plugin) Send(address string, message *m.Message) (err error) { attr := NewMessageParams() _, _ = attr.Deserialize(message.Attributes) - p.Service.EventBus().Publish("system/plugins/html5_notify", events.EventDirectMessage{ + p.Service.EventBus().Publish("system/dashboard", events.EventDirectMessage{ UserID: userID, Query: "html5_notify", Message: Notification{ diff --git a/plugins/logs/actor.go b/plugins/logs/actor.go index 46e025d24..b9d500995 100644 --- a/plugins/logs/actor.go +++ b/plugins/logs/actor.go @@ -19,20 +19,16 @@ package logs import ( - "sync" - - "github.com/e154/smart-home/common/events" - m "github.com/e154/smart-home/models" - "github.com/rcrowley/go-metrics" "github.com/e154/smart-home/common" + m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor cores int64 model string ErrTotal metrics.Counter @@ -41,7 +37,6 @@ type Actor struct { WarnTotal metrics.Counter WarnToday metrics.Counter WarnYesterday metrics.Counter - updateLock *sync.Mutex } // NewActor ... @@ -56,7 +51,6 @@ func NewActor(entity *m.Entity, WarnTotal: metrics.NewCounter(), WarnToday: metrics.NewCounter(), WarnYesterday: metrics.NewCounter(), - updateLock: &sync.Mutex{}, } if entity != nil { @@ -82,12 +76,6 @@ func (e *Actor) Spawn() { func (e *Actor) selfUpdate() { - e.updateLock.Lock() - defer e.updateLock.Unlock() - - oldState := e.GetEventState() - e.Now(oldState) - e.AttrMu.Lock() e.Attrs[AttrErrTotal].Value = e.ErrTotal.Count() e.Attrs[AttrErrToday].Value = e.ErrToday.Count() @@ -97,13 +85,7 @@ func (e *Actor) selfUpdate() { e.Attrs[AttrWarnYesterday].Value = e.WarnYesterday.Count() e.AttrMu.Unlock() - go e.SaveState(events.EventStateChanged{ - StorageSave: true, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, true) } func (e *Actor) LogsHook(level common.LogLevel) { diff --git a/plugins/memory/actor.go b/plugins/memory/actor.go index e00916ed8..941736372 100644 --- a/plugins/memory/actor.go +++ b/plugins/memory/actor.go @@ -25,14 +25,13 @@ import ( "github.com/shirou/gopsutil/v3/mem" "github.com/e154/smart-home/common" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor total metrics.Gauge free metrics.Gauge usedPercent metrics.GaugeFloat64 @@ -72,9 +71,6 @@ func (e *Actor) selfUpdate() { e.updateLock.Lock() defer e.updateLock.Unlock() - oldState := e.GetEventState() - e.Now(oldState) - v, _ := mem.VirtualMemory() e.total.Update(int64(v.Total)) e.free.Update(int64(v.Free)) @@ -92,11 +88,5 @@ func (e *Actor) selfUpdate() { // "used_percent": usedPercent, //}) - go e.SaveState(events.EventStateChanged{ - StorageSave: false, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, false) } diff --git a/plugins/memory_app/actor.go b/plugins/memory_app/actor.go index ffb480e9b..c1f72bd10 100644 --- a/plugins/memory_app/actor.go +++ b/plugins/memory_app/actor.go @@ -23,14 +23,13 @@ import ( "sync" "time" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor updateLock *sync.Mutex } @@ -63,9 +62,6 @@ func (e *Actor) selfUpdate() { e.updateLock.Lock() defer e.updateLock.Unlock() - oldState := e.GetEventState() - e.Now(oldState) - var s runtime.MemStats runtime.ReadMemStats(&s) @@ -82,11 +78,5 @@ func (e *Actor) selfUpdate() { // "total_alloc": float32(s.TotalAlloc), //}) - go e.SaveState(events.EventStateChanged{ - StorageSave: false, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, false) } diff --git a/plugins/messagebird/actor.go b/plugins/messagebird/actor.go index c5fb209f5..a2416d5b6 100644 --- a/plugins/messagebird/actor.go +++ b/plugins/messagebird/actor.go @@ -31,7 +31,6 @@ import ( "github.com/e154/smart-home/common" "github.com/e154/smart-home/common/apperr" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/plugins/notify" notifyCommon "github.com/e154/smart-home/plugins/notify/common" @@ -40,7 +39,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor AccessToken string Name string notify *notify.Notify @@ -169,9 +168,6 @@ func (e *Actor) UpdateBalance() (bal Balance, err error) { e.balanceLock.Lock() defer e.balanceLock.Unlock() - oldState := e.GetEventState() - now := e.Now(oldState) - var b *balance.Balance if common.TestMode() { b = &balance.Balance{ @@ -200,25 +196,10 @@ func (e *Actor) UpdateBalance() (bal Balance, err error) { if err != nil { log.Warn(err.Error()) } - - if oldState.LastUpdated != nil { - delta := now.Sub(*oldState.LastUpdated).Milliseconds() - //fmt.Println("delta", delta) - if delta < 200 { - e.AttrMu.Unlock() - return - } - } } e.AttrMu.Unlock() - go e.SaveState(events.EventStateChanged{ - StorageSave: true, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, true) return } diff --git a/plugins/modbus_rtu/actor.go b/plugins/modbus_rtu/actor.go index 520a9fdc9..5cad00024 100644 --- a/plugins/modbus_rtu/actor.go +++ b/plugins/modbus_rtu/actor.go @@ -22,8 +22,9 @@ import ( "fmt" "sync" - "github.com/e154/smart-home/common/events" + "github.com/pkg/errors" + "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/plugins/node" "github.com/e154/smart-home/system/scripts" @@ -32,7 +33,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor scriptService scripts.ScriptService actionPool chan events.EventCallEntityAction stateMu *sync.Mutex @@ -67,13 +68,10 @@ func NewActor(entity *m.Entity, } } - for _, engine := range actor.ScriptEngines { - engine.Spawn(func(engine *scripts.Engine) { - engine.EvalString(fmt.Sprintf("const ENTITY_ID = \"%s\";", entity.Id)) - engine.PushFunction("ModbusRtu", NewModbusRtu(service.EventBus(), actor)) - engine.Do() - }) - } + actor.ScriptsEngine.Spawn(func(engine *scripts.Engine) { + engine.PushFunction("ModbusRtu", NewModbusRtu(service.EventBus(), actor)) + engine.Do() + }) // action worker go func() { @@ -96,27 +94,9 @@ func (e *Actor) Spawn() { // SetState ... func (e *Actor) SetState(params supervisor.EntityStateParams) error { - oldState := e.GetEventState() - - e.Now(oldState) - - if params.NewState != nil { - state := e.States[*params.NewState] - e.State = &state - e.State.ImageUrl = state.ImageUrl - } - - e.AttrMu.Lock() - _, _ = e.Attrs.Deserialize(params.AttributeValues) - e.AttrMu.Unlock() - - go e.SaveState(events.EventStateChanged{ - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - StorageSave: params.StorageSave, - }) + e.SetActorState(params.NewState) + e.DeserializeAttr(params.AttributeValues) + e.SaveState(false, params.StorageSave) return nil } @@ -126,13 +106,18 @@ func (e *Actor) addAction(event events.EventCallEntityAction) { } func (e *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := e.Actions[msg.ActionName] - if !ok { - log.Warnf("action %s not found", msg.ActionName) - return + if action, ok := e.Actions[msg.ActionName]; ok { + if action.ScriptEngine != nil && action.ScriptEngine.Engine() != nil { + if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } + return + } } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } } } diff --git a/plugins/modbus_tcp/actor.go b/plugins/modbus_tcp/actor.go index f441c425e..06968eb12 100644 --- a/plugins/modbus_tcp/actor.go +++ b/plugins/modbus_tcp/actor.go @@ -22,18 +22,18 @@ import ( "fmt" "sync" - "github.com/e154/smart-home/system/scripts" + "github.com/pkg/errors" "github.com/e154/smart-home/common/events" - m "github.com/e154/smart-home/models" "github.com/e154/smart-home/plugins/node" + "github.com/e154/smart-home/system/scripts" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor actionPool chan events.EventCallEntityAction stateMu *sync.Mutex } @@ -67,13 +67,10 @@ func NewActor(entity *m.Entity, } } - for _, engine := range actor.ScriptEngines { - engine.Spawn(func(engine *scripts.Engine) { - engine.EvalString(fmt.Sprintf("const ENTITY_ID = \"%s\";", entity.Id)) - engine.PushFunction("ModbusTcp", NewModbusTcp(service.EventBus(), actor)) - engine.Do() - }) - } + actor.ScriptsEngine.Spawn(func(engine *scripts.Engine) { + engine.PushFunction("ModbusTcp", NewModbusTcp(service.EventBus(), actor)) + engine.Do() + }) // action worker go func() { @@ -96,27 +93,9 @@ func (e *Actor) Spawn() { // SetState ... func (e *Actor) SetState(params supervisor.EntityStateParams) error { - oldState := e.GetEventState() - - e.Now(oldState) - - if params.NewState != nil { - state := e.States[*params.NewState] - e.State = &state - e.State.ImageUrl = state.ImageUrl - } - - e.AttrMu.Lock() - _, _ = e.Attrs.Deserialize(params.AttributeValues) - e.AttrMu.Unlock() - - go e.SaveState(events.EventStateChanged{ - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - StorageSave: params.StorageSave, - }) + e.SetActorState(params.NewState) + e.DeserializeAttr(params.AttributeValues) + e.SaveState(false, params.StorageSave) return nil } @@ -126,16 +105,18 @@ func (e *Actor) addAction(event events.EventCallEntityAction) { } func (e *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := e.Actions[msg.ActionName] - if !ok { - log.Warnf("action %s not found", msg.ActionName) - return - } - if action.ScriptEngine.Engine() == nil { - return + if action, ok := e.Actions[msg.ActionName]; ok { + if action.ScriptEngine != nil && action.ScriptEngine.Engine() != nil { + if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } + return + } } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } } } diff --git a/plugins/moon/actor.go b/plugins/moon/actor.go index 9373c3967..e14cdfdc4 100644 --- a/plugins/moon/actor.go +++ b/plugins/moon/actor.go @@ -23,8 +23,6 @@ import ( "sync" "time" - "github.com/e154/smart-home/common/events" - "github.com/e154/smart-home/common/astronomics/moonphase" "github.com/e154/smart-home/common/astronomics/suncalc" m "github.com/e154/smart-home/models" @@ -33,7 +31,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor positionLock *sync.Mutex lat, lon float64 moonAzimuth float64 @@ -94,10 +92,6 @@ func (e *Actor) UpdateMoonPosition(now time.Time) { e.positionLock.Lock() defer e.positionLock.Unlock() - oldState := e.GetEventState() - - e.Now(oldState) - moon := moonphase.New(now) //fmt.Println(moon.PhaseName()) @@ -146,19 +140,11 @@ func (e *Actor) UpdateMoonPosition(now time.Time) { attributeValues[AttrHorizonState] = e.horizonState - if state, ok := e.States[e.horizonState]; ok { - e.State = &state - } + e.SetActorState(&e.horizonState) //log.Debugf("Moon horizonState %v", e.horizonState) e.DeserializeAttr(attributeValues) - go e.SaveState(events.EventStateChanged{ - StorageSave: true, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, true) } diff --git a/plugins/mqtt/actor.go b/plugins/mqtt/actor.go index f68017fb4..04a8a77e5 100644 --- a/plugins/mqtt/actor.go +++ b/plugins/mqtt/actor.go @@ -19,20 +19,20 @@ package mqtt import ( - "fmt" "sync" - "github.com/e154/smart-home/system/scripts" + "github.com/pkg/errors" "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/mqtt" + "github.com/e154/smart-home/system/scripts" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor message *Message mqttMessageQueue chan *Message actionPool chan events.EventCallEntityAction @@ -63,13 +63,10 @@ func NewActor(entity *m.Entity, } } - for _, engine := range actor.ScriptEngines { - engine.Spawn(func(engine *scripts.Engine) { - engine.EvalString(fmt.Sprintf("const ENTITY_ID = \"%s\";", entity.Id)) - engine.PushStruct("message", actor.message) - engine.Do() - }) - } + actor.ScriptsEngine.Spawn(func(engine *scripts.Engine) { + engine.PushStruct("message", actor.message) + engine.Do() + }) if actor.Setts == nil { actor.Setts = NewSettings() @@ -108,43 +105,10 @@ func (e *Actor) Spawn() { // SetState ... func (e *Actor) SetState(params supervisor.EntityStateParams) error { - e.stateMu.Lock() - defer e.stateMu.Unlock() - - oldState := e.GetEventState() - now := e.Now(oldState) - - if params.NewState != nil { - if state, ok := e.States[*params.NewState]; ok { - e.State = &state - } - } - - e.AttrMu.Lock() - changed, err := e.Attrs.Deserialize(params.AttributeValues) - if !changed { - if err != nil { - log.Warn(err.Error()) - } - if oldState.LastUpdated != nil { - delta := now.Sub(*oldState.LastUpdated).Milliseconds() - //fmt.Println("delta", delta) - if delta < 200 { - e.AttrMu.Unlock() - return nil - } - } - } - e.AttrMu.Unlock() - - go e.SaveState(events.EventStateChanged{ - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - StorageSave: params.StorageSave, - }) + e.SetActorState(params.NewState) + e.DeserializeAttr(params.AttributeValues) + e.SaveState(false, params.StorageSave) return nil } @@ -165,9 +129,10 @@ func (e *Actor) mqttNewMessage(message *Message) { defer e.newMsgMu.Unlock() e.message.Update(message) - for _, engine := range e.ScriptEngines { - if _, err := engine.Engine().AssertFunction(FuncMqttEvent, message); err != nil { - log.Error(err.Error()) + + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncMqttEvent, message); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) return } } @@ -178,15 +143,17 @@ func (e *Actor) addAction(event events.EventCallEntityAction) { } func (e *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := e.Actions[msg.ActionName] - if !ok { - log.Warnf("action %s not found", msg.ActionName) - return - } - if action.ScriptEngine.Engine() == nil { - return + if action, ok := e.Actions[msg.ActionName]; ok { + if action.ScriptEngine != nil && action.ScriptEngine.Engine() != nil { + if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } + return + } } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } } } diff --git a/plugins/mqtt_bridge/actor.go b/plugins/mqtt_bridge/actor.go index 8d6ab0a23..9777d37e6 100644 --- a/plugins/mqtt_bridge/actor.go +++ b/plugins/mqtt_bridge/actor.go @@ -22,16 +22,14 @@ import ( "context" "strings" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor - actionPool chan events.EventCallEntityAction - bridge *MqttBridge + *supervisor.BaseActor + bridge *MqttBridge } // NewActor ... @@ -39,8 +37,7 @@ func NewActor(entity *m.Entity, service supervisor.Service) (actor *Actor) { actor = &Actor{ - BaseActor: supervisor.NewBaseActor(entity, service), - actionPool: make(chan events.EventCallEntityAction, 1000), + BaseActor: supervisor.NewBaseActor(entity, service), } var topics = strings.Split(entity.Settings[AttrTopics].String(), ",") @@ -64,18 +61,10 @@ func NewActor(entity *m.Entity, log.Error(err.Error()) } - // action worker - go func() { - for msg := range actor.actionPool { - actor.runAction(msg) - } - }() - return actor } func (e *Actor) Destroy() { - close(e.actionPool) if err := e.bridge.Shutdown(context.Background()); err != nil { log.Error(err.Error()) } @@ -91,45 +80,9 @@ func (e *Actor) Spawn() { // SetState ... func (e *Actor) SetState(params supervisor.EntityStateParams) error { - oldState := e.GetEventState() - - e.Now(oldState) - - if params.NewState != nil { - state := e.States[*params.NewState] - e.State = &state - e.State.ImageUrl = state.ImageUrl - } - - e.AttrMu.Lock() - _, _ = e.Attrs.Deserialize(params.AttributeValues) - e.AttrMu.Unlock() - - go e.SaveState(events.EventStateChanged{ - StorageSave: params.StorageSave, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SetActorState(params.NewState) + e.DeserializeAttr(params.AttributeValues) + e.SaveState(false, params.StorageSave) return nil } - -func (e *Actor) addAction(event events.EventCallEntityAction) { - e.actionPool <- event -} - -func (e *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := e.Actions[msg.ActionName] - if !ok { - log.Warnf("action %s not found", msg.ActionName) - return - } - if action.ScriptEngine.Engine() == nil { - return - } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) - } -} diff --git a/plugins/mqtt_bridge/plugin.go b/plugins/mqtt_bridge/plugin.go index dfd5904d9..dc2bd6c39 100644 --- a/plugins/mqtt_bridge/plugin.go +++ b/plugins/mqtt_bridge/plugin.go @@ -22,7 +22,6 @@ import ( "context" "embed" - "github.com/e154/smart-home/common/events" "github.com/e154/smart-home/common/logger" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/mqtt" @@ -63,9 +62,6 @@ func (p *plugin) Load(ctx context.Context, service supervisor.Service) (err erro if err = p.Plugin.Load(ctx, service, p.ActorConstructor); err != nil { return } - - _ = p.Service.EventBus().Subscribe("system/entities/+", p.eventHandler) - return nil } @@ -74,9 +70,6 @@ func (p *plugin) Unload(ctx context.Context) (err error) { if err = p.Plugin.Unload(ctx); err != nil { return } - - _ = p.Service.EventBus().Unsubscribe("system/entities/+", p.eventHandler) - return nil } @@ -91,20 +84,6 @@ func (p *plugin) Name() string { return Name } -func (p *plugin) eventHandler(topic string, msg interface{}) { - - switch v := msg.(type) { - case events.EventStateChanged: - case events.EventCallEntityAction: - value, ok := p.Actors.Load(v.EntityId) - if !ok { - return - } - actor := value.(*Actor) - actor.addAction(v) - } -} - // Type ... func (p *plugin) Type() supervisor.PluginType { return supervisor.PluginInstallable @@ -123,10 +102,8 @@ func (p *plugin) Version() string { // Options ... func (p *plugin) Options() m.PluginOptions { return m.PluginOptions{ - Actors: true, - ActorCustomAttrs: true, - ActorCustomActions: true, - ActorStates: supervisor.ToEntityStateShort(NewStates()), - ActorSetts: NewSettings(), + Actors: true, + ActorStates: supervisor.ToEntityStateShort(NewStates()), + ActorSetts: NewSettings(), } } diff --git a/plugins/neural_network/actor.go b/plugins/neural_network/actor.go index f590d0023..3ea87d12c 100644 --- a/plugins/neural_network/actor.go +++ b/plugins/neural_network/actor.go @@ -27,7 +27,7 @@ import ( ) type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor actionPool chan events.EventCallEntityAction network1 *Network1 network2 *Network2 diff --git a/plugins/node/actor.go b/plugins/node/actor.go index d30608273..5c66f11e9 100644 --- a/plugins/node/actor.go +++ b/plugins/node/actor.go @@ -21,12 +21,11 @@ package node import ( "encoding/json" "fmt" + "github.com/e154/smart-home/common" "strings" "sync" "time" - "github.com/e154/smart-home/common/events" - m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/mqtt" "github.com/e154/smart-home/system/supervisor" @@ -34,7 +33,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor mqttClient mqtt.MqttCli stateMu *sync.Mutex quit chan struct{} @@ -55,15 +54,15 @@ func NewActor(entity *m.Entity, lastState: m.AttributeValue{}, } - if actor.Attrs == nil { + if actor.Attrs == nil || len(actor.Attrs) == 0 { actor.Attrs = NewAttr() } - if actor.States == nil { + if actor.States == nil || len(actor.States) == 0 { actor.States = NewStates() } - if actor.Setts == nil { + if actor.Setts == nil || len(actor.Setts) == 0 { actor.Setts = NewSettings() } @@ -82,8 +81,7 @@ func (e *Actor) Destroy() { // Spawn ... func (e *Actor) Spawn() { - state := e.States["wait"] - e.State = &state + e.SetActorState(common.String("wait")) go func() { e.quit = make(chan struct{}) @@ -152,27 +150,9 @@ func (e *Actor) updateStatus() { state = "connected" } - oldState := e.GetEventState() - e.Now(oldState) - - e.AttrMu.Lock() - changed, _ := e.Attrs.Deserialize(e.lastState) - e.AttrMu.Unlock() - - if !changed && e.State.Name == state { - return - } - - if state, ok := e.States[state]; ok { - e.State = &state - } - - go e.SaveState(events.EventStateChanged{ - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.DeserializeAttr(e.lastState) + e.SetActorState(&state) + e.SaveState(false, true) } func (e *Actor) localTopic(r string) string { diff --git a/plugins/onvif/actor.go b/plugins/onvif/actor.go index 662cf7a1f..ca13016c1 100644 --- a/plugins/onvif/actor.go +++ b/plugins/onvif/actor.go @@ -19,7 +19,7 @@ package onvif import ( - "fmt" + "github.com/pkg/errors" "github.com/e154/smart-home/common" "github.com/e154/smart-home/common/events" @@ -31,7 +31,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor client *Client snapshotUri *string } @@ -58,13 +58,10 @@ func NewActor(entity *m.Entity, } } - for _, engine := range actor.ScriptEngines { - engine.Spawn(func(engine *scripts.Engine) { - engine.EvalString(fmt.Sprintf("const ENTITY_ID = \"%s\";", entity.Id)) - engine.PushStruct("Camera", clientBind) - engine.Do() - }) - } + actor.ScriptsEngine.Spawn(func(engine *scripts.Engine) { + engine.PushStruct("Camera", clientBind) + engine.Do() + }) if actor.Attrs == nil { actor.Attrs = NewAttr() @@ -98,27 +95,9 @@ func (a *Actor) Spawn() { // SetState ... func (a *Actor) SetState(params supervisor.EntityStateParams) error { - oldState := a.GetEventState() - - a.Now(oldState) - - if params.NewState != nil { - state := a.States[*params.NewState] - a.State = &state - a.State.ImageUrl = state.ImageUrl - } - - a.AttrMu.Lock() - _, _ = a.Attrs.Deserialize(params.AttributeValues) - a.AttrMu.Unlock() - - go a.SaveState(events.EventStateChanged{ - StorageSave: params.StorageSave, - PluginName: a.Id.PluginName(), - EntityId: a.Id, - OldState: oldState, - NewState: a.GetEventState(), - }) + a.SetActorState(params.NewState) + a.DeserializeAttr(params.AttributeValues) + a.SaveState(false, params.StorageSave) return nil } @@ -128,16 +107,18 @@ func (a *Actor) addAction(event events.EventCallEntityAction) { } func (a *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := a.Actions[msg.ActionName] - if !ok { - log.Warnf("action %s not found", msg.ActionName) - return - } - if action.ScriptEngine.Engine() == nil { - return + if action, ok := a.Actions[msg.ActionName]; ok { + if action.ScriptEngine != nil && action.ScriptEngine.Engine() != nil { + if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", a.Id).Error()) + } + return + } } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) + if a.ScriptsEngine != nil && a.ScriptsEngine.Engine() != nil { + if _, err := a.ScriptsEngine.AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", a.Id).Error()) + } } } diff --git a/plugins/plugins.go b/plugins/plugins.go index eaf72d974..8e0d98bce 100644 --- a/plugins/plugins.go +++ b/plugins/plugins.go @@ -51,6 +51,7 @@ import ( _ "github.com/e154/smart-home/plugins/version" _ "github.com/e154/smart-home/plugins/weather_met" _ "github.com/e154/smart-home/plugins/weather_owm" + _ "github.com/e154/smart-home/plugins/webdav" _ "github.com/e154/smart-home/plugins/webpush" _ "github.com/e154/smart-home/plugins/zigbee2mqtt" ) diff --git a/plugins/scene/actor.go b/plugins/scene/actor.go index 6e04d58c9..403cfabee 100644 --- a/plugins/scene/actor.go +++ b/plugins/scene/actor.go @@ -21,17 +21,19 @@ package scene import ( "sync" - "github.com/e154/smart-home/common/events" + "github.com/pkg/errors" + "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor - eventPool chan events.EventCallScene - stateMu *sync.Mutex + *supervisor.BaseActor + eventPool chan events.EventCallScene + actionPool chan events.EventCallEntityAction + stateMu *sync.Mutex } // NewActor ... @@ -39,9 +41,10 @@ func NewActor(entity *m.Entity, service supervisor.Service) (actor *Actor, err error) { actor = &Actor{ - BaseActor: supervisor.NewBaseActor(entity, service), - eventPool: make(chan events.EventCallScene, 99), - stateMu: &sync.Mutex{}, + BaseActor: supervisor.NewBaseActor(entity, service), + eventPool: make(chan events.EventCallScene, 99), + actionPool: make(chan events.EventCallEntityAction, 1000), + stateMu: &sync.Mutex{}, } // action worker @@ -67,10 +70,30 @@ func (e *Actor) addEvent(event events.EventCallScene) { } func (e *Actor) runEvent(msg events.EventCallScene) { - for _, engine := range e.ScriptEngines { - if _, err := engine.Engine().AssertFunction(FuncSceneEvent, msg.EntityId); err != nil { + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncSceneEvent, msg.EntityId); err != nil { log.Error(err.Error()) return } } } + +func (e *Actor) addAction(event events.EventCallEntityAction) { + e.actionPool <- event +} + +func (e *Actor) runAction(msg events.EventCallEntityAction) { + if action, ok := e.Actions[msg.ActionName]; ok { + if action.ScriptEngine != nil && action.ScriptEngine.Engine() != nil { + if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } + return + } + } + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } + } +} diff --git a/plugins/scene/plugin.go b/plugins/scene/plugin.go index a17f71de4..945431407 100644 --- a/plugins/scene/plugin.go +++ b/plugins/scene/plugin.go @@ -93,11 +93,7 @@ func (p *plugin) eventHandler(_ string, msg interface{}) { return } actor := value.(*Actor) - actor.addEvent(events.EventCallScene{ - PluginName: v.PluginName, - EntityId: v.EntityId, - Args: v.Args, - }) + go actor.addAction(v) case events.EventCallScene: value, ok := p.Actors.Load(v.EntityId) @@ -105,7 +101,7 @@ func (p *plugin) eventHandler(_ string, msg interface{}) { return } actor := value.(*Actor) - actor.addEvent(v) + go actor.addEvent(v) default: //fmt.Printf("new event: %v\n", reflect.TypeOf(v).String()) diff --git a/plugins/scene/types.go b/plugins/scene/types.go index 771895397..a8df67acf 100644 --- a/plugins/scene/types.go +++ b/plugins/scene/types.go @@ -23,6 +23,8 @@ const ( Name = "scene" // EntityScene ... EntityScene = string("scene") + // FuncEntityAction ... + FuncEntityAction = "entityAction" Version = "0.0.1" ) diff --git a/plugins/sensor/actor.go b/plugins/sensor/actor.go index 4ca41a005..e73a33688 100644 --- a/plugins/sensor/actor.go +++ b/plugins/sensor/actor.go @@ -19,6 +19,8 @@ package sensor import ( + "github.com/pkg/errors" + "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" @@ -26,7 +28,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor actionPool chan events.EventCallEntityAction } @@ -61,27 +63,9 @@ func (e *Actor) Spawn() { // SetState ... func (e *Actor) SetState(params supervisor.EntityStateParams) error { - oldState := e.GetEventState() - - e.Now(oldState) - - if params.NewState != nil { - state := e.States[*params.NewState] - e.State = &state - e.State.ImageUrl = state.ImageUrl - } - - e.AttrMu.Lock() - _, _ = e.Attrs.Deserialize(params.AttributeValues) - e.AttrMu.Unlock() - - go e.SaveState(events.EventStateChanged{ - StorageSave: params.StorageSave, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SetActorState(params.NewState) + e.DeserializeAttr(params.AttributeValues) + e.SaveState(false, params.StorageSave) return nil } @@ -91,15 +75,17 @@ func (e *Actor) addAction(event events.EventCallEntityAction) { } func (e *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := e.Actions[msg.ActionName] - if !ok { - log.Warnf("action '%s' not found", msg.ActionName) - return - } - if action.ScriptEngine.Engine() == nil { - return + if action, ok := e.Actions[msg.ActionName]; ok { + if action.ScriptEngine != nil && action.ScriptEngine.Engine() != nil { + if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } + return + } } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } } } diff --git a/plugins/slack/actor.go b/plugins/slack/actor.go index da2ac87e7..6c28932f1 100644 --- a/plugins/slack/actor.go +++ b/plugins/slack/actor.go @@ -33,7 +33,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor notify *notify.Notify Token string UserName string diff --git a/plugins/speedtest/actor.go b/plugins/speedtest/actor.go index f2feca02d..0611894ac 100644 --- a/plugins/speedtest/actor.go +++ b/plugins/speedtest/actor.go @@ -19,6 +19,7 @@ package speedtest import ( + "github.com/e154/smart-home/common" "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" @@ -28,7 +29,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor isStarted *atomic.Bool } @@ -112,12 +113,6 @@ func (e *Actor) runTest() { func (e *Actor) updateState(state *speedtest.Server) { - oldState := e.GetEventState() - - e.Now(oldState) - - //debug.Println(state) - var attributeValues = make(m.AttributeValue) if state != nil { attributeValues[AttrDLSpeed] = state.DLSpeed @@ -131,22 +126,15 @@ func (e *Actor) updateState(state *speedtest.Server) { attributeValues[AttrPoint] = []interface{}{state.Lon, state.Lat} s := e.States[StateCompleted] - e.State = &s + e.SetActorState(common.String(s.Name)) } else { s := e.States[StateInProcess] - e.State = &s + e.SetActorState(common.String(s.Name)) } e.DeserializeAttr(attributeValues) - go e.SaveState(events.EventStateChanged{ - StorageSave: state != nil, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - DoNotSaveMetric: state == nil, - }) + e.SaveState(state == nil, state != nil) } func (e *Actor) addAction(event events.EventCallEntityAction) { diff --git a/plugins/sun/actor.go b/plugins/sun/actor.go index 47e04828e..84364afda 100644 --- a/plugins/sun/actor.go +++ b/plugins/sun/actor.go @@ -19,13 +19,12 @@ package sun import ( + "github.com/e154/smart-home/common" "math" "sort" "sync" "time" - "github.com/e154/smart-home/common/events" - "github.com/e154/smart-home/common/astronomics/suncalc" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" @@ -33,7 +32,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor positionLock *sync.Mutex lat, lon, elevation float64 solarAzimuth float64 @@ -95,10 +94,6 @@ func (e *Actor) UpdateSunPosition(now time.Time) { e.positionLock.Lock() defer e.positionLock.Unlock() - oldState := e.GetEventState() - - e.Now(oldState) - sunrisePos := suncalc.GetSunPosition(now, e.lat, e.lon) e.solarAzimuth = sunrisePos.Azimuth*180/math.Pi + 180 e.solarElevation = sunrisePos.Altitude * 180 / math.Pi @@ -145,9 +140,7 @@ func (e *Actor) UpdateSunPosition(now time.Time) { for _, t := range dayTimes { if now.Sub(t.Time).Minutes() > 0 { e.phase = t.MorningName - if state, ok := e.States[t.MorningName]; ok { - e.State = &state - } + e.SetActorState(common.String(t.MorningName)) } } //log.Debugf("Sun phase %v", e.phase) @@ -166,11 +159,5 @@ func (e *Actor) UpdateSunPosition(now time.Time) { e.DeserializeAttr(attributeValues) - go e.SaveState(events.EventStateChanged{ - StorageSave: true, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, true) } diff --git a/plugins/telegram/actor.go b/plugins/telegram/actor.go index b3d4a6e0a..6afaed8c3 100644 --- a/plugins/telegram/actor.go +++ b/plugins/telegram/actor.go @@ -41,7 +41,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor isStarted *atomic.Bool AccessToken string bot *tele.Bot @@ -218,9 +218,6 @@ func (e *Actor) getChatList() (list []m.TelegramChat, err error) { // UpdateStatus ... func (e *Actor) UpdateStatus() (err error) { - oldState := e.GetEventState() - now := e.Now(oldState) - var attributeValues = make(m.AttributeValue) // ... @@ -230,25 +227,10 @@ func (e *Actor) UpdateStatus() (err error) { if err != nil { log.Warn(err.Error()) } - - if oldState.LastUpdated != nil { - delta := now.Sub(*oldState.LastUpdated).Milliseconds() - //fmt.Println("delta", delta) - if delta < 200 { - e.AttrMu.Unlock() - return - } - } } e.AttrMu.Unlock() - go e.SaveState(events.EventStateChanged{ - StorageSave: true, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, true) return } @@ -329,22 +311,16 @@ func (e *Actor) addAction(event events.EventCallEntityAction) { func (e *Actor) runAction(msg events.EventCallEntityAction) { if action, ok := e.Actions[msg.ActionName]; ok { - if action.ScriptEngine.Engine() == nil { - return - } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { - log.Error(err.Error()) + if action.ScriptEngine != nil && action.ScriptEngine.Engine() != nil { + if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } return } - return } - - if e.ScriptEngines != nil { - for _, engine := range e.ScriptEngines { - if _, err := engine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { - log.Error(err.Error()) - return - } + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) } } } diff --git a/plugins/twilio/actor.go b/plugins/twilio/actor.go index 751b03e7f..cf81f8fe8 100644 --- a/plugins/twilio/actor.go +++ b/plugins/twilio/actor.go @@ -33,7 +33,6 @@ import ( "github.com/e154/smart-home/common" "github.com/e154/smart-home/common/apperr" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/plugins/notify" notifyCommon "github.com/e154/smart-home/plugins/notify/common" @@ -42,7 +41,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor from string sid string authToken string @@ -189,9 +188,6 @@ func (e *Actor) Balance() (balance Balance, err error) { // UpdateBalance ... func (e *Actor) UpdateBalance() (err error) { - oldState := e.GetEventState() - now := e.Now(oldState) - var balance Balance if common.TestMode() { balance = Balance{ @@ -216,25 +212,10 @@ func (e *Actor) UpdateBalance() (err error) { if err != nil { log.Warn(err.Error()) } - - if oldState.LastUpdated != nil { - delta := now.Sub(*oldState.LastUpdated).Milliseconds() - //fmt.Println("delta", delta) - if delta < 200 { - e.AttrMu.Unlock() - return - } - } } e.AttrMu.Unlock() - go e.SaveState(events.EventStateChanged{ - StorageSave: true, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, true) return } diff --git a/plugins/updater/actor.go b/plugins/updater/actor.go index 85b5de383..58e643417 100644 --- a/plugins/updater/actor.go +++ b/plugins/updater/actor.go @@ -20,13 +20,13 @@ package updater import ( "encoding/json" + "github.com/e154/smart-home/common" "sync" "time" m "github.com/e154/smart-home/models" "github.com/Masterminds/semver" - "github.com/e154/smart-home/common/events" "github.com/e154/smart-home/common/web" "github.com/e154/smart-home/system/supervisor" "github.com/e154/smart-home/version" @@ -34,7 +34,7 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor checkLock *sync.Mutex latestVersion string latestDownloadUrl string @@ -85,13 +85,11 @@ func (e *Actor) setState(v string) { switch v { case "exist_update": - state := e.States["exist_update"] - e.State = &state + e.SetActorState(common.String("exist_update")) e.Value.Store(supervisor.StateOk) return case supervisor.StateError: - state := e.States["error"] - e.State = &state + e.SetActorState(common.String("error")) } e.Value.Store(v) @@ -136,8 +134,6 @@ func (e *Actor) check() { } } - oldState := e.GetEventState() - e.AttrMu.Lock() e.Attrs[AttrUpdaterLatestVersion].Value = e.latestVersion e.Attrs[AttrUpdaterLatestVersionTime].Value = e.latestVersionTime @@ -145,11 +141,5 @@ func (e *Actor) check() { e.Attrs[AttrUpdaterLatestCheck].Value = e.lastCheck e.AttrMu.Unlock() - go e.SaveState(events.EventStateChanged{ - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - StorageSave: true, - }) + e.SaveState(false, true) } diff --git a/plugins/uptime/actor.go b/plugins/uptime/actor.go index 3e7152088..54400561b 100644 --- a/plugins/uptime/actor.go +++ b/plugins/uptime/actor.go @@ -26,14 +26,13 @@ import ( "go.uber.org/atomic" "github.com/e154/smart-home/common" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor appStarted time.Time total *atomic.Uint64 } @@ -70,10 +69,6 @@ func (e *Actor) Spawn() { func (e *Actor) update() { - oldState := e.GetEventState() - - e.Now(oldState) - total, err := GetUptime() if err != nil { return @@ -85,12 +80,7 @@ func (e *Actor) update() { e.Attrs[AttrUptimeTotal].Value = e.total.Load() e.AttrMu.Unlock() - go e.SaveState(events.EventStateChanged{ - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, false) } func (e *Actor) check() { @@ -168,9 +158,6 @@ LOOP: downtimePercent = 0 } - oldState := e.GetEventState() - e.Now(oldState) - var attributeValues = make(m.AttributeValue) attributeValues[AttrAppStarted] = e.appStarted attributeValues[AttrFirstStart] = common.TimeValue(firstStart) @@ -186,13 +173,7 @@ LOOP: e.DeserializeAttr(attributeValues) - go e.SaveState(events.EventStateChanged{ - StorageSave: true, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, true) //fmt.Println("first start", firstStart) //fmt.Println("last shutdown", lastShutdown) diff --git a/plugins/version/actor.go b/plugins/version/actor.go index 3b74b79b5..745da47e3 100644 --- a/plugins/version/actor.go +++ b/plugins/version/actor.go @@ -24,15 +24,13 @@ import ( m "github.com/e154/smart-home/models" - "github.com/e154/smart-home/common/events" - "github.com/e154/smart-home/system/supervisor" "github.com/e154/smart-home/version" ) // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor updateLock *sync.Mutex } @@ -63,9 +61,6 @@ func (e *Actor) selfUpdate() { e.updateLock.Lock() defer e.updateLock.Unlock() - oldState := e.GetEventState() - e.Now(oldState) - var s runtime.MemStats runtime.ReadMemStats(&s) @@ -80,11 +75,5 @@ func (e *Actor) selfUpdate() { e.Attrs[AttrGoVersion].Value = version.GoVersion e.AttrMu.Unlock() - go e.SaveState(events.EventStateChanged{ - StorageSave: false, - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - }) + e.SaveState(false, false) } diff --git a/plugins/weather_met/actor.go b/plugins/weather_met/actor.go index 450f43814..0d8affb18 100644 --- a/plugins/weather_met/actor.go +++ b/plugins/weather_met/actor.go @@ -20,10 +20,10 @@ package weather_met import ( "fmt" + "github.com/e154/smart-home/common" "time" "github.com/e154/smart-home/common/astronomics/suncalc" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/plugins/weather" "github.com/e154/smart-home/system/supervisor" @@ -31,12 +31,11 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor Zone - actionPool chan events.EventCallEntityAction - weather *WeatherMet - winter bool - theme string + weather *WeatherMet + winter bool + theme string } // NewActor ... @@ -44,9 +43,8 @@ func NewActor(entity *m.Entity, service supervisor.Service) *Actor { actor := &Actor{ - BaseActor: supervisor.NewBaseActor(entity, service), - actionPool: make(chan events.EventCallEntityAction, 1000), - weather: NewWeatherMet(service.Adaptors(), service.Crawler()), + BaseActor: supervisor.NewBaseActor(entity, service), + weather: NewWeatherMet(service.Adaptors(), service.Crawler()), } if actor.Attrs == nil { @@ -75,13 +73,6 @@ func NewActor(entity *m.Entity, actor.winter = winter.Bool() } - // action worker - go func() { - for msg := range actor.actionPool { - actor.runAction(msg) - } - }() - return actor } @@ -99,41 +90,13 @@ func (e *Actor) SetState(params supervisor.EntityStateParams) error { return nil } -func (e *Actor) addAction(event events.EventCallEntityAction) { - e.actionPool <- event -} - -func (e *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := e.Actions[msg.ActionName] - if !ok { - log.Warnf("action %s not found", msg.ActionName) - return - } - if action.ScriptEngine.Engine() == nil { - return - } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) - } -} - func (e *Actor) update() { - oldState := e.GetEventState() - - e.Now(oldState) - if !e.updateForecast() { return } - go e.SaveState(events.EventStateChanged{ - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - StorageSave: true, - }) + e.SaveState(false, true) } @@ -157,9 +120,9 @@ func (e *Actor) updateForecast() (changed bool) { var night = suncalc.IsNight(sunrisePos) if val, ok := e.Attrs[weather.AttrWeatherMain]; ok { - state := e.States[val.String()] - e.State = &state - e.State.ImageUrl = weather.GetImagePath(state.Name, e.theme, night, e.winter) + e.SetActorState(common.String(val.String())) + e.SetActorStateImage(weather.GetImagePath(val.String(), e.theme, night, e.winter), nil) + } for i := 1; i < 6; i++ { diff --git a/plugins/weather_owm/actor.go b/plugins/weather_owm/actor.go index 4c142563d..52e146472 100644 --- a/plugins/weather_owm/actor.go +++ b/plugins/weather_owm/actor.go @@ -20,10 +20,10 @@ package weather_owm import ( "fmt" + "github.com/e154/smart-home/common" "time" "github.com/e154/smart-home/common/astronomics/suncalc" - "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/plugins/weather" "github.com/e154/smart-home/system/supervisor" @@ -31,12 +31,11 @@ import ( // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor Zone - actionPool chan events.EventCallEntityAction - weather *WeatherOwm - winter bool - theme string + weather *WeatherOwm + winter bool + theme string } // NewActor ... @@ -44,9 +43,8 @@ func NewActor(entity *m.Entity, service supervisor.Service) *Actor { actor := &Actor{ - BaseActor: supervisor.NewBaseActor(entity, service), - actionPool: make(chan events.EventCallEntityAction, 1000), - weather: NewWeatherOwm(service.Adaptors(), service.Crawler()), + BaseActor: supervisor.NewBaseActor(entity, service), + weather: NewWeatherOwm(service.Adaptors(), service.Crawler()), } if actor.Attrs == nil { @@ -75,13 +73,6 @@ func NewActor(entity *m.Entity, actor.winter = winter.Bool() } - // action worker - go func() { - for msg := range actor.actionPool { - actor.runAction(msg) - } - }() - return actor } @@ -99,41 +90,13 @@ func (e *Actor) SetState(params supervisor.EntityStateParams) error { return nil } -func (e *Actor) addAction(event events.EventCallEntityAction) { - e.actionPool <- event -} - -func (e *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := e.Actions[msg.ActionName] - if !ok { - log.Warnf("action %s not found", msg.ActionName) - return - } - if action.ScriptEngine.Engine() == nil { - return - } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) - } -} - func (e *Actor) update() { - oldState := e.GetEventState() - - e.Now(oldState) - if !e.updateForecast() { return } - go e.SaveState(events.EventStateChanged{ - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - StorageSave: true, - }) + e.SaveState(false, true) } @@ -157,9 +120,8 @@ func (e *Actor) updateForecast() (changed bool) { var night = suncalc.IsNight(sunrisePos) if val, ok := e.Attrs[weather.AttrWeatherMain]; ok { - state := e.States[val.String()] - e.State = &state - e.State.ImageUrl = weather.GetImagePath(state.Name, e.theme, night, e.winter) + e.SetActorState(common.String(val.String())) + e.SetActorStateImage(weather.GetImagePath(val.String(), e.theme, night, e.winter), nil) } for i := 1; i < 6; i++ { diff --git a/plugins/webdav/Readme.md b/plugins/webdav/Readme.md new file mode 100644 index 000000000..d56d7500e --- /dev/null +++ b/plugins/webdav/Readme.md @@ -0,0 +1,3 @@ +### WEBDAV Plugin + +[Documentation](https://e154.github.io/smart-home/docs/plugins/webdav/) diff --git a/plugins/webdav/Readme.ru.md b/plugins/webdav/Readme.ru.md new file mode 100644 index 000000000..c9e58d0ce --- /dev/null +++ b/plugins/webdav/Readme.ru.md @@ -0,0 +1,3 @@ +### Плагин WEBDAV + +[Документация](https://e154.github.io/smart-home/ru/docs/plugins/webdav/) diff --git a/plugins/webdav/fs.go b/plugins/webdav/fs.go new file mode 100644 index 000000000..19e38cfe5 --- /dev/null +++ b/plugins/webdav/fs.go @@ -0,0 +1,70 @@ +// This file is part of the Smart Home +// Program complex distribution https://github.com/e154/smart-home +// Copyright (C) 2024, Filippov Alex +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library. If not, see +// . + +package webdav + +import ( + "context" + "github.com/pkg/errors" + "os" + + "github.com/spf13/afero" + "golang.org/x/net/webdav" +) + +type FS struct { + afero.Fs +} + +func NewFS() *FS { + return &FS{ + Fs: afero.NewMemMapFs(), + } +} + +func (f *FS) Mkdir(ctx context.Context, name string, perm os.FileMode) error { + return errors.New("operation not allowed") +} + +func (f *FS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) { + _, err := f.Fs.Stat(name) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + log.Infof("created new file %s", name) + } else { + return nil, err + } + } + return f.Fs.OpenFile(name, flag, perm) +} + +func (f *FS) RemoveAll(ctx context.Context, name string) error { + return f.Fs.RemoveAll(name) +} + +func (f *FS) Rename(ctx context.Context, oldName, newName string) error { + return f.Fs.Rename(oldName, newName) +} + +func (f *FS) Stat(ctx context.Context, name string) (os.FileInfo, error) { + fileInfo, err := f.Fs.Stat(name) + if err != nil { + return nil, err + } + return fileInfo, err +} diff --git a/plugins/webdav/plugin.go b/plugins/webdav/plugin.go new file mode 100644 index 000000000..8fdb768a8 --- /dev/null +++ b/plugins/webdav/plugin.go @@ -0,0 +1,135 @@ +// This file is part of the Smart Home +// Program complex distribution https://github.com/e154/smart-home +// Copyright (C) 2016-2023, Filippov Alex +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library. If not, see +// . + +package webdav + +import ( + "context" + "embed" + "net/http" + + "github.com/e154/smart-home/common/logger" + m "github.com/e154/smart-home/models" + "github.com/e154/smart-home/system/supervisor" +) + +var ( + log = logger.MustGetLogger("plugins.webdav") +) + +var _ supervisor.Pluggable = (*plugin)(nil) + +//go:embed Readme.md +//go:embed Readme.ru.md +var F embed.FS + +func init() { + supervisor.RegisterPlugin(Name, New) +} + +type plugin struct { + *supervisor.Plugin + server *Server + settings m.Attributes +} + +// New ... +func New() supervisor.Pluggable { + p := &plugin{ + Plugin: supervisor.NewPlugin(), + } + p.F = F + return p +} + +// Load ... +func (p *plugin) Load(ctx context.Context, service supervisor.Service) (err error) { + if err = p.Plugin.Load(ctx, service, nil); err != nil { + return + } + + // load settings + p.settings, err = p.LoadSettings(p) + if err != nil { + log.Warn(err.Error()) + p.settings = NewSettings() + } + + if p.settings == nil { + p.settings = NewSettings() + } + + p.server = NewServer() + p.server.Start(service.Adaptors(), service.ScriptService(), service.EventBus()) + + return +} + +// Unload ... +func (p *plugin) Unload(ctx context.Context) (err error) { + if err = p.Plugin.Unload(ctx); err != nil { + return + } + + p.server.Shutdown() + + return +} + +// Name ... +func (p plugin) Name() string { + return Name +} + +// Type ... +func (p *plugin) Type() supervisor.PluginType { + return supervisor.PluginBuiltIn +} + +// Depends ... +func (p *plugin) Depends() []string { + return nil +} + +// Version ... +func (p *plugin) Version() string { + return Version +} + +// Options ... +func (p *plugin) Options() m.PluginOptions { + return m.PluginOptions{ + Javascript: m.PluginOptionsJs{}, + Setts: NewSettings(), + } +} + +// ServeHTTP ... +func (p *plugin) ServeHTTP(w http.ResponseWriter, r *http.Request) { + username, password, _ := r.BasicAuth() + + if p.settings[AttrAnonymous].Bool() || + username == p.settings[AttrUser].String() && password == p.settings[AttrPassword].Decrypt() { + p.server.ServeHTTP(w, r) + return + } + + w.Header().Set("WWW-Authenticate", `Basic realm="BASIC WebDAV REALM"`) + w.WriteHeader(401) + w.Write([]byte("401 Unauthorized\n")) +} diff --git a/plugins/webdav/scripts.go b/plugins/webdav/scripts.go new file mode 100644 index 000000000..dc7fd9fbf --- /dev/null +++ b/plugins/webdav/scripts.go @@ -0,0 +1,396 @@ +// This file is part of the Smart Home +// Program complex distribution https://github.com/e154/smart-home +// Copyright (C) 2024, Filippov Alex +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library. If not, see +// . + +package webdav + +import ( + "context" + "fmt" + "os" + "path/filepath" + "sync" + "time" + + "github.com/pkg/errors" + "github.com/spf13/afero" + "go.uber.org/atomic" + + "github.com/e154/smart-home/adaptors" + "github.com/e154/smart-home/common/events" + m "github.com/e154/smart-home/models" + "github.com/e154/smart-home/system/bus" + "github.com/e154/smart-home/system/scripts" +) + +type Scripts struct { + *FS + adaptors *adaptors.Adaptors + scriptService scripts.ScriptService + eventBus bus.Bus + rootDir string + done chan struct{} + isStarted *atomic.Bool + sync.Mutex + fileInfos map[string]*FileInfo +} + +func NewScripts(fs *FS) *Scripts { + return &Scripts{ + FS: fs, + rootDir: "scripts", + isStarted: atomic.NewBool(false), + } +} + +func (s *Scripts) Start(adaptors *adaptors.Adaptors, scriptService scripts.ScriptService, eventBus bus.Bus) { + if !s.isStarted.CompareAndSwap(false, true) { + return + } + + s.adaptors = adaptors + s.eventBus = eventBus + s.scriptService = scriptService + s.fileInfos = make(map[string]*FileInfo) + + s.preload() + + s.done = make(chan struct{}) + go func() { + ticker := time.NewTicker(time.Second * 5) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + s.syncFiles() + case <-s.done: + return + } + } + }() + + _ = eventBus.Subscribe("system/models/scripts/+", s.eventHandler) +} + +func (s *Scripts) Shutdown() { + if !s.isStarted.CompareAndSwap(true, false) { + return + } + + s.fileInfos = nil + close(s.done) + _ = s.eventBus.Unsubscribe("system/models/scripts/+", s.eventHandler) +} + +// eventHandler ... +func (s *Scripts) eventHandler(_ string, message interface{}) { + + switch msg := message.(type) { + case events.EventUpdatedScriptModel: + go s.eventUpdateScript(msg) + case events.EventRemovedScriptModel: + go s.eventRemoveScript(msg) + case events.EventCreatedScriptModel: + go s.eventAddScript(msg) + } +} + +func (s *Scripts) eventAddScript(msg events.EventCreatedScriptModel) { + if msg.Owner == events.OwnerSystem { + return + } + filePath := s.getFilePath(msg.Script) + if err := afero.WriteFile(s.Fs, filePath, []byte(msg.Script.Source), 0644); err != nil { + log.Error(err.Error()) + return + } + _ = s.Fs.Chtimes(filePath, msg.Script.CreatedAt, msg.Script.CreatedAt) + info, err := s.Fs.Stat(filePath) + if err != nil { + log.Error(err.Error()) + return + } + s.Lock() + s.fileInfos[filePath] = &FileInfo{ + Size: info.Size(), + ModTime: info.ModTime(), + LastCheck: time.Now(), + IsInitialized: true, + } + s.Unlock() +} + +func (s *Scripts) eventRemoveScript(msg events.EventRemovedScriptModel) { + if msg.Owner == events.OwnerSystem { + return + } + filePath := s.getFilePath(msg.Script) + _ = s.Fs.RemoveAll(filePath) + s.Lock() + delete(s.fileInfos, filePath) + s.Unlock() +} + +func (s *Scripts) eventUpdateScript(msg events.EventUpdatedScriptModel) { + if msg.Owner == events.OwnerSystem { + return + } + filePath := s.getFilePath(msg.Script) + _ = afero.WriteFile(s.Fs, filePath, []byte(msg.Script.Source), 0644) + _ = s.Fs.Chtimes(filePath, msg.Script.UpdatedAt, msg.Script.UpdatedAt) + info, err := s.Fs.Stat(filePath) + if err != nil { + log.Error(err.Error()) + return + } + s.Lock() + s.fileInfos[filePath] = &FileInfo{ + Size: info.Size(), + ModTime: info.ModTime(), + LastCheck: time.Now(), + IsInitialized: true, + } + s.Unlock() + +} + +func (s *Scripts) preload() { + log.Info("Preload script list") + + var recordDir = filepath.Join(rootDir, s.rootDir) + + _ = s.Fs.MkdirAll(recordDir, 0755) + + var page int64 + var scripts []*m.Script + const perPage = 500 + var err error + +LOOP: + + if scripts, _, err = s.adaptors.Script.List(context.Background(), perPage, perPage*page, "desc", "id", nil, nil); err != nil { + log.Error(err.Error()) + return + } + + for _, script := range scripts { + filePath := s.getFilePath(script) + if err = afero.WriteFile(s.Fs, filePath, []byte(script.Source), 0644); err != nil { + log.Error(err.Error()) + } + if err = s.Fs.Chtimes(filePath, script.CreatedAt, script.UpdatedAt); err != nil { + log.Error(err.Error()) + } + } + + if len(scripts) != 0 { + page++ + goto LOOP + } + + s.Lock() + defer s.Unlock() + err = afero.Walk(s.Fs, filepath.Join(rootDir, s.rootDir), func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + + s.fileInfos[path] = &FileInfo{ + Size: info.Size(), + ModTime: info.ModTime(), + LastCheck: time.Now(), + IsInitialized: true, + } + + return nil + }) +} + +func (s *Scripts) getFilePath(script *m.Script) string { + return filepath.Join(rootDir, s.rootDir, getFileName(script)) +} + +func (s *Scripts) RemoveScript(ctx context.Context, path string) (err error) { + scriptName := extractScriptName(filepath.Base(path)) + var script *m.Script + script, err = s.adaptors.Script.GetByName(ctx, scriptName) + if err == nil { + if err = s.adaptors.Script.Delete(ctx, script.Id); err != nil { + return + } + s.eventBus.Publish(fmt.Sprintf("system/models/scripts/%d", script.Id), events.EventRemovedScriptModel{ + Common: events.Common{ + Owner: events.OwnerSystem, + }, + ScriptId: script.Id, + Script: script, + }) + } + return +} + +func (s *Scripts) CreateScript(ctx context.Context, name string, fileInfo os.FileInfo) (err error) { + scriptName := extractScriptName(fileInfo.Name()) + lang := extractScriptLang(fileInfo.Name()) + + if lang == "" { + err = errors.New("bad extension") + return + } + + var source []byte + source, err = afero.ReadFile(s.Fs, name) + if err != nil { + return + } + + script := &m.Script{ + Name: scriptName, + Lang: lang, + Source: string(source), + } + engine, err := s.scriptService.NewEngine(script) + if err != nil { + return + } + if err = engine.Compile(); err != nil { + return + } + + if _, err = s.adaptors.Script.Add(ctx, script); err != nil { + return err + } + + s.eventBus.Publish(fmt.Sprintf("system/models/scripts/%d", script.Id), events.EventCreatedScriptModel{ + Common: events.Common{ + Owner: events.OwnerSystem, + }, + ScriptId: script.Id, + Script: script, + }) + + return +} + +func (s *Scripts) UpdateScript(ctx context.Context, name string, fileInfo os.FileInfo) (err error) { + scriptName := extractScriptName(fileInfo.Name()) + lang := extractScriptLang(fileInfo.Name()) + + if lang == "" { + err = errors.New("bad extension") + return + } + + var source []byte + source, err = afero.ReadFile(s.Fs, name) + if err != nil { + return + } + + var script *m.Script + script, err = s.adaptors.Script.GetByName(ctx, scriptName) + if err == nil { + script.Source = string(source) + script.Lang = lang + + var engine *scripts.Engine + engine, err = s.scriptService.NewEngine(script) + if err != nil { + return + } + if err = engine.Compile(); err != nil { + return + } + if err = s.adaptors.Script.Update(ctx, script); err != nil { + return err + } + s.eventBus.Publish(fmt.Sprintf("system/models/scripts/%d", script.Id), events.EventUpdatedScriptModel{ + Common: events.Common{ + Owner: events.OwnerSystem, + }, + ScriptId: script.Id, + Script: script, + }) + return + } + return +} + +func (s *Scripts) syncFiles() { + s.Lock() + defer s.Unlock() + + for _, fileInfo := range s.fileInfos { + fileInfo.IsInitialized = false + } + + _ = afero.Walk(s.Fs, "/webdav/scripts", func(path string, info os.FileInfo, err error) error { + if !s.isStarted.Load() { + return errors.New("not started") + } + if info.IsDir() { + return nil + } + fileInfo, ok := s.fileInfos[path] + if ok { + fileInfo.IsInitialized = true + if info.ModTime().After(fileInfo.ModTime) || fileInfo.Size != info.Size() { + log.Infof("File %s has changed.", path) + fileInfo.Size = info.Size() + fileInfo.ModTime = info.ModTime() + fileInfo.LastCheck = time.Now() + + if _err := s.UpdateScript(context.Background(), path, info); _err != nil { + log.Error(_err.Error()) + return _err + } + } + } else { + if _err := s.CreateScript(context.Background(), path, info); _err != nil { + log.Error(_err.Error()) + return _err + } + s.fileInfos[path] = &FileInfo{ + Size: info.Size(), + ModTime: info.ModTime(), + LastCheck: time.Now(), + IsInitialized: true, + } + } + return nil + }) + + if !s.isStarted.Load() { + return + } + + for path, fileInfo := range s.fileInfos { + if !s.isStarted.Load() { + return + } + if !fileInfo.IsInitialized { + if err := s.RemoveScript(context.Background(), path); err != nil { + log.Error(err.Error()) + } + delete(s.fileInfos, path) + } + } +} diff --git a/plugins/webdav/server.go b/plugins/webdav/server.go new file mode 100644 index 000000000..d1b02abc5 --- /dev/null +++ b/plugins/webdav/server.go @@ -0,0 +1,85 @@ +// This file is part of the Smart Home +// Program complex distribution https://github.com/e154/smart-home +// Copyright (C) 2024, Filippov Alex +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library. If not, see +// . + +package webdav + +import ( + "net/http" + + "go.uber.org/atomic" + "golang.org/x/net/webdav" + + "github.com/e154/smart-home/adaptors" + "github.com/e154/smart-home/system/bus" + "github.com/e154/smart-home/system/scripts" +) + +const rootDir = "/webdav" + +type Server struct { + adaptors *adaptors.Adaptors + eventBus bus.Bus + isStarted *atomic.Bool + scripts *Scripts + *FS + handler *webdav.Handler +} + +func NewServer() *Server { + server := &Server{ + isStarted: atomic.NewBool(false), + } + + return server +} + +func (s *Server) Start(adaptors *adaptors.Adaptors, scriptService scripts.ScriptService, eventBus bus.Bus) { + if !s.isStarted.CompareAndSwap(false, true) { + return + } + + s.adaptors = adaptors + s.eventBus = eventBus + + s.FS = NewFS() + s.handler = &webdav.Handler{ + FileSystem: s, + LockSystem: webdav.NewMemLS(), + } + + _ = s.MkdirAll("/webdav/scripts", 0755) + + s.scripts = NewScripts(s.FS) + s.scripts.Start(adaptors, scriptService, eventBus) + +} + +func (s *Server) Shutdown() { + if !s.isStarted.CompareAndSwap(true, false) { + return + } + + s.scripts.Shutdown() +} + +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if !s.isStarted.Load() { + return + } + s.handler.ServeHTTP(w, r) +} diff --git a/plugins/webdav/types.go b/plugins/webdav/types.go new file mode 100644 index 000000000..9785a0c22 --- /dev/null +++ b/plugins/webdav/types.go @@ -0,0 +1,105 @@ +// This file is part of the Smart Home +// Program complex distribution https://github.com/e154/smart-home +// Copyright (C) 2016-2023, Filippov Alex +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library. If not, see +// . + +package webdav + +import ( + "fmt" + "strings" + "time" + + "github.com/e154/smart-home/common" + m "github.com/e154/smart-home/models" +) + +const ( + // Name ... + Name = "webdav" + + AttrUser = "user" + AttrPassword = "password" + AttrAnonymous = "anonymous" + + Version = "0.0.1" +) + +// NewSettings ... +func NewSettings() m.Attributes { + return m.Attributes{ + AttrUser: { + Name: AttrUser, + Type: common.AttributeString, + }, + AttrPassword: { + Name: AttrPassword, + Type: common.AttributeEncrypted, + }, + AttrAnonymous: { + Name: AttrAnonymous, + Type: common.AttributeBool, + }, + } +} + +func extractScriptName(path string) string { + res := strings.Split(path, ".") + if len(res) > 0 { + return res[0] + } + return path +} + +func extractScriptLang(path string) common.ScriptLang { + res := strings.Split(path, ".") + if len(res) > 1 { + switch strings.ToLower(res[1]) { + case "ts": + return "ts" + case "js": + return "javascript" + case "coffee": + return "coffeescript" + } + } + return "" +} + +func scriptExt(script *m.Script) (ext string) { + switch script.Lang { + case common.ScriptLangTs: + ext = "ts" + case common.ScriptLangCoffee: + ext = "coffee" + case common.ScriptLangJavascript: + ext = "js" + default: + ext = "txt" + } + return +} + +func getFileName(script *m.Script) string { + return fmt.Sprintf("%s.%s", script.Name, scriptExt(script)) +} + +type FileInfo struct { + Size int64 + ModTime time.Time + LastCheck time.Time + IsInitialized bool +} diff --git a/plugins/webpush/events.go b/plugins/webpush/events.go index 4e912a2c3..cb9cd4165 100644 --- a/plugins/webpush/events.go +++ b/plugins/webpush/events.go @@ -23,16 +23,19 @@ import m "github.com/e154/smart-home/models" // EventAddWebPushSubscription ... type EventAddWebPushSubscription struct { UserID int64 `json:"user_id"` + SessionID string `json:"session_id"` Subscription *m.Subscription `json:"subscription"` } // EventGetWebPushPublicKey ... type EventGetWebPushPublicKey struct { - UserID int64 `json:"user_id,omitempty"` + UserID int64 `json:"user_id,omitempty"` + SessionID string `json:"session_id"` } // EventNewWebPushPublicKey ... type EventNewWebPushPublicKey struct { UserID int64 `json:"user_id,omitempty"` + SessionID string `json:"session_id"` PublicKey string `json:"public_key"` } diff --git a/plugins/webpush/plugin.go b/plugins/webpush/plugin.go index a3f9ea175..8f5fc3d50 100644 --- a/plugins/webpush/plugin.go +++ b/plugins/webpush/plugin.go @@ -247,8 +247,9 @@ func (p *plugin) eventHandler(_ string, event interface{}) { } func (p *plugin) sendPublicKey(event EventGetWebPushPublicKey) { - p.Service.EventBus().Publish(TopicPluginWebpush, EventNewWebPushPublicKey{ + p.Service.EventBus().Publish("system/dashboard", EventNewWebPushPublicKey{ UserID: event.UserID, + SessionID: event.SessionID, PublicKey: p.VAPIDPublicKey, }) } diff --git a/plugins/zigbee2mqtt/actor.go b/plugins/zigbee2mqtt/actor.go index c5e740a62..ee7bc6d63 100644 --- a/plugins/zigbee2mqtt/actor.go +++ b/plugins/zigbee2mqtt/actor.go @@ -20,20 +20,20 @@ package zigbee2mqtt import ( "context" - "fmt" "sync" - "github.com/e154/smart-home/system/scripts" + "github.com/pkg/errors" "github.com/e154/smart-home/common/events" m "github.com/e154/smart-home/models" "github.com/e154/smart-home/system/mqtt" + "github.com/e154/smart-home/system/scripts" "github.com/e154/smart-home/system/supervisor" ) // Actor ... type Actor struct { - supervisor.BaseActor + *supervisor.BaseActor zigbee2mqttDevice *m.Zigbee2mqttDevice mqttMessageQueue chan *Message actionPool chan events.EventCallEntityAction @@ -66,12 +66,9 @@ func NewActor(entity *m.Entity, } } - for _, engine := range actor.ScriptEngines { - engine.Spawn(func(engine *scripts.Engine) { - engine.EvalString(fmt.Sprintf("const ENTITY_ID = \"%s\";", entity.Id)) - engine.Do() - }) - } + actor.ScriptsEngine.Spawn(func(engine *scripts.Engine) { + engine.Do() + }) // mqtt worker go func() { @@ -101,45 +98,9 @@ func (e *Actor) Spawn() { // SetState ... func (e *Actor) SetState(params supervisor.EntityStateParams) error { - e.stateMu.Lock() - defer e.stateMu.Unlock() - - oldState := e.GetEventState() - now := e.Now(oldState) - - if params.NewState != nil { - if state, ok := e.States[*params.NewState]; ok { - e.State = &state - } - } - - var changed bool - var err error - - e.AttrMu.Lock() - if changed, err = e.Attrs.Deserialize(params.AttributeValues); !changed { - if err != nil { - log.Warn(err.Error()) - } - - if oldState.LastUpdated != nil { - delta := now.Sub(*oldState.LastUpdated).Milliseconds() - //fmt.Println("delta", delta) - if delta < 200 { - e.AttrMu.Unlock() - return nil - } - } - } - e.AttrMu.Unlock() - - go e.SaveState(events.EventStateChanged{ - PluginName: e.Id.PluginName(), - EntityId: e.Id, - OldState: oldState, - NewState: e.GetEventState(), - StorageSave: params.StorageSave, - }) + e.SetActorState(params.NewState) + e.DeserializeAttr(params.AttributeValues) + e.SaveState(false, params.StorageSave) return nil } @@ -158,11 +119,9 @@ func (e *Actor) mqttNewMessage(message *Message) { e.newMsgMu.Lock() defer e.newMsgMu.Unlock() - for _, engine := range e.ScriptEngines { - if _, err := engine.Engine().AssertFunction(FuncZigbee2mqttEvent, message); err != nil { - log.Error(err.Error()) - return - } + if _, err := e.ScriptsEngine.AssertFunction(FuncZigbee2mqttEvent, message); err != nil { + log.Error(err.Error()) + return } } @@ -171,15 +130,17 @@ func (e *Actor) addAction(event events.EventCallEntityAction) { } func (e *Actor) runAction(msg events.EventCallEntityAction) { - action, ok := e.Actions[msg.ActionName] - if !ok { - log.Warnf("action %s not found", msg.ActionName) - return - } - if action.ScriptEngine.Engine() == nil { - return + if action, ok := e.Actions[msg.ActionName]; ok { + if action.ScriptEngine != nil && action.ScriptEngine.Engine() != nil { + if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } + return + } } - if _, err := action.ScriptEngine.Engine().AssertFunction(FuncEntityAction, msg.EntityId, action.Name, msg.Args); err != nil { - log.Error(err.Error()) + if e.ScriptsEngine != nil && e.ScriptsEngine.Engine() != nil { + if _, err := e.ScriptsEngine.AssertFunction(FuncEntityAction, msg.EntityId, msg.ActionName, msg.Args); err != nil { + log.Error(errors.Wrapf(err, "entity id: %s ", e.Id).Error()) + } } } diff --git a/static_source/admin/package.json b/static_source/admin/package.json index b6f9e211f..b963eaba4 100644 --- a/static_source/admin/package.json +++ b/static_source/admin/package.json @@ -25,8 +25,10 @@ "analysis": "windicss-analysis" }, "dependencies": { + "@daybrush/utils": "^1.13.0", "@element-plus/icons-vue": "^2.1.0", "@iconify/iconify": "^3.1.0", + "@moveable/helper": "^0.1.3", "@tinymce/tinymce-vue": "^5.1.0", "@vueuse/core": "^9.13.0", "@wangeditor/editor": "^5.1.23", @@ -41,6 +43,7 @@ "element-plus": "2.3.3", "intro.js": "^7.0.1", "js-cookie": "^3.0.5", + "json-editor-vue": "^0.11.2", "lodash-es": "^4.17.21", "lodash.debounce": "^4.0.8", "mitt": "^3.0.0", @@ -61,6 +64,7 @@ "uuid-generator-ts": "^1.1.0", "vue": "3.2.47", "vue-i18n": "9.2.2", + "vue-json-viewer": "3", "vue-lite-youtube-embed": "^1.2.0", "vue-router": "^4.1.6", "vue-types": "^5.0.2", @@ -78,7 +82,7 @@ "@iconify/json": "^2.2.48", "@intlify/unplugin-vue-i18n": "^0.10.0", "@purge-icons/generated": "^0.9.0", - "@types/codemirror": "^5.60.8", + "@types/codemirror": "^5.60.15", "@types/intro.js": "^5.1.1", "@types/lodash-es": "^4.17.7", "@types/node": "^18.15.11", diff --git a/static_source/admin/pnpm-lock.yaml b/static_source/admin/pnpm-lock.yaml index fe8aff881..cbedbbe6b 100644 --- a/static_source/admin/pnpm-lock.yaml +++ b/static_source/admin/pnpm-lock.yaml @@ -5,12 +5,18 @@ settings: excludeLinksFromLockfile: false dependencies: + '@daybrush/utils': + specifier: ^1.13.0 + version: 1.13.0 '@element-plus/icons-vue': specifier: ^2.1.0 version: 2.1.0(vue@3.2.47) '@iconify/iconify': specifier: ^3.1.0 version: 3.1.0 + '@moveable/helper': + specifier: ^0.1.3 + version: 0.1.3 '@tinymce/tinymce-vue': specifier: ^5.1.0 version: 5.1.0(vue@3.2.47) @@ -53,6 +59,9 @@ dependencies: js-cookie: specifier: ^3.0.5 version: 3.0.5 + json-editor-vue: + specifier: ^0.11.2 + version: 0.11.2(@lezer/common@1.2.1)(vue@3.2.47) lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -113,6 +122,9 @@ dependencies: vue-i18n: specifier: 9.2.2 version: 9.2.2(vue@3.2.47) + vue-json-viewer: + specifier: '3' + version: 3.0.4(vue@3.2.47) vue-lite-youtube-embed: specifier: ^1.2.0 version: 1.2.0(vue@3.2.47) @@ -161,8 +173,8 @@ devDependencies: specifier: ^0.9.0 version: 0.9.0 '@types/codemirror': - specifier: ^5.60.8 - version: 5.60.8 + specifier: ^5.60.15 + version: 5.60.15 '@types/intro.js': specifier: ^5.1.1 version: 5.1.1 @@ -327,7 +339,6 @@ packages: dependencies: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.18 - dev: true /@antfu/utils@0.7.4: resolution: {integrity: sha512-qe8Nmh9rYI/HIspLSTwtbMFPj6dISG6+dJnOguTlPNXtCvS2uezdxscVBb7/3DrmNbQK49TDqpkSQ1chbRGdpQ==} @@ -1575,6 +1586,75 @@ packages: '@egjs/component': 3.0.4 dev: false + /@codemirror/autocomplete@6.12.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0)(@lezer/common@1.2.1): + resolution: {integrity: sha512-r4IjdYFthwbCQyvqnSlx0WBHRHi8nBvU+WjJxFUij81qsBfhNudf/XKKmmC2j3m0LaOYUQTf3qiEK1J8lO1sdg==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + '@lezer/common': ^1.0.0 + dependencies: + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@lezer/common': 1.2.1 + dev: false + + /@codemirror/commands@6.3.3: + resolution: {integrity: sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==} + dependencies: + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@lezer/common': 1.2.1 + dev: false + + /@codemirror/lang-json@6.0.1: + resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==} + dependencies: + '@codemirror/language': 6.10.0 + '@lezer/json': 1.0.2 + dev: false + + /@codemirror/language@6.10.0: + resolution: {integrity: sha512-2vaNn9aPGCRFKWcHPFksctzJ8yS5p7YoaT+jHpc0UGKzNuAIx4qy6R5wiqbP+heEEdyaABA582mNqSHzSoYdmg==} + dependencies: + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + style-mod: 4.1.0 + dev: false + + /@codemirror/lint@6.4.2: + resolution: {integrity: sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==} + dependencies: + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + crelt: 1.0.6 + dev: false + + /@codemirror/search@6.5.5: + resolution: {integrity: sha512-PIEN3Ke1buPod2EHbJsoQwlbpkz30qGZKcnmH1eihq9+bPQx8gelauUwLYaY4vBOuBAuEhmpDLii4rj/uO0yMA==} + dependencies: + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + crelt: 1.0.6 + dev: false + + /@codemirror/state@6.4.0: + resolution: {integrity: sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A==} + dev: false + + /@codemirror/view@6.23.0: + resolution: {integrity: sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ==} + dependencies: + '@codemirror/state': 6.4.0 + style-mod: 4.1.0 + w3c-keyname: 2.2.8 + dev: false + /@commitlint/cli@17.5.1: resolution: {integrity: sha512-pRRgGSzdHQHehxZbGA3qF6wVPyl+EEQgTe/t321rtMLFbuJ7nRj2waS17s/v5oEbyZtiY5S8PGB6XtEIm0I+Sg==} engines: {node: '>=v14'} @@ -2064,6 +2144,28 @@ packages: '@floating-ui/core': 1.3.1 dev: false + /@fortawesome/fontawesome-common-types@6.5.1: + resolution: {integrity: sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==} + engines: {node: '>=6'} + requiresBuild: true + dev: false + + /@fortawesome/free-regular-svg-icons@6.5.1: + resolution: {integrity: sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + '@fortawesome/fontawesome-common-types': 6.5.1 + dev: false + + /@fortawesome/free-solid-svg-icons@6.5.1: + resolution: {integrity: sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + '@fortawesome/fontawesome-common-types': 6.5.1 + dev: false + /@humanwhocodes/config-array@0.11.10: resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} engines: {node: '>=10.10.0'} @@ -2229,12 +2331,10 @@ packages: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.18 - dev: true /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} - dev: true /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} @@ -2244,7 +2344,6 @@ packages: /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} - dev: true /@jridgewell/source-map@0.3.3: resolution: {integrity: sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==} @@ -2255,18 +2354,15 @@ packages: /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - dev: true /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true /@jridgewell/trace-mapping@0.3.18: resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 - dev: true /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -2275,6 +2371,36 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@lezer/common@1.2.1: + resolution: {integrity: sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==} + dev: false + + /@lezer/highlight@1.2.0: + resolution: {integrity: sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==} + dependencies: + '@lezer/common': 1.2.1 + dev: false + + /@lezer/json@1.0.2: + resolution: {integrity: sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==} + dependencies: + '@lezer/common': 1.2.1 + '@lezer/highlight': 1.2.0 + '@lezer/lr': 1.3.14 + dev: false + + /@lezer/lr@1.3.14: + resolution: {integrity: sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==} + dependencies: + '@lezer/common': 1.2.1 + dev: false + + /@moveable/helper@0.1.3: + resolution: {integrity: sha512-hSSyPkU7/cZuJys79rG0/xMs2Py09a8RUvBtpi/AftLYeSS8JAmMNwiw9lL19nddsVNB9cb2UON+U6uyFNS8Zw==} + dependencies: + '@daybrush/utils': 1.13.0 + dev: false + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2330,6 +2456,18 @@ packages: '@iconify/iconify': 3.1.0 dev: true + /@replit/codemirror-indentation-markers@6.5.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0): + resolution: {integrity: sha512-5RgeuQ6erfROi1EVI2X7G4UR+KByjb07jhYMynvpvlrV22JlnARifmKMGEUKy0pKcxBNfwbFqoUlTYHPgyZNlg==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + dependencies: + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + dev: false + /@rollup/plugin-node-resolve@13.3.0(rollup@3.20.2): resolution: {integrity: sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==} engines: {node: '>= 10.0.0'} @@ -2399,6 +2537,10 @@ packages: '@daybrush/utils': 1.13.0 dev: false + /@sphinxxxx/color-conversion@2.2.2: + resolution: {integrity: sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==} + dev: false + /@sxzz/popperjs-es@2.11.7: resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} dev: false @@ -2437,10 +2579,10 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/codemirror@5.60.8: - resolution: {integrity: sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==} + /@types/codemirror@5.60.15: + resolution: {integrity: sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==} dependencies: - '@types/tern': 0.23.4 + '@types/tern': 0.23.9 dev: true /@types/eslint@8.40.2: @@ -2456,7 +2598,6 @@ packages: /@types/estree@1.0.1: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} - dev: true /@types/event-emitter@0.3.3: resolution: {integrity: sha512-UfnOK1pIxO7P+EgPRZXD9jMpimd8QEFcEZ5R67R1UhGbv4zghU5+NE7U8M8G9H5Jc8FI51rqDWQs6FtUfq2e/Q==} @@ -2552,8 +2693,8 @@ packages: '@types/node': 18.15.11 dev: true - /@types/tern@0.23.4: - resolution: {integrity: sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg==} + /@types/tern@0.23.9: + resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} dependencies: '@types/estree': 1.0.1 dev: true @@ -3301,11 +3442,16 @@ packages: hasBin: true dev: true + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + /acorn@8.9.0: resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==} engines: {node: '>=0.4.0'} hasBin: true - dev: true /aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} @@ -3331,7 +3477,6 @@ packages: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - dev: true /animate.css@4.1.1: resolution: {integrity: sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==} @@ -3387,7 +3532,6 @@ packages: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -3397,6 +3541,12 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true + /aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + dependencies: + dequal: 2.0.3 + dev: false + /arr-diff@4.0.0: resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} engines: {node: '>=0.10.0'} @@ -3510,6 +3660,12 @@ packages: - debug dev: false + /axobject-query@4.0.0: + resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} + dependencies: + dequal: 2.0.3 + dev: false + /babel-plugin-polyfill-corejs2@0.4.3(@babel/core@7.22.5): resolution: {integrity: sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw==} peerDependencies: @@ -3585,7 +3741,6 @@ packages: /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} - dev: true /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -3647,7 +3802,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: true /browserslist@4.21.9: resolution: {integrity: sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==} @@ -3865,7 +4019,6 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true /class-utils@0.3.6: resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} @@ -3957,6 +4110,16 @@ packages: engines: {node: '>=0.8'} dev: true + /code-red@1.0.4: + resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + '@types/estree': 1.0.1 + acorn: 8.11.3 + estree-walker: 3.0.3 + periscopic: 3.1.0 + dev: false + /codemirror-editor-vue3@2.3.0(codemirror@5.65.13)(diff-match-patch@1.0.5)(vue@3.2.47): resolution: {integrity: sha512-e6y/2wBL4Xb4SD10e/jq/lcoIeiWPMw2XnF6KePLlbmOphZ830XGU3kSz52qkoa9RUjaY6zHuSlwd09o13O/oQ==} peerDependencies: @@ -3969,6 +4132,18 @@ packages: vue: 3.2.47 dev: false + /codemirror-wrapped-line-indent@1.0.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0): + resolution: {integrity: sha512-8ny0CSJ1T6mYQuBF7yGzgRRv+zOXUBaBscvJH3jczK7Isi19RA2Ans9ip0gZBkUWCOhFmmw1IC1a+uN4zj0GNg==} + peerDependencies: + '@codemirror/language': ^6.9.0 + '@codemirror/state': ^6.2.1 + '@codemirror/view': ^6.17.1 + dependencies: + '@codemirror/language': 6.10.0 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + dev: false + /codemirror@5.65.13: resolution: {integrity: sha512-SVWEzKXmbHmTQQWaz03Shrh4nybG0wXx2MEu3FO4ezbPW8IbnZEd5iGHGEffSUaitKYa3i+pHpBsSvw8sPHtzg==} dev: false @@ -4179,6 +4354,10 @@ packages: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true + /crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + dev: false + /croact-css-styled@1.1.9: resolution: {integrity: sha512-G7yvRiVJ3Eoj0ov2h2xR4312hpOzATay2dGS9clK8yJQothjH1sBXIyvOeRP5wBKD9mPcKcoUXPCPsl0tQog4w==} dependencies: @@ -4293,7 +4472,6 @@ packages: dependencies: mdn-data: 2.0.30 source-map-js: 1.0.2 - dev: true /css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} @@ -4441,6 +4619,11 @@ packages: resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==} dev: false + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: false + /detect-file@1.0.0: resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} engines: {node: '>=0.10.0'} @@ -4450,6 +4633,11 @@ packages: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} dev: false + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: false + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -4968,6 +5156,12 @@ packages: /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.1 + dev: false + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -5089,7 +5283,6 @@ packages: /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true /fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} @@ -5167,7 +5360,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /finalhandler@1.1.2: resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} @@ -5319,7 +5511,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true /function-bind@1.1.1: @@ -5390,7 +5581,6 @@ packages: engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: true /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} @@ -5730,6 +5920,14 @@ packages: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} dev: false + /immutable-json-patch@5.1.3: + resolution: {integrity: sha512-95AsF9hJTPpwtBGAnHmw57PASL672tb+vGHR5xLhH2VPuHSsLho7grjlfgQ65DIhHP+UmLCjdmuuA6L1ndJbZg==} + dev: false + + /immutable@4.3.4: + resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} + dev: false + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -5829,7 +6027,6 @@ packages: engines: {node: '>=8'} dependencies: binary-extensions: 2.2.0 - dev: true /is-buffer@1.1.6: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} @@ -5895,7 +6092,6 @@ packages: /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -5911,7 +6107,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-hotkey@0.2.0: resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==} @@ -5941,7 +6136,6 @@ packages: /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-obj@2.0.0: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} @@ -5974,6 +6168,12 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + /is-reference@3.0.2: + resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + dependencies: + '@types/estree': 1.0.1 + dev: false + /is-relative@1.0.0: resolution: {integrity: sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==} engines: {node: '>=0.10.0'} @@ -6078,6 +6278,11 @@ packages: hasBin: true dev: true + /jmespath@0.16.0: + resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} + engines: {node: '>= 0.6.0'} + dev: false + /js-base64@2.6.4: resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} dev: true @@ -6117,6 +6322,24 @@ packages: hasBin: true dev: true + /json-editor-vue@0.11.2(@lezer/common@1.2.1)(vue@3.2.47): + resolution: {integrity: sha512-KH11D2U4SNJ0hOoIwHGFLwnY/Xfl4zTfyt4rMbJN5AJUDLwBOnGx5rL+2/Xb1cV9CX7R8ADm9ysN69AGGVRObQ==} + engines: {npm: '>=8.3.0'} + requiresBuild: true + peerDependencies: + '@vue/composition-api': '>=1' + vue: 2||3 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vanilla-jsoneditor: 0.20.0(@lezer/common@1.2.1) + vue: 3.2.47 + vue-demi: 0.14.6(vue@3.2.47) + transitivePeerDependencies: + - '@lezer/common' + dev: false + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -6127,7 +6350,10 @@ packages: /json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - dev: true + + /json-source-map@0.6.1: + resolution: {integrity: sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==} + dev: false /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -6170,6 +6396,11 @@ packages: engines: {'0': node >= 0.2.0} dev: true + /jsonrepair@3.5.1: + resolution: {integrity: sha512-F0VxiEj1j7m1OAVUVy6fFYk5s8tthF61J7tjYtEACw1DeNQqKmZF6dPddduxc7Tc5IrLqKTdLAwUNTmrqqg+hw==} + hasBin: true + dev: false + /jspdf@2.5.1: resolution: {integrity: sha512-hXObxz7ZqoyhxET78+XR34Xu2qFGrJJ2I2bE5w4SM8eFaFEkW2xcGRVUss360fYelwRSid/jT078kbNvmoW0QA==} dependencies: @@ -6346,6 +6577,10 @@ packages: engines: {node: '>=14'} dev: true + /locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + dev: false + /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -6524,6 +6759,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + /make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -6577,7 +6819,6 @@ packages: /mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} - dev: true /memoize-one@6.0.0: resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} @@ -6841,7 +7082,6 @@ packages: /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: true /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -6930,7 +7170,6 @@ packages: /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - dev: true /normalize-range@0.1.2: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} @@ -7332,13 +7571,20 @@ packages: dev: false optional: true + /periscopic@3.1.0: + resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + dependencies: + '@types/estree': 1.0.1 + estree-walker: 3.0.3 + is-reference: 3.0.2 + dev: false + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true /pidtree@0.6.0: resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} @@ -7578,7 +7824,6 @@ packages: /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} - dev: true /q@1.5.1: resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} @@ -7719,7 +7964,6 @@ packages: engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 - dev: true /rechoir@0.8.0: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} @@ -7800,7 +8044,6 @@ packages: /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - dev: true /require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} @@ -7961,6 +8204,16 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true + /sass@1.70.0: + resolution: {integrity: sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + immutable: 4.3.4 + source-map-js: 1.0.2 + dev: false + /sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} requiresBuild: true @@ -8365,6 +8618,10 @@ packages: engines: {node: '>=8'} dev: true + /style-mod@4.1.0: + resolution: {integrity: sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==} + dev: false + /style-search@0.1.0: resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} dev: true @@ -8507,6 +8764,26 @@ packages: engines: {node: '>= 0.4'} dev: true + /svelte@4.2.9: + resolution: {integrity: sha512-hsoB/WZGEPFXeRRLPhPrbRz67PhP6sqYgvwcAs+gWdSQSvNDw+/lTeUJSWe5h2xC97Fz/8QxAOqItwBzNJPU8w==} + engines: {node: '>=16'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.18 + '@types/estree': 1.0.1 + acorn: 8.9.0 + aria-query: 5.3.0 + axobject-query: 4.0.0 + code-red: 1.0.4 + css-tree: 2.3.1 + estree-walker: 3.0.3 + is-reference: 3.0.2 + locate-character: 3.0.0 + magic-string: 0.30.5 + periscopic: 3.1.0 + dev: false + /svg-baker@1.7.0: resolution: {integrity: sha512-nibslMbkXOIkqKVrfcncwha45f97fGuAOn1G99YwnwTj8kF9YiM6XexPcUso97NxOm6GsP0SIvYVIosBis1xLg==} dependencies: @@ -8658,7 +8935,6 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /to-regex@3.0.2: resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} @@ -8919,7 +9195,6 @@ packages: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.0 - dev: true /urix@0.1.0: resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} @@ -8979,6 +9254,43 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /vanilla-jsoneditor@0.20.0(@lezer/common@1.2.1): + resolution: {integrity: sha512-2Q1//eCTauqy0kqvz2dLurzov0BA7FVHCl+6MBUBqem1CWmUONFuMvr8F/mmeqWY8D5WqcrRtb60akDWDFakgQ==} + dependencies: + '@codemirror/autocomplete': 6.12.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0)(@lezer/common@1.2.1) + '@codemirror/commands': 6.3.3 + '@codemirror/lang-json': 6.0.1 + '@codemirror/language': 6.10.0 + '@codemirror/lint': 6.4.2 + '@codemirror/search': 6.5.5 + '@codemirror/state': 6.4.0 + '@codemirror/view': 6.23.0 + '@fortawesome/free-regular-svg-icons': 6.5.1 + '@fortawesome/free-solid-svg-icons': 6.5.1 + '@replit/codemirror-indentation-markers': 6.5.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0) + ajv: 8.12.0 + codemirror-wrapped-line-indent: 1.0.0(@codemirror/language@6.10.0)(@codemirror/state@6.4.0)(@codemirror/view@6.23.0) + diff-sequences: 29.6.3 + immutable-json-patch: 5.1.3 + jmespath: 0.16.0 + json-source-map: 0.6.1 + jsonrepair: 3.5.1 + lodash-es: 4.17.21 + memoize-one: 6.0.0 + natural-compare-lite: 1.4.0 + sass: 1.70.0 + svelte: 4.2.9 + vanilla-picker: 2.12.2 + transitivePeerDependencies: + - '@lezer/common' + dev: false + + /vanilla-picker@2.12.2: + resolution: {integrity: sha512-dk0gNeNL9fQFGd1VEhNDQfFlbCqAiksRh1H2tVPlavkH88n/a/y30rXi9PPKrYPTK5kEfPO4xcldt4ts/1wIAg==} + dependencies: + '@sphinxxxx/color-conversion': 2.2.2 + dev: false + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -9314,6 +9626,10 @@ packages: '@vue/server-renderer': 3.2.47(vue@3.2.47) '@vue/shared': 3.2.47 + /w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + dev: false + /wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: diff --git a/static_source/admin/src/api/api.ts b/static_source/admin/src/api/api.ts index 7d27c8aa9..309da446d 100644 --- a/static_source/admin/src/api/api.ts +++ b/static_source/admin/src/api/api.ts @@ -55,6 +55,15 @@ api.instance.interceptors.response.use( return } + if (response.status == 403 ) { + ElMessage({ + message: 'access forbidden: ' + res.error.message, + type: 'error', + duration: 5 * 1000 + }); + return + } + ElMessage({ message: res.error.message || t('Error'), type: 'error', diff --git a/static_source/admin/src/api/stub.ts b/static_source/admin/src/api/stub.ts index 0599fb492..adc289c46 100644 --- a/static_source/admin/src/api/stub.ts +++ b/static_source/admin/src/api/stub.ts @@ -1812,6 +1812,8 @@ export class Api extends HttpClient @@ -2442,6 +2444,8 @@ export class Api extends HttpClient @@ -3818,7 +3822,7 @@ export class Api extends HttpClient @@ -5167,6 +5171,8 @@ export class Api extends HttpClient extends HttpClient diff --git a/static_source/admin/src/components/JsonEditor/JsonEditor.vue b/static_source/admin/src/components/JsonEditor/JsonEditor.vue new file mode 100644 index 000000000..4823888cf --- /dev/null +++ b/static_source/admin/src/components/JsonEditor/JsonEditor.vue @@ -0,0 +1,201 @@ + + + + + diff --git a/static_source/admin/src/components/JsonViewer/JsonViewer.vue b/static_source/admin/src/components/JsonViewer/JsonViewer.vue index b468102d2..cab16d04a 100644 --- a/static_source/admin/src/components/JsonViewer/JsonViewer.vue +++ b/static_source/admin/src/components/JsonViewer/JsonViewer.vue @@ -1,24 +1,6 @@ - diff --git a/static_source/admin/src/components/Setting/src/Setting.vue b/static_source/admin/src/components/Setting/src/Setting.vue index ea7583dff..9392a4599 100644 --- a/static_source/admin/src/components/Setting/src/Setting.vue +++ b/static_source/admin/src/components/Setting/src/Setting.vue @@ -1,6 +1,6 @@ diff --git a/static_source/admin/src/views/Backups/index.vue b/static_source/admin/src/views/Backups/index.vue index ae6886b46..81bc7a9bf 100644 --- a/static_source/admin/src/views/Backups/index.vue +++ b/static_source/admin/src/views/Backups/index.vue @@ -100,51 +100,53 @@ const getList = async () => { } const addNew = async () => { - api.v1.backupServiceNewBackup({}) + const res = await api.v1.backupServiceNewBackup({}) .catch(() => { }) .finally(() => { }) - ElMessage({ - title: t('Success'), - message: t('message.createdSuccessfully'), - type: 'success', - duration: 2000 - }); + if (res.status == 200) { + ElMessage({ + title: t('Success'), + message: t('message.createdSuccessfully'), + type: 'success', + duration: 2000 + }); + } } const restore = async (backup: ApiBackup) => { - api.v1.backupServiceRestoreBackup(backup.name) + const res = await api.v1.backupServiceRestoreBackup(backup.name) .catch(() => { }) .finally(() => { }) - setTimeout(async () => { + if (res.status == 200) { ElMessage({ title: t('Success'), message: t('message.callSuccessful'), type: 'success', duration: 2000 }); - }, 2000) + } } const remove = async (backup: ApiBackup) => { - api.v1.backupServiceDeleteBackup(backup.name) + const res = await api.v1.backupServiceDeleteBackup(backup.name) .catch(() => { }) .finally(() => { }) - setTimeout(async () => { + if (res.status == 200) { ElMessage({ title: t('Success'), message: t('message.callSuccessful'), type: 'success', duration: 2000 }); - }, 2000) + } } const getUploadURL = () => { @@ -178,6 +180,16 @@ const onSuccess: UploadProps['onSuccess'] = (file: ApiBackup, uploadFile) => { }) } +const onError: UploadProps['onError'] = (error, uploadFile, uploadFiles) => { + const body = JSON.parse(error.message) + const {message, code} = body.error; + ElMessage({ + message: message, + type: 'error', + duration: 0 + }) +} + const forceFileDownload = (file: ApiBackup) => { const link = document.createElement('a') link.href = getDownloadURL(file) @@ -244,6 +256,7 @@ getList() :action="getUploadURL()" :multiple="true" :on-success="onSuccess" + :on-error="onError" :auto-upload="true" > diff --git a/static_source/admin/src/views/Dashboard/bus.ts b/static_source/admin/src/views/Dashboard/bus.ts index b0c6e8c16..3b8c12ba4 100644 --- a/static_source/admin/src/views/Dashboard/bus.ts +++ b/static_source/admin/src/views/Dashboard/bus.ts @@ -18,6 +18,9 @@ export const useBus = (option?: Option) => { } return { - bus + on: bus.on, + off: bus.off, + emit: bus.emit, + all: bus.all } } diff --git a/static_source/admin/src/views/Dashboard/card_items/chart/index.vue b/static_source/admin/src/views/Dashboard/card_items/chart/index.vue index 23cc16d5b..4e73756b2 100644 --- a/static_source/admin/src/views/Dashboard/card_items/chart/index.vue +++ b/static_source/admin/src/views/Dashboard/card_items/chart/index.vue @@ -13,7 +13,7 @@ import {Cache, GetTokens, RenderText} from "@/views/Dashboard/render"; import {UUID} from "uuid-generator-ts"; import stream from "@/api/stream"; -const {bus} = useBus() +const {emit} = useBus() // --------------------------------- // common @@ -522,7 +522,7 @@ const prepareData = debounce( async () => { case 'doughnut': loaded.value = true; fistTime.value = false - bus.emit('updateChart', props.item.payload.chart.type) + emit('updateChart', props.item.payload.chart.type) return } @@ -545,7 +545,7 @@ const prepareData = debounce( async () => { console.warn(`unknown chart type ${props.item.entity.metrics[props.item.payload.chart?.metric_index || 0].type}`); } - bus.emit('updateChart', props.item.payload.chart.type) + emit('updateChart', props.item.payload.chart.type) loaded.value = true; fistTime.value = false diff --git a/static_source/admin/src/views/Dashboard/card_items/chart_custom/editor.vue b/static_source/admin/src/views/Dashboard/card_items/chart_custom/editor.vue index 1ea55d08b..764b462d1 100644 --- a/static_source/admin/src/views/Dashboard/card_items/chart_custom/editor.vue +++ b/static_source/admin/src/views/Dashboard/card_items/chart_custom/editor.vue @@ -33,8 +33,8 @@ import {useI18n} from "@/hooks/web/useI18n"; import {Infotip} from "@/components/Infotip"; import {debounce} from "lodash-es"; import {EChartsOption} from "echarts"; -import ImageSearch from "@/views/Images/components/ImageSearch.vue"; import {ApiImage} from "@/api/stub"; +import JsonEditor from "@/components/JsonEditor/JsonEditor.vue"; const {t} = useI18n() @@ -125,14 +125,22 @@ const removeAttrItem = (prop: SeriesItem, index: number) => { // attributes item -const editorHandler = debounce((val: string) => { +const editorHandler = debounce((val: any) => { if (!val) { - val = defaultData; + val = { + text: defaultData, + } } try { - var options: EChartsOption = parsedObject(val) as EChartsOption; + let options: EChartsOption; + + if (val.json) { + options = val.json as EChartsOption + } else if(val.text) { + options = parsedObject(val.text) as EChartsOption + } if (!options.grid) { options['grid'] = { @@ -225,7 +233,7 @@ const onSelectImage = (image: ApiImage) => { - + diff --git a/static_source/admin/src/views/Dashboard/card_items/common/editor.vue b/static_source/admin/src/views/Dashboard/card_items/common/editor.vue index 2acff3b1f..7f13ad243 100644 --- a/static_source/admin/src/views/Dashboard/card_items/common/editor.vue +++ b/static_source/admin/src/views/Dashboard/card_items/common/editor.vue @@ -148,7 +148,7 @@ const removeAction = (index: number) => { -
+
{{$t('dashboard.editor.buttonOptions') }} @@ -157,7 +157,7 @@ const removeAction = (index: number) => { - +
diff --git a/static_source/admin/src/views/Dashboard/card_items/common/show-on.vue b/static_source/admin/src/views/Dashboard/card_items/common/show-on.vue index 9332c6a6c..ba144c879 100644 --- a/static_source/admin/src/views/Dashboard/card_items/common/show-on.vue +++ b/static_source/admin/src/views/Dashboard/card_items/common/show-on.vue @@ -81,7 +81,7 @@ const removeShowOnProp = (index: number) => {
- + { if (props.item?.payload.entityStorage?.entityIds?.length ) { if (selectedEntities.value.length == 0) { - params.entityId = props.item?.payload.entityStorage?.entityIds.join(",") + params.entityId = props.item?.payload.entityStorage?.entityIds || [] } else { - params.entityId = selectedEntities.value.join(",") + params.entityId = selectedEntities.value } } @@ -211,7 +211,7 @@ const getList = debounce( async () => { } else { tableObject.tableList = []; } -}, 100) +}, 1000) const onStateChanged = (event: EventStateChange) => { diff --git a/static_source/admin/src/views/Dashboard/card_items/index.ts b/static_source/admin/src/views/Dashboard/card_items/index.ts index f2e31b286..ffd763753 100644 --- a/static_source/admin/src/views/Dashboard/card_items/index.ts +++ b/static_source/admin/src/views/Dashboard/card_items/index.ts @@ -30,6 +30,8 @@ import joystick from './joystick/index.vue'; import joystickEditor from './joystick/editor.vue'; import icon from './icon/index.vue'; import iconEditor from './icon/editor.vue'; +import tiles from './tiles/index.vue'; +import tilesEditor from './tiles/editor.vue'; export const CardItemName = (name: string): any => { switch (name) { @@ -63,6 +65,8 @@ export const CardItemName = (name: string): any => { return joystick; case 'icon': return icon; + case 'tiles': + return tiles; default: // console.error(`unknown card name "${name}"`); return dummy; @@ -101,6 +105,8 @@ export const CardEditorName = (name: string): any => { return joystickEditor; case 'icon': return iconEditor; + case 'tiles': + return tilesEditor; default: // console.error(`unknown card name "${name}"`); return dummyEditor; @@ -127,5 +133,6 @@ export const CardItemList: ItemsType[] = [ {label: 'COLOR_PICKER', value: 'colorPicker'}, {label: 'STREAM_PLAYER', value: 'streamPlayer'}, {label: 'JOYSTICK', value: 'joystick'}, - {label: 'ICON', value: 'icon'} + {label: 'ICON', value: 'icon'}, + {label: 'TILES', value: 'tiles'} ]; diff --git a/static_source/admin/src/views/Dashboard/card_items/joystick/types.ts b/static_source/admin/src/views/Dashboard/card_items/joystick/types.ts index a96c64f7c..a824c0655 100644 --- a/static_source/admin/src/views/Dashboard/card_items/joystick/types.ts +++ b/static_source/admin/src/views/Dashboard/card_items/joystick/types.ts @@ -117,12 +117,12 @@ export class JoystickController { this.active = false; } - _stick._value.addEventListener('mousedown', handleDown); - _stick._value.addEventListener('touchstart', handleDown); + _stick._value.addEventListener('mousedown', handleDown, {passive: false}); + _stick._value.addEventListener('touchstart', handleDown, {passive: false}); document.addEventListener('mousemove', handleMove, {passive: false}); document.addEventListener('touchmove', handleMove, {passive: false}); - document.addEventListener('mouseup', handleUp); - document.addEventListener('touchend', handleUp); + document.addEventListener('mouseup', handleUp, {passive: false}); + document.addEventListener('touchend', handleUp, {passive: false}); } } diff --git a/static_source/admin/src/views/Dashboard/card_items/state/editor.vue b/static_source/admin/src/views/Dashboard/card_items/state/editor.vue index a3f361d57..4b9691174 100644 --- a/static_source/admin/src/views/Dashboard/card_items/state/editor.vue +++ b/static_source/admin/src/views/Dashboard/card_items/state/editor.vue @@ -101,13 +101,13 @@ const onSelectIconForState = (index: number, icon: string) => { const reloadKeyDefaultImage = 0; const onSelectDefaultImage = (image: ApiImage) => { - console.log('select image', image); + // console.log('select image', image); if (!props.item.payload.state) { initDefaultValue(); } - current_item.value.payload.state.defaultImage = image as ApiImage || undefined; + currentItem.value.payload.state.defaultImage = image as ApiImage || undefined; // this.reloadKeyDefaultImage += 1 props.item.update(); } diff --git a/static_source/admin/src/views/Dashboard/card_items/text/editor.vue b/static_source/admin/src/views/Dashboard/card_items/text/editor.vue index ad63417e9..bf1da288d 100644 --- a/static_source/admin/src/views/Dashboard/card_items/text/editor.vue +++ b/static_source/admin/src/views/Dashboard/card_items/text/editor.vue @@ -117,6 +117,7 @@ const updateCurrentState = () => { + {{ $t('dashboard.editor.textOptions') }} @@ -285,6 +286,7 @@ const updateCurrentState = () => {
+ diff --git a/static_source/admin/src/views/Dashboard/card_items/tiles/editor.vue b/static_source/admin/src/views/Dashboard/card_items/tiles/editor.vue new file mode 100644 index 000000000..435946b45 --- /dev/null +++ b/static_source/admin/src/views/Dashboard/card_items/tiles/editor.vue @@ -0,0 +1,424 @@ + + + + + diff --git a/static_source/admin/src/views/Dashboard/card_items/tiles/index.vue b/static_source/admin/src/views/Dashboard/card_items/tiles/index.vue new file mode 100644 index 000000000..9a1fb02c3 --- /dev/null +++ b/static_source/admin/src/views/Dashboard/card_items/tiles/index.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/static_source/admin/src/views/Dashboard/card_items/tiles/tilePreview.vue b/static_source/admin/src/views/Dashboard/card_items/tiles/tilePreview.vue new file mode 100644 index 000000000..36010f0da --- /dev/null +++ b/static_source/admin/src/views/Dashboard/card_items/tiles/tilePreview.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/static_source/admin/src/views/Dashboard/card_items/tiles/tileView.vue b/static_source/admin/src/views/Dashboard/card_items/tiles/tileView.vue new file mode 100644 index 000000000..78dc13ab7 --- /dev/null +++ b/static_source/admin/src/views/Dashboard/card_items/tiles/tileView.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/static_source/admin/src/views/Dashboard/card_items/tiles/types.ts b/static_source/admin/src/views/Dashboard/card_items/tiles/types.ts new file mode 100644 index 000000000..7def63965 --- /dev/null +++ b/static_source/admin/src/views/Dashboard/card_items/tiles/types.ts @@ -0,0 +1,27 @@ +import {ApiEntity, ApiImage} from '@/api/stub' + +export interface TileProp { + image?: ApiImage + key: string + position?: boolean + top?: number + left?: number + height?: number + width?: number +} + +export interface ItemPayloadTiles { + items: TileProp[] + image?: ApiImage + columns: number + rows: number + tileHeight: number + tileWidth: number + attribute: string + entity?: ApiEntity + entityId?: string + actionName?: string + position?: boolean + top?: number + left?: number +} diff --git a/static_source/admin/src/views/Dashboard/card_items/video/VideoMse.vue b/static_source/admin/src/views/Dashboard/card_items/video/VideoMse.vue index 6ee9112d3..1f79e0221 100644 --- a/static_source/admin/src/views/Dashboard/card_items/video/VideoMse.vue +++ b/static_source/admin/src/views/Dashboard/card_items/video/VideoMse.vue @@ -102,7 +102,7 @@ const startPlay = () => { startPlay(); }, 1000); }; - }, false) + }, {passive: false}) } const stopPlay = () => { diff --git a/static_source/admin/src/views/Dashboard/components/KeystrokeCapture.vue b/static_source/admin/src/views/Dashboard/components/KeystrokeCapture.vue new file mode 100644 index 000000000..713957f5b --- /dev/null +++ b/static_source/admin/src/views/Dashboard/components/KeystrokeCapture.vue @@ -0,0 +1,263 @@ + + + + + diff --git a/static_source/admin/src/views/Dashboard/components/KeystrokeCaptureViewer.vue b/static_source/admin/src/views/Dashboard/components/KeystrokeCaptureViewer.vue new file mode 100644 index 000000000..c0a10f47b --- /dev/null +++ b/static_source/admin/src/views/Dashboard/components/KeystrokeCaptureViewer.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/static_source/admin/src/views/Dashboard/core.ts b/static_source/admin/src/views/Dashboard/core.ts index 6a3e99cc5..2c4de1c72 100644 --- a/static_source/admin/src/views/Dashboard/core.ts +++ b/static_source/admin/src/views/Dashboard/core.ts @@ -33,8 +33,9 @@ import {ItemPayloadJoystick} from "@/views/Dashboard/card_items/joystick/types"; import {ItemPayloadVideo} from "@/views/Dashboard/card_items/video/types"; import {ItemPayloadEntityStorage} from "@/views/Dashboard/card_items/entity_storage/types"; import {prepareUrl} from "@/utils/serverId"; +import {ItemPayloadTiles} from "@/views/Dashboard/card_items/tiles/types"; -const {bus} = useBus() +const {emit} = useBus() export interface ButtonAction { entityId: string; @@ -82,6 +83,13 @@ export interface CompareProp { entityId?: string; } +export interface KeysProp { + keys?: Map; + entity?: { id?: string }; + entityId?: string; + action?: string; +} + export interface ItemPayloadImage { attrField?: string; image?: ApiImage; @@ -111,6 +119,7 @@ export interface ItemPayload { joystick?: ItemPayloadJoystick; video?: ItemPayloadVideo; entityStorage?: ItemPayloadEntityStorage; + tiles?: ItemPayloadTiles; } export interface ItemParams { @@ -122,6 +131,7 @@ export interface ItemParams { transform: string; showOn: CompareProp[]; hideOn: CompareProp[]; + keysCapture?: KeysProp[]; asButton: boolean; buttonActions: ButtonAction[]; } @@ -235,7 +245,7 @@ export class CardItem { if (!this.payload.text) { this.payload.text = { items: [], - default_text: 'default text', + default_text: '
default text
', current_text: '' } as ItemPayloadText; } @@ -298,6 +308,17 @@ export class CardItem { if (!this.payload.entityStorage) { this.payload.entityStorage = {} as ItemPayloadEntityStorage; } + if (!this.payload.tiles) { + this.payload.tiles = { + items: [], + defaultImage: undefined, + columns: 5, + rows: 5, + tileHeight: 25, + tileWidth: 25, + attribute: '', + } as ItemPayloadTiles; + } } } @@ -363,7 +384,7 @@ export class CardItem { payload: { text: { items: [], - default_text: 'default text', + default_text: '
default text
', current_text: '' } as ItemPayloadText }, @@ -638,6 +659,7 @@ export class Card { hidden: boolean showOn: CompareProp[] = []; hideOn: CompareProp[] = []; + keysCapture: KeysProp[] = []; selectedItem = -1; @@ -675,6 +697,9 @@ export class Card { if (payload.hideOn) { this.hideOn = payload.hideOn; } + if (payload.keysCapture) { + this.keysCapture = payload.keysCapture; + } } for (const index in card.items) { @@ -759,9 +784,10 @@ export class Card { items.push(this.items[index].serialize()); } - const payload = btoa(unescape(encodeURIComponent(JSON.stringify({ + const payload = btoa(unescape(encodeURIComponent(serializedObject({ showOn: this.showOn, hideOn: this.hideOn, + keysCapture: this.keysCapture, })))); const card = { id: this.id, @@ -858,7 +884,7 @@ export class Card { this.updateItemList() - bus.emit('selected_card_item', -1); + emit('selected_card_item', -1); } async copyItem(index: number) { @@ -888,6 +914,36 @@ export class Card { this.items.sort(sortCardItems); } + sortCardItemUp(item: CardItem, index: number) { + if (!this.items[index - 1]) { + return; + } + + const rows = [this.items[index - 1], this.items[index]]; + this.items.splice(index - 1, 2, rows[1], rows[0]); + + let counter = 0 + for (const index in this.items) { + this.items[index].weight = counter; + counter++; + } + } + + sortCardItemDown(item: CardItem, index: number) { + if (!this.items[index + 1]) { + return; + } + + const rows = [this.items[index], this.items[index + 1]]; + this.items.splice(index, 2, rows[1], rows[0]); + + let counter = 0 + for (const index in this.items) { + this.items[index].weight = counter; + counter++; + } + } + // --------------------------------- // events // --------------------------------- @@ -961,7 +1017,6 @@ export class Tab { cards: Card[] = []; columnWidth: number; dashboardId: number; - dragEnabled: boolean; enabled: boolean; entities: Map; gap: boolean; @@ -975,7 +1030,6 @@ export class Tab { this.cards = []; this.columnWidth = tab.columnWidth; this.dashboardId = tab.dashboardId; - this.dragEnabled = tab.dragEnabled; this.enabled = tab.enabled; this.entities = tab.entities; this.gap = tab.gap; @@ -997,7 +1051,7 @@ export class Tab { icon: '', columnWidth: columnWidth, gap: false, - background: 'inherit', + background: '', enabled: true, weight: weight, dashboardId: boardId @@ -1036,7 +1090,6 @@ export class Tab { background: this.background, icon: this.icon, enabled: this.enabled, - dragEnabled: this.dragEnabled, weight: this.weight, dashboardId: this.dashboardId, cards: cards, @@ -1052,6 +1105,39 @@ export class Tab { this.cards.sort(sortCards); } + sortCardUp(card: Card, index: number) { + if (!this.cards[index - 1]) { + return; + } + + const rows = [this.cards[index - 1], this.cards[index]]; + this.cards.splice(index - 1, 2, rows[1], rows[0]); + + let counter = 0 + for (const index in this.cards) { + this.cards[index].weight = counter; + this.cards[index].update(); + counter++; + } + + } + + sortCardDown(card: Card, index: number) { + if (!this.cards[index + 1]) { + return; + } + + const rows = [this.cards[index], this.cards[index + 1]]; + this.cards.splice(index, 2, rows[1], rows[0]); + + let counter = 0 + for (const index in this.cards) { + this.cards[index].weight = counter; + this.cards[index].update(); + counter++; + } + } + get cards2(): Card[] { //todo fix items sort const cards: Card[] = []; @@ -1077,8 +1163,7 @@ export class Tab { export class Core { current: ApiDashboard = {} as ApiDashboard; - activeTab = 0; // index - currentTabId: number | undefined; + private _activeTabIdx = 0; // index activeCard: number | undefined = undefined; // index currentCardId: number | undefined; @@ -1095,17 +1180,11 @@ export class Core { currentBoard(current: ApiDashboard) { this.current = current; this.tabs = []; - this.activeTab = 0; - this.currentTabId = undefined; + this._activeTabIdx = 0; if (current.tabs && current.tabs.length > 0) { for (const index in current.tabs) { this.tabs.push(new Tab(current.tabs[index])); } - this.currentTabId = this.tabs[this.activeTab].id; - - if (this.tabs[this.activeTab].cards.length > 0) { - this.activeCard = 0; - } } // get entity for card item @@ -1122,8 +1201,16 @@ export class Core { } } + if (!this.getActiveTab) { + return + } + this.sortTabs(); + if (this.getActiveTab.cards.length > 0) { + this.onSelectedCard(this.getActiveTab.cards[0].id) + } + this.updateCurrentTab(); } @@ -1204,106 +1291,144 @@ export class Core { // --------------------------------- // tabs // --------------------------------- + + set activeTabIdx(idx: number) { + if (this._activeTabIdx == idx) { + return + } + this._activeTabIdx = idx; + } + get activeTabIdx(): number { + return this._activeTabIdx; + } + get getActiveTab(): Tab | undefined { + if (this._activeTabIdx === undefined || this._activeTabIdx < 0) { + this._activeTabIdx = 0 + } + return this.tabs[this._activeTabIdx] || undefined; + } + + selectTabInMenu(idx: number) { + if (this._activeTabIdx === idx) return; + this._activeTabIdx = idx; + this.updateCurrentTab(); + } + async createTab() { const tab = await Tab.createNew(this.current.id, 'NEW_TAB' + (this.tabs.length + 1), 300, this.tabs.length); this.tabs.push(tab); - this.activeTab = (this.tabs.length - 1); - this.currentTabId = tab.id; + this._activeTabIdx = (this.tabs.length - 1); this.currentCardId = undefined; + this.activeCard = -1 } async updateTab() { - if (this.activeTab < 0) { + const tab = this.getActiveTab + if (!tab) { return; } - bus.emit('update_tab', this.currentTabId) - if (this.tabs[this.activeTab]) { - return this.tabs[this.activeTab].update(); + emit('update_tab', tab.id) + if (this.getActiveTab) { + return this.getActiveTab.update(); } } async removeTab() { - if (!this.currentTabId) { + const tab = this.getActiveTab + if (!tab) { return; } + this.tabs.splice(this._activeTabIdx, 1); + this._activeTabIdx = this.tabs.length - 1; + this.currentCardId = undefined; this.activeCard = undefined; - bus.emit('remove_tab', this.currentTabId) - return api.v1.dashboardTabServiceDeleteDashboardTab(this.currentTabId); + this.updateCurrentTab(); + + return api.v1.dashboardTabServiceDeleteDashboardTab(tab.id); } updateCurrentTab() { - if (!this.tabs.length) { - this.currentTabId = undefined; + const tab = this.getActiveTab + if (!tab) { return; } - this.currentTabId = this.tabs[this.activeTab].id; - console.log(`select tab id:${this.currentTabId}`); - // this.activeCard = -1 - // this.currentCardId = undefined - - bus.emit('update_tab', this.currentTabId) + console.log(`select tab id:${tab.id}`); + emit('update_tab', tab.id) } // --------------------------------- // cards // --------------------------------- onSelectedCard(id: number) { - if (this.activeTab < 0) { + const tab = this.getActiveTab + if (!tab) { return; } + // console.log(`select card id:${id}`); - for (const index in this.tabs[this.activeTab].cards) { - const cardId = this.tabs[this.activeTab].cards[index].id; - if (cardId === id) { + for (const index in tab.cards) { + const cardId = tab.cards[index].id; + if (cardId == id) { this.activeCard = index as unknown as number; this.currentCardId = id; - this.tabs[this.activeTab].cards[index].active = true + tab.cards[index].active = true } else { // console.log(`disable id:${cardId}`) - this.tabs[this.activeTab].cards[index].active = false + tab.cards[index].active = false } } } async createCard() { - if (this.activeTab < 0 || !this.currentTabId) { + const tab = this.getActiveTab + if (!tab) { return; } const card = await Card.createNew( - 'new card' + this.tabs[this.activeTab].cards.length, + 'new card' + tab.cards.length, randColor(), - this.tabs[this.activeTab].columnWidth, + tab.columnWidth, getSize(), - this.tabs[this.activeTab].id, - 10 * this.tabs[this.activeTab].cards.length || 0 + tab.id, + 10 * tab.cards.length || 0 ); - this.tabs[this.activeTab].cards.push(card); - this.activeCard = this.tabs[this.activeTab].cards.length - 1; + tab.cards.push(card); + this.activeCard = tab.cards.length - 1; this.currentCardId = card.id; - bus.emit('update_tab', this.currentTabId); + emit('update_tab', tab.id); } async updateCard() { - if (this.activeTab < 0 || this.activeCard == undefined) { + const tab = this.getActiveTab + if (!tab) { + return; + } + + if (this.activeCard == undefined) { return; } // move to direct call - // bus.emit('update_tab', this.currentTabId); + // emit('update_tab', this.currentTabId); - return this.tabs[this.activeTab].cards[this.activeCard].update(); + return tab.cards[this.activeCard].update(); } async removeCard() { - if (this.activeTab < 0 || !this.currentCardId) { + const tab = this.getActiveTab + if (!tab) { + return; + } + + if (!this.currentCardId) { return; } @@ -1311,9 +1436,9 @@ export class Core { const {data} = await api.v1.dashboardCardServiceDeleteDashboardCard(this.currentCardId); if (data) { - for (const index: number in this.tabs[this.activeTab].cards) { - if (this.tabs[this.activeTab].cards[index].id == this.currentCardId) { - this.tabs[this.activeTab].cards.splice(index, 1); + for (const index: number in tab.cards) { + if (tab.cards[index].id == this.currentCardId) { + tab.cards.splice(index, 1); } } @@ -1321,22 +1446,24 @@ export class Core { this.activeCard = undefined; } - if (this.currentTabId) { - bus.emit('update_tab', this.currentTabId); - } + emit('update_tab', tab.id); } async importCard(card: ApiDashboardCard) { - if (this.activeTab < 0 || !this.currentTabId) { + const tab = this.getActiveTab + if (!tab) { return; } - card.dashboardTabId = this.currentTabId; + + card.dashboardTabId = tab.id; + card.id = undefined + const {data} = await api.v1.dashboardCardServiceImportDashboardCard(card); if (data) { - this.tabs[this.activeTab].cards.push(new Card(data)); + this.getActiveTab.cards.push(new Card(data)); } - bus.emit('update_tab', this.currentTabId); + emit('update_tab', tab.id); return data; } @@ -1345,28 +1472,38 @@ export class Core { // Card item // --------------------------------- async createCardItem() { - if (this.activeTab < 0 || this.activeCard == undefined) { + const tab = this.getActiveTab + if (!tab) { return; } - const card = await this.tabs[this.activeTab].cards[this.activeCard]; + if (this.activeCard == undefined) { + return; + } + + const card = await tab.cards[this.activeCard]; if (!card) { return; } - await this.tabs[this.activeTab].cards[this.activeCard].createCardItem(); + await tab.cards[this.activeCard].createCardItem(); - // bus.emit('update_tab', this.currentTabId); + // emit('update_tab', this.currentTabId); } async removeCardItem(index: number) { - if (this.activeTab < 0 || this.activeCard == undefined) { + const tab = this.getActiveTab + if (!tab) { + return; + } + + if (this.activeCard == undefined) { return; } - await this.tabs[this.activeTab].cards[this.activeCard].removeItem(index); + await tab.cards[this.activeCard].removeItem(index); - // bus.emit('update_tab', this.currentTabId); + // emit('update_tab', this.currentTabId); } } // \Core @@ -1436,6 +1573,12 @@ export function serializedObject(obj: any): string { if (typeof value === 'function') { return value.toString(); // Convert function to string } + if(value instanceof Map) { + return { + dataType: 'Map', + value: Array.from(value.entries()), // or with spread: value: [...value] + }; + } return value; }); } @@ -1445,6 +1588,11 @@ export function parsedObject(str): any { if (typeof value === 'string' && value.indexOf('function') === 0) { return new Function('return ' + value)(); // Create a function using Function constructor } + if(typeof value === 'object' && value !== null) { + if (value.dataType === 'Map') { + return new Map(value.value); + } + } return value; }); } diff --git a/static_source/admin/src/views/Dashboard/editor/TabCard.vue b/static_source/admin/src/views/Dashboard/editor/TabCard.vue index 283c83baa..11b7bbf76 100644 --- a/static_source/admin/src/views/Dashboard/editor/TabCard.vue +++ b/static_source/admin/src/views/Dashboard/editor/TabCard.vue @@ -1,25 +1,44 @@ @@ -253,5 +320,10 @@ const onSnap = e => { .movable { position: absolute; } - +.item-card { + position: relative; + overflow: hidden; + width: 100%; + height: 100%; +} diff --git a/static_source/admin/src/views/Dashboard/editor/ViewTab.vue b/static_source/admin/src/views/Dashboard/editor/ViewTab.vue index de63aa84a..42884a041 100644 --- a/static_source/admin/src/views/Dashboard/editor/ViewTab.vue +++ b/static_source/admin/src/views/Dashboard/editor/ViewTab.vue @@ -27,18 +27,6 @@ const reload = debounce(() => { reloadKey.value += 1 }, 100) -watch( - () => props.tab, - (val?: Tab) => { - if (!val) return; - reload() - }, - { - deep: false, - immediate: true - } -) - useBus({ name: 'update_tab', callback: (tabId: number) => { @@ -89,11 +77,12 @@ const cards = computed(() => props.tab?.cards2) diff --git a/static_source/admin/src/views/Dashboard/editor/editor.vue b/static_source/admin/src/views/Dashboard/editor/editor.vue index 2cfd8b32f..8cea6a760 100644 --- a/static_source/admin/src/views/Dashboard/editor/editor.vue +++ b/static_source/admin/src/views/Dashboard/editor/editor.vue @@ -1,7 +1,7 @@