diff --git a/api/go.mod b/api/go.mod index d9a30bc12..8bda9eaff 100644 --- a/api/go.mod +++ b/api/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( github.com/99designs/gqlgen v0.17.12 github.com/DATA-DOG/go-sqlmock v1.5.0 - github.com/factly/x v0.0.77 + github.com/factly/x v0.0.84 github.com/gavv/httpexpect/v2 v2.2.0 github.com/go-chi/chi v4.1.2+incompatible github.com/go-chi/cors v1.1.1 diff --git a/api/go.sum b/api/go.sum index 4a12d26a8..45b6eb724 100644 --- a/api/go.sum +++ b/api/go.sum @@ -136,6 +136,10 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/factly/x v0.0.77 h1:HQXTOLTpPWZhNhCC3TwLQk9nfKlJqzSYjjrCQ20p57o= github.com/factly/x v0.0.77/go.mod h1:ZB3evopCd4ev4uZNCxldkh0AgYsxvEabiefrM8RJHBc= +github.com/factly/x v0.0.83 h1:p+/p/QhxWN2DYRuxeRYk5KV27dH+JFjXwKXQAPGIedk= +github.com/factly/x v0.0.83/go.mod h1:ZB3evopCd4ev4uZNCxldkh0AgYsxvEabiefrM8RJHBc= +github.com/factly/x v0.0.84 h1:tMVbLGAoF6gqZqFNiPY5zAybp7N2XwOolaukFBEYgxg= +github.com/factly/x v0.0.84/go.mod h1:ZB3evopCd4ev4uZNCxldkh0AgYsxvEabiefrM8RJHBc= github.com/fasthttp/websocket v1.4.2 h1:AU/zSiIIAuJjBMf5o+vO0syGOnEfvZRu40xIhW/3RuM= github.com/fasthttp/websocket v1.4.2/go.mod h1:smsv/h4PBEBaU0XDTY5UwJTpZv69fQ0FfcLJr21mA6Y= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= diff --git a/api/graph/loaders/dataloader.go b/api/graph/loaders/dataloader.go index fe3f54120..c7ba9f466 100644 --- a/api/graph/loaders/dataloader.go +++ b/api/graph/loaders/dataloader.go @@ -13,6 +13,7 @@ import ( "github.com/factly/dega-api/graph/models" "github.com/factly/dega-api/graph/validator" "github.com/factly/dega-api/util" + httpx "github.com/factly/dega-api/util/httpx" "github.com/spf13/viper" ) @@ -323,7 +324,7 @@ func DataloaderMiddleware(next http.Handler) http.Handler { } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-User", fmt.Sprint(keys[0])) - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { diff --git a/api/graph/resolvers/post.go b/api/graph/resolvers/post.go index 67f07ddd4..356503bb0 100644 --- a/api/graph/resolvers/post.go +++ b/api/graph/resolvers/post.go @@ -212,11 +212,12 @@ func (r *queryResolver) Posts(ctx context.Context, spaces []int, formats *models if err != nil { return nil, err } + oID, err := validator.GetOrganisation(ctx) if err != nil { return nil, err } - + columns := []string{"created_at", "updated_at", "name", "slug"} pageSortBy := "created_at" pageSortOrder := "desc" diff --git a/api/graph/resolvers/sitemap.go b/api/graph/resolvers/sitemap.go index a31d17f2e..851fbcfde 100644 --- a/api/graph/resolvers/sitemap.go +++ b/api/graph/resolvers/sitemap.go @@ -14,4 +14,4 @@ func (r *sitemapResolver) PublishedDate(ctx context.Context, obj *models.Sitemap func (r *Resolver) Sitemap() generated.SitemapResolver { return &sitemapResolver{r} } -type sitemapResolver struct{ *Resolver } +type sitemapResolver struct{ *Resolver } \ No newline at end of file diff --git a/api/graph/resolvers/sitemaps.go b/api/graph/resolvers/sitemaps.go index 7882023e0..ac87faf3f 100644 --- a/api/graph/resolvers/sitemaps.go +++ b/api/graph/resolvers/sitemaps.go @@ -11,6 +11,7 @@ import ( "github.com/factly/dega-api/graph/generated" "github.com/factly/dega-api/graph/models" "github.com/factly/dega-api/graph/validator" + "github.com/factly/dega-api/util/httpx" "github.com/spf13/viper" ) @@ -104,7 +105,7 @@ func (r *sitemapsResolver) Users(ctx context.Context, obj *models.Sitemaps) ([]* req.Header.Set("Content-Type", "application/json") req.Header.Set("X-User", fmt.Sprint(postAuthor.AuthorID)) - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { diff --git a/api/graph/validator/organisation.go b/api/graph/validator/organisation.go index f2263173f..b1407770f 100644 --- a/api/graph/validator/organisation.go +++ b/api/graph/validator/organisation.go @@ -2,11 +2,15 @@ package validator import ( "context" + "encoding/json" "errors" + "fmt" "net/http" - "github.com/factly/dega-api/config" - "github.com/factly/dega-api/graph/models" + "github.com/factly/dega-api/util/httpx" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + "github.com/spf13/viper" ) type ctxKeyOrganisationID int @@ -25,16 +29,20 @@ func CheckOrganisation() func(http.Handler) http.Handler { return } - space := &models.Space{} - space.ID = spaceID - - err = config.DB.First(&space).Error + token, err := GetSpaceToken(ctx) if err != nil { w.WriteHeader(http.StatusUnauthorized) return } - ctx = context.WithValue(ctx, OrgIDKey, space.OrganisationID) + organisationID, err := GetOrganisationIDfromSpaceID(uint(spaceID), token) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + ctx = context.WithValue(ctx, OrgIDKey, organisationID) next.ServeHTTP(w, r.WithContext(ctx)) }) } @@ -51,3 +59,28 @@ func GetOrganisation(ctx context.Context) (int, error) { } return 0, errors.New("something went wrong") } + +func GetOrganisationIDfromSpaceID(spaceID uint, token string) (int, error) { + req, err := http.NewRequest(http.MethodGet, viper.GetString("kavach_url")+fmt.Sprintf("/util/space/%d/getOrganisationUsingToken", spaceID), nil) + if err != nil { + return 0, err + } + req.Header.Set("X-Space-Token", token) + client := httpx.CustomHttpClient() + response, err := client.Do(req) + if err != nil { + return 0, err + } + defer response.Body.Close() + responseBody := map[string]interface{}{} + err = json.NewDecoder(response.Body).Decode(&responseBody) + if err != nil { + return 0, err + } + + if response.StatusCode != 200 { + return 0, errors.New("internal server error on kavach while getting organisation id from space id") + } + organisationID := int(responseBody["organisation_id"].(float64)) + return organisationID, nil +} diff --git a/api/graph/validator/space.go b/api/graph/validator/space.go index 5568318e3..d30818795 100644 --- a/api/graph/validator/space.go +++ b/api/graph/validator/space.go @@ -3,14 +3,25 @@ package validator import ( "context" "errors" + "fmt" + "net/http" "strconv" + + "github.com/factly/x/requestx" + "github.com/spf13/viper" ) type ctxKeySpaceID int +type ctxKeyToken string // SpaceIDKey is the key that holds the unique space ID in a request context. const SpaceIDKey ctxKeySpaceID = 0 +const TokenIDKey ctxKeyToken = "spaceToken" + +type ValidationBody struct { + Token string `json:"token" validate:"required"` +} // CheckSpace - to check Space in header func CheckSpace() func(http.Handler) http.Handler { @@ -22,14 +33,31 @@ func CheckSpace() func(http.Handler) http.Handler { return } - sid, err := strconv.Atoi(space) - if err != nil || sid == 0 { + sID, err := strconv.Atoi(space) + if err != nil || sID == 0 { w.WriteHeader(http.StatusUnauthorized) return } - ctx := r.Context() - ctx = context.WithValue(ctx, SpaceIDKey, sid) + authHeader := r.Header.Get("X-Dega-API-Key") + if authHeader == "" { + w.WriteHeader(http.StatusUnauthorized) + return + } + + tokenBody := ValidationBody{ + Token: authHeader, + } + + res, err := requestx.Request("POST", viper.GetString("kavach_url")+"/spaces/"+fmt.Sprintf("%d", sID)+"/validateToken", tokenBody, nil) + if err != nil || res.StatusCode != http.StatusOK { + w.WriteHeader(http.StatusUnauthorized) + return + } + + ctx := r.Context() + ctx = context.WithValue(ctx, SpaceIDKey, sID) + ctx = context.WithValue(ctx, TokenIDKey, authHeader) next.ServeHTTP(w, r.WithContext(ctx)) }) } @@ -46,3 +74,14 @@ func GetSpace(ctx context.Context) (uint, error) { } return 0, errors.New("something went wrong") } + +func GetSpaceToken(ctx context.Context) (string, error) { + if ctx == nil { + return "", errors.New("context not found") + } + token := ctx.Value(TokenIDKey) + if token != nil { + return token.(string), nil + } + return "", errors.New("something went wrong") +} diff --git a/api/server.go b/api/server.go index 012492f8a..77918e143 100644 --- a/api/server.go +++ b/api/server.go @@ -8,7 +8,6 @@ import ( "github.com/factly/x/healthx" "github.com/factly/x/loggerx" "github.com/factly/x/meilisearchx" - "github.com/factly/x/middlewarex" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/viper" @@ -79,7 +78,7 @@ func main() { srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers.Resolver{}})) - r := router.With(validator.CheckSpace(), validator.CheckOrganisation(), middlewarex.ValidateAPIToken("X-Dega-API-Key", "dega", validator.GetOrganisation)) + r := router.With(validator.CheckSpace(), validator.CheckOrganisation()) if cache.IsEnabled() { r = r.With(cache.CachingMiddleware(), cache.RespMiddleware) diff --git a/api/util/httpx/client.go b/api/util/httpx/client.go new file mode 100644 index 000000000..d9d70ad9e --- /dev/null +++ b/api/util/httpx/client.go @@ -0,0 +1,12 @@ +package httpx + +import ( + "net/http" + "time" +) + +const httpTimeout = 10 + +func CustomHttpClient() *http.Client { + return &http.Client{Timeout: time.Minute * time.Duration(httpTimeout)} +} diff --git a/config.env.example b/config.env.example new file mode 100644 index 000000000..cedfc5063 --- /dev/null +++ b/config.env.example @@ -0,0 +1,50 @@ +MODE=development + +# dependencies services +KAVACH_URL=http://kavach-server:8000 +IMAGEPROXY_URL=http://127.0.0.1:7001 +KETO_URL=http://keto:4466 +NATS_URL=http://nats:4222 +KRATOS_PUBLIC_URL=http://kratos:4433 +IFRAMELY_URL=http://iframely:8061 +OATHKEEPER_HOST=oathkeeper:4455 + +GOOGLE_KEY=GOOGLE_KEY # for google fact checks search + +# database params +DATABASE_HOST=postgres +DATABASE_USER=postgres +DATABASE_PASSWORD=postgres +DATABASE_NAME=dega +DATABASE_PORT=5432 +DATABASE_SSL_MODE=disable + +# set this to true if want to use sqlite db +USE_SQLITE=false +SQLITE_DB_PATH=dega.db + +TEMPLATES_PATH=web/templates/* + +DEFAULT_NUMBER_OF_MEDIA=10 +DEFAULT_NUMBER_OF_SPACES=2 +DEFAULT_NUMBER_OF_POSTS=10 + +CREATE_SUPER_ORGANISATION=true # give SUPER_ORGANISATION_TITLE, DEFAULT_USER_EMAIL & DEFAULT_USER_PASSWORD +SUPER_ORGANISATION_TITLE='Dega Administration' +DEFAULT_USER_EMAIL=admin@dega.in +DEFAULT_USER_PASSWORD=2ssad32sadADSd@!@4 + +ENABLE_HUKZ=true # include hukz in docker-compose and give HUKZ_URL, NATS_URL, NATS_USER_NAME & NATS_USER_PASSWORD +ENABLE_FEEDS=true +ENABLE_SEARCH_INDEXING=true # include meilisearch in docker-compost and give MEILI_KEY & MEILI_URL + +MEILI_URL=http://meilisearch:7700 +MEILI_KEY=password + +NATS_URL=nats://nats:4222 +NATS_USER_NAME=natsuser +NATS_USER_PASSWORD=natspassword +HUKZ_URL=http://hukz:8000 + +DEGA_APPLICATION_ID=1 +ORGANISATION_PERMISSION_ENABLED=false \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index dee9c68ca..367cb2f18 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,9 +18,9 @@ services: networks: - dega - ########################################################################### - ####### MINIO ####### - ########################################################################### + ######################################################################### + ##### MINIO ####### + ######################################################################### minio: image: minio/minio ports: @@ -45,14 +45,7 @@ services: networks: - dega entrypoint: > - /bin/sh -c " - apk add --no-cache bash; - wait-for-it.sh minio:9000 -- echo 'Minio is up. Creating the bucket!!'; - /usr/bin/mc config host add myminio http://minio:9000 miniokey miniosecret; - /usr/bin/mc mb myminio/dega; - /usr/bin/mc policy set download myminio/dega; - exit 0; - " + /bin/sh -c " apk add --no-cache bash; wait-for-it.sh minio:9000 -- echo 'Minio is up. Creating the bucket!!'; /usr/bin/mc config host add myminio http://minio:9000 miniokey miniosecret; /usr/bin/mc mb myminio/dega; /usr/bin/mc policy set download myminio/dega; exit 0; " ########################################################################### ####### IFRAMELY ####### @@ -150,24 +143,37 @@ services: - dega keto-migrate: - image: oryd/keto:v0.5.7 + image: oryd/keto:v0.9.0-alpha.0 environment: + - DSN=postgres://postgres:postgres@postgres:5432/keto?sslmode=disable - LOG_LEVEL=debug depends_on: - postgres - command: migrate sql postgres://postgres:postgres@postgres:5432/keto?sslmode=disable + volumes: + - type: bind + source: ./keto + target: /etc/config/keto + # command: namespace migrate legacy -c "/etc/config/keto/keto.yml" --yes + command: migrate up -c "/etc/config/keto/keto.yml" --yes restart: on-failure networks: - dega keto: - image: oryd/keto:v0.5.7 - ports: - - "4466:4466" depends_on: - keto-migrate + image: oryd/keto:v0.9.0-alpha.0 + ports: + - "4466:4466" # read + - "4467:4467" # write + - "4468:4468" # metric environment: - - DSN=postgres://postgres:postgres@postgres:5432/keto?sslmode=disable + - LOG_LEVEL=debug + volumes: + - type: bind + source: ./keto + target: /etc/config/keto + command: serve -c "/etc/config/keto/keto.yml" networks: - dega @@ -231,11 +237,14 @@ services: - kavach-migrate ports: - 5001:8000 - image: factly/kavach-server:0.23.0 + image: factly/kavach-server volumes: - type: bind source: ./volumes/kavach-server/data target: /app/data + - type: bind + source: ./kavach-config.env + target: /app/config.env environment: - WAIT_HOSTS=postgres:5432, kratos:4434 , keto:4466 - KAVACH_DATABASE_HOST=postgres @@ -251,6 +260,7 @@ services: - KAVACH_DOMAIN_NAME=http://127.0.0.1:4455/.factly/kavach/web - KAVACH_SENDGRID_API_KEY=SENDGRID_API_KEY - KAVACH_ENABLE_MULTITENANCY=true + - DISABLE_REGISTRATION=false restart: unless-stopped networks: - dega @@ -260,7 +270,7 @@ services: - postgres - keto - kratos - image: factly/kavach-server:0.23.0 + image: factly/kavach-migrate environment: - WAIT_HOSTS=postgres:5432, kratos:4434 , keto:4466 - KAVACH_DATABASE_HOST=postgres @@ -276,12 +286,15 @@ services: - KAVACH_DOMAIN_NAME=http://127.0.0.1:4455/.factly/kavach/web - KAVACH_SENDGRID_API_KEY=SENDGRID_API_KEY - KAVACH_ENABLE_MULTITENANCY=true - entrypoint: /app/kavach-server migrate + volumes: + - type: bind + source: ./kavach-config.env + target: /app/config.env networks: - dega kavach-web: - image: factly/kavach-web:0.23.0-dev + image: factly/kavach-web volumes: - type: bind source: ./volumes/kavach-web/config.js diff --git a/kavach-config.env.example b/kavach-config.env.example new file mode 100644 index 000000000..99e8c58a3 --- /dev/null +++ b/kavach-config.env.example @@ -0,0 +1,30 @@ +DATABASE_HOST=postgres +DATABASE_USER=postgres +DATABASE_PASSWORD=postgres +DATABASE_NAME=kavach +DATABASE_PORT=5432 +DATABASE_SSL_MODE=disable +KETO_URL=http://keto:4466 +KETO_WRITE_API_URL=http://keto:4467 +KETO_READ_API_URL=http://keto:4466 +KRATOS_ADMIN_URL=http://kratos:4434 +KRATOS_PUBLIC_URL=http://kratos:4433 +MODE=development +IMAGEPROXY_URL=http://127.0.0.1:7001 +USE_SQLITE=false +SQLITE_DB_PATH=kavach.db +SENDGRID_API_KEY=API-KEY +DOMAIN_NAME=http://127.0.0.1:4455/.factly/kavach/web # development mode +DYNAMIC_FROM_EMAIL='kavach' +MANDE_HOST=http://mande.factly.in +DYNAMIC_MANDE_TEMPLATE_ID=1 +DYNAMIC_SENDGRID_API_KEY=API-KEY +ENABLE_MULTITENANCY=true +DISABLE_REGISTRATION=false +SUPER_USER_EMAIL=kavach-develop@factly.in +APPLICATION_NAME=Kavach +DEFAULT_USER_EMAIL=vsumit01@gmail.com +DEFAULT_USER_PASSWORD=Cristiano123$$ +DEFAULT_ORGANISATION_NAME=FACTLYADMIN + + diff --git a/keto/keto.yml b/keto/keto.yml new file mode 100644 index 000000000..1fc672fa5 --- /dev/null +++ b/keto/keto.yml @@ -0,0 +1,12 @@ + +dsn: postgres://postgres:postgres@postgres:5432/keto?sslmode=disable +version: v0.9.0-alpha.0 +namespaces: + - id: 0 + name: organisations + - id: 1 + name: applications + - id: 2 + name: spaces + - id: 3 + name: superorganisation diff --git a/server/config/organisation.go b/server/config/organisation.go index 6f04a4e10..04241e16e 100644 --- a/server/config/organisation.go +++ b/server/config/organisation.go @@ -8,7 +8,7 @@ import ( "net/http" "regexp" "strings" - + "github.com/factly/dega-server/util/http" "github.com/spf13/viper" ) @@ -51,8 +51,7 @@ func CheckSuperOrganisation() bool { // check if policy is present in keto req, _ := http.NewRequest("GET", viper.GetString("keto_url")+ketoPolicyPath+"/app:dega:superorg", nil) req.Header.Set("Content-Type", "application/json") - - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { return false @@ -77,7 +76,6 @@ func CheckSuperOrganisation() bool { req, _ = http.NewRequest("GET", viper.GetString("kavach_url")+"/organisations/"+orgID, nil) req.Header.Set("Content-Type", "application/json") - client = &http.Client{} resp, err = client.Do(req) if err != nil { return false @@ -177,7 +175,7 @@ func createKratosUser() (*http.Response, error) { req, _ := http.NewRequest("GET", viper.GetString("kratos_public_url")+"/self-service/registration/api", nil) req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { return nil, err @@ -231,7 +229,7 @@ func createKavachUser(kavachUserCheckers map[string]interface{}) (*http.Response req, _ := http.NewRequest("POST", viper.GetString("kavach_url")+"/users/checker", buf) req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { return nil, err @@ -262,7 +260,7 @@ func createKavachOrganisation(userID string) (*http.Response, error) { req.Header.Set("Content-Type", "application/json") req.Header.Set("X-User", userID) - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { @@ -302,7 +300,7 @@ func createKetoPolicy(organisationID uint) (*http.Response, error) { req, _ := http.NewRequest("PUT", viper.GetString("keto_url")+ketoPolicyPath, buf) req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { diff --git a/server/config/vars.go b/server/config/vars.go index cecfc9b42..25e1df9d1 100644 --- a/server/config/vars.go +++ b/server/config/vars.go @@ -59,6 +59,9 @@ func SetupVars() { log.Fatal("please provide create_super_organisation (bool) config param") } + if !viper.IsSet("organisation_permission_enabled") { + log.Fatal("please provide organisation permission enabled bool config param") + } if viper.GetBool("create_super_organisation") { if !viper.IsSet("kratos_public_url") { log.Fatal("please provide kratos_public_url config param") diff --git a/server/data/policies.json b/server/data/policies.json index 9a6bb1605..c6b890ffa 100644 --- a/server/data/policies.json +++ b/server/data/policies.json @@ -1,7 +1,7 @@ [ { "name": "Editor", - "Description": "Editor", + "description": "Editor", "permissions": [ { "resource": "categories", @@ -47,7 +47,7 @@ }, { "name": "Author", - "Description": "Author", + "description": "Author", "permissions": [ { "resource": "categories", @@ -93,7 +93,7 @@ }, { "name": "Contributor", - "Description": "Contributor", + "description": "Contributor", "permissions": [ { "resource": "formats", diff --git a/server/data/roles.json b/server/data/roles.json new file mode 100644 index 000000000..24d0a24ea --- /dev/null +++ b/server/data/roles.json @@ -0,0 +1,17 @@ +[ + { + "name":"Editor", + "slug":"editor", + "description":"Someone who edits the posts, pages, factchecks, etc." + }, + { + "name":"Author", + "slug":"author", + "description":"Someone who writes the posts, pages, factchecks, etc." + }, + { + "name":"Contributor", + "slug":"contributor", + "description":"Someone who contributes in some way to the posts, pages, factchecks, etc." + } +] \ No newline at end of file diff --git a/server/go.mod b/server/go.mod index a6d16f7d9..30debdf0c 100644 --- a/server/go.mod +++ b/server/go.mod @@ -7,7 +7,7 @@ require ( github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 github.com/dlmiddlecote/sqlstats v1.0.2 github.com/eduncan911/podcast v1.4.2 - github.com/factly/x v0.0.77 + github.com/factly/x v0.0.82 github.com/gavv/httpexpect v2.0.0+incompatible github.com/gavv/httpexpect/v2 v2.1.0 github.com/go-chi/chi v4.1.2+incompatible diff --git a/server/go.sum b/server/go.sum index b41080dd1..a6fe35987 100644 --- a/server/go.sum +++ b/server/go.sum @@ -140,8 +140,12 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= -github.com/factly/x v0.0.77 h1:HQXTOLTpPWZhNhCC3TwLQk9nfKlJqzSYjjrCQ20p57o= -github.com/factly/x v0.0.77/go.mod h1:ZB3evopCd4ev4uZNCxldkh0AgYsxvEabiefrM8RJHBc= +github.com/factly/x v0.0.80 h1:K23FGEPdPNPsGvReVnnfZFhjiYRdu4phzRm0Soh8XVw= +github.com/factly/x v0.0.80/go.mod h1:ZB3evopCd4ev4uZNCxldkh0AgYsxvEabiefrM8RJHBc= +github.com/factly/x v0.0.81 h1:jkJd6icepUhSko3qffN9nlXio27r8c8zK1oT7d+LMgA= +github.com/factly/x v0.0.81/go.mod h1:ZB3evopCd4ev4uZNCxldkh0AgYsxvEabiefrM8RJHBc= +github.com/factly/x v0.0.82 h1:qKrmP6yDEeyS20nKYWxG0gDUZqkMogW8xSkIhqXSEbc= +github.com/factly/x v0.0.82/go.mod h1:ZB3evopCd4ev4uZNCxldkh0AgYsxvEabiefrM8RJHBc= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fasthttp/websocket v1.4.2 h1:AU/zSiIIAuJjBMf5o+vO0syGOnEfvZRu40xIhW/3RuM= diff --git a/server/service/core/action/author/feed.go b/server/service/core/action/author/feed.go index 6aff3d3c5..a906da9af 100644 --- a/server/service/core/action/author/feed.go +++ b/server/service/core/action/author/feed.go @@ -10,14 +10,22 @@ import ( "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" + "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" "github.com/factly/x/paginationx" "github.com/go-chi/chi" "github.com/gorilla/feeds" ) func Feeds(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } spaceID := chi.URLParam(r, "space_id") sID, err := strconv.Atoi(spaceID) if err != nil { @@ -26,6 +34,13 @@ func Feeds(w http.ResponseWriter, r *http.Request) { return } + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + offset, limit := paginationx.Parse(r.URL.Query()) sort := r.URL.Query().Get("sort") if sort != "asc" { @@ -41,14 +56,13 @@ func Feeds(w http.ResponseWriter, r *http.Request) { slugMap[each] = true } - space := model.Space{} - space.ID = uint(sID) - if err := config.DB.Preload("Logo").First(&space).Error; err != nil { + space, err := util.GetSpacefromKavach(uint(uID), uint(orgID), uint(sID)) + if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.RecordNotFound())) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - + now := time.Now() feed := &feeds.Feed{ Id: fmt.Sprint(space.ID), diff --git a/server/service/core/action/author/list.go b/server/service/core/action/author/list.go index 853ed9c7f..f25295f6f 100644 --- a/server/service/core/action/author/list.go +++ b/server/service/core/action/author/list.go @@ -11,6 +11,7 @@ import ( "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/middlewarex" "github.com/factly/x/paginationx" @@ -52,10 +53,24 @@ func list(w http.ResponseWriter, r *http.Request) { return } + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + sID, err := middlewarex.GetSpace(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + result := paging{} result.Nodes = make([]model.Author, 0) - url := fmt.Sprint(viper.GetString("kavach_url"), "/organisations/", oID, "/users") + url := fmt.Sprint(viper.GetString("kavach_url"), "/organisations/", oID, "/applications/", applicationID, "/spaces/", sID, "/users") req, err := http.NewRequest("GET", url, nil) if err != nil { @@ -66,7 +81,7 @@ func list(w http.ResponseWriter, r *http.Request) { req.Header.Set("Content-Type", "application/json") req.Header.Set("X-User", strconv.Itoa(uID)) - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { diff --git a/server/service/core/action/author/mapper.go b/server/service/core/action/author/mapper.go index b7e983bb7..9bb85bf99 100644 --- a/server/service/core/action/author/mapper.go +++ b/server/service/core/action/author/mapper.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/factly/dega-server/service/core/model" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/loggerx" "github.com/spf13/viper" ) @@ -22,9 +23,8 @@ func Mapper(oID int, uID int) map[string]model.Author { } req.Header.Set("Content-Type", "application/json") req.Header.Set("X-User", fmt.Sprint(uID)) - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) - if err != nil { return userMap } diff --git a/server/service/core/action/category/create.go b/server/service/core/action/category/create.go index 97911346c..893ca1d11 100644 --- a/server/service/core/action/category/create.go +++ b/server/service/core/action/category/create.go @@ -5,14 +5,13 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "github.com/factly/x/loggerx" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/meilisearchx" @@ -112,12 +111,20 @@ func create(w http.ResponseWriter, r *http.Request) { } // Store HTML description - var description string - if len(category.Description.RawMessage) > 0 && !reflect.DeepEqual(category.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(category.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(category.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(category.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse category description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(category.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -128,9 +135,9 @@ func create(w http.ResponseWriter, r *http.Request) { UpdatedAt: category.UpdatedAt, }, Name: category.Name, - Description: category.Description, + Description: jsonDescription, BackgroundColour: category.BackgroundColour, - HTMLDescription: description, + HTMLDescription: htmlDescription, Slug: slugx.Approve(&config.DB, categorySlug, sID, tableName), ParentID: parentID, MediumID: mediumID, diff --git a/server/service/core/action/category/feed.go b/server/service/core/action/category/feed.go index fcfa1eccd..19b9152e9 100644 --- a/server/service/core/action/category/feed.go +++ b/server/service/core/action/category/feed.go @@ -8,13 +8,22 @@ import ( "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/action/post" "github.com/factly/dega-server/service/core/model" + "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" "github.com/factly/x/paginationx" "github.com/go-chi/chi" ) func Feeds(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + spaceID := chi.URLParam(r, "space_id") sID, err := strconv.Atoi(spaceID) if err != nil { @@ -23,6 +32,20 @@ func Feeds(w http.ResponseWriter, r *http.Request) { return } + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + space, err := util.GetSpacefromKavach(uint(uID), uint(orgID), uint(sID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + offset, limit := paginationx.Parse(r.URL.Query()) sort := r.URL.Query().Get("sort") if sort != "asc" { @@ -32,14 +55,6 @@ func Feeds(w http.ResponseWriter, r *http.Request) { slugs := chi.URLParam(r, "slugs") categorySlugs := strings.Split(slugs, ",") - space := model.Space{} - space.ID = uint(sID) - if err := config.DB.Preload("Logo").First(&space).Error; err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.RecordNotFound())) - return - } - categoryIDs := make([]uint, 0) categoryList := make([]model.Category, 0) config.DB.Model(&model.Category{}).Where("slug IN (?)", categorySlugs).Find(&categoryList) @@ -47,7 +62,7 @@ func Feeds(w http.ResponseWriter, r *http.Request) { categoryIDs = append(categoryIDs, each.ID) } - feed := post.GetFeed(space) + feed := post.GetFeed(*space) postList := make([]model.Post, 0) config.DB.Model(&model.Post{}).Joins("JOIN post_categories ON posts.id = post_categories.post_id").Where(&model.Post{ @@ -55,7 +70,7 @@ func Feeds(w http.ResponseWriter, r *http.Request) { SpaceID: uint(sID), }).Where("is_page = ?", false).Where("category_id IN (?)", categoryIDs).Order("created_at " + sort).Offset(offset).Limit(limit).Find(&postList) - feed.Items = post.GetItemsList(postList, space) + feed.Items = post.GetItemsList(postList, *space) if err := feed.WriteRss(w); err != nil { loggerx.Error(err) diff --git a/server/service/core/action/category/update.go b/server/service/core/action/category/update.go index 16b4195d5..46145f6fc 100644 --- a/server/service/core/action/category/update.go +++ b/server/service/core/action/category/update.go @@ -4,13 +4,11 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "strconv" "time" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -20,6 +18,7 @@ import ( "github.com/factly/x/slugx" "github.com/factly/x/validationx" "github.com/go-chi/chi" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -135,12 +134,20 @@ func update(w http.ResponseWriter, r *http.Request) { } // Store HTML description - var description string - if len(category.Description.RawMessage) > 0 && !reflect.DeepEqual(category.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(category.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(category.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(category.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse category description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(category.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -152,8 +159,8 @@ func update(w http.ResponseWriter, r *http.Request) { "updated_by_id": uID, "name": category.Name, "slug": categorySlug, - "description": category.Description, - "html_description": description, + "description": jsonDescription, + "html_description": htmlDescription, "medium_id": category.MediumID, "is_featured": category.IsFeatured, "meta_fields": category.MetaFields, diff --git a/server/service/core/action/format/default.go b/server/service/core/action/format/default.go index 0c8475ebf..dbddca808 100644 --- a/server/service/core/action/format/default.go +++ b/server/service/core/action/format/default.go @@ -67,7 +67,12 @@ func createDefaults(w http.ResponseWriter, r *http.Request) { tx := config.DB.WithContext(context.WithValue(r.Context(), userContext, uID)).Begin() for i := range formats { formats[i].SpaceID = uint(sID) - tx.Model(&model.Format{}).FirstOrCreate(&formats[i], &formats[i]) + err := tx.Model(&model.Format{}).FirstOrCreate(&formats[i], &formats[i]).Error + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DBError())) + return + } if config.SearchEnabled() { _ = insertIntoMeili(formats[i]) } diff --git a/server/service/core/action/format/feed.go b/server/service/core/action/format/feed.go index a3ae44ec2..46780294c 100644 --- a/server/service/core/action/format/feed.go +++ b/server/service/core/action/format/feed.go @@ -8,13 +8,21 @@ import ( "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/action/post" "github.com/factly/dega-server/service/core/model" + "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" "github.com/factly/x/paginationx" "github.com/go-chi/chi" ) func Feeds(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } spaceID := chi.URLParam(r, "space_id") sID, err := strconv.Atoi(spaceID) if err != nil { @@ -23,6 +31,13 @@ func Feeds(w http.ResponseWriter, r *http.Request) { return } + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + offset, limit := paginationx.Parse(r.URL.Query()) sort := r.URL.Query().Get("sort") if sort != "asc" { @@ -32,11 +47,10 @@ func Feeds(w http.ResponseWriter, r *http.Request) { slugs := chi.URLParam(r, "slugs") formatSlugs := strings.Split(slugs, ",") - space := model.Space{} - space.ID = uint(sID) - if err := config.DB.Preload("Logo").First(&space).Error; err != nil { + space, err := util.GetSpacefromKavach(uint(uID), uint(orgID), uint(sID)) + if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.RecordNotFound())) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } @@ -47,7 +61,7 @@ func Feeds(w http.ResponseWriter, r *http.Request) { formatIDs = append(formatIDs, each.ID) } - feed := post.GetFeed(space) + feed := post.GetFeed(*space) postList := make([]model.Post, 0) config.DB.Model(&model.Post{}).Where(&model.Post{ @@ -55,7 +69,7 @@ func Feeds(w http.ResponseWriter, r *http.Request) { SpaceID: uint(sID), }).Where("is_page = ?", false).Where("format_id IN (?)", formatIDs).Order("created_at " + sort).Offset(offset).Limit(limit).Find(&postList) - feed.Items = post.GetItemsList(postList, space) + feed.Items = post.GetItemsList(postList, *space) if err := feed.WriteRss(w); err != nil { loggerx.Error(err) diff --git a/server/service/core/action/page/create.go b/server/service/core/action/page/create.go index 59865545e..3da14c003 100644 --- a/server/service/core/action/page/create.go +++ b/server/service/core/action/page/create.go @@ -6,12 +6,10 @@ import ( "errors" "fmt" "net/http" - "reflect" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/core/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -20,6 +18,7 @@ import ( "github.com/factly/x/renderx" "github.com/factly/x/slugx" "github.com/factly/x/validationx" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -86,12 +85,20 @@ func create(w http.ResponseWriter, r *http.Request) { } // Store HTML description - var description string - if len(page.Description.RawMessage) > 0 && !reflect.DeepEqual(page.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(page.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(page.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(page.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse post description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(page.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -107,8 +114,8 @@ func create(w http.ResponseWriter, r *http.Request) { IsPage: true, Subtitle: page.Subtitle, Excerpt: page.Excerpt, - Description: page.Description, - HTMLDescription: description, + Description: jsonDescription, + HTMLDescription: htmlDescription, IsHighlighted: page.IsHighlighted, IsSticky: page.IsSticky, FeaturedMediumID: featuredMediumID, diff --git a/server/service/core/action/page/list.go b/server/service/core/action/page/list.go index a3dfaf3f5..0fbfdf686 100644 --- a/server/service/core/action/page/list.go +++ b/server/service/core/action/page/list.go @@ -167,6 +167,11 @@ func list(w http.ResponseWriter, r *http.Request) { result.Nodes = append(result.Nodes, *pageList) } + // for _, node := range result.Nodes { + // println(node) + + // } + renderx.JSON(w, http.StatusOK, result) } func generateFilters(tagIDs, categoryIDs, authorIDs, status []string) string { diff --git a/server/service/core/action/page/update.go b/server/service/core/action/page/update.go index 98e7e4ba7..40f048081 100644 --- a/server/service/core/action/page/update.go +++ b/server/service/core/action/page/update.go @@ -6,14 +6,12 @@ import ( "errors" "fmt" "net/http" - "reflect" "strconv" "time" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/core/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/dega-server/util/arrays" "github.com/factly/x/errorx" @@ -24,6 +22,7 @@ import ( "github.com/factly/x/slugx" "github.com/factly/x/validationx" "github.com/go-chi/chi" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -122,12 +121,20 @@ func update(w http.ResponseWriter, r *http.Request) { } // Store HTML description - var description string - if len(page.Description.RawMessage) > 0 && !reflect.DeepEqual(page.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(page.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(page.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(page.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse page description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(page.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -170,8 +177,8 @@ func update(w http.ResponseWriter, r *http.Request) { "status": page.Status, "published_date": page.PublishedDate, "excerpt": page.Excerpt, - "description": page.Description, - "html_description": description, + "description": jsonDescription, + "html_description": htmlDescription, "is_highlighted": page.IsHighlighted, "is_sticky": page.IsSticky, "format_id": page.FormatID, diff --git a/server/service/core/action/permissions/space/details.go b/server/service/core/action/permissions/space/details.go index a8c3e5e40..716bedf25 100644 --- a/server/service/core/action/permissions/space/details.go +++ b/server/service/core/action/permissions/space/details.go @@ -35,7 +35,7 @@ func details(w http.ResponseWriter, r *http.Request) { result := model.SpacePermission{} result.ID = uint(id) - err = config.DB.Preload("Space").First(&result).Error + err = config.DB.First(&result).Error if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.RecordNotFound())) diff --git a/server/service/core/action/permissions/space/my.go b/server/service/core/action/permissions/space/my.go index 2b343a4d3..e3a9154cd 100644 --- a/server/service/core/action/permissions/space/my.go +++ b/server/service/core/action/permissions/space/my.go @@ -33,7 +33,7 @@ func my(w http.ResponseWriter, r *http.Request) { err = config.DB.Model(&model.SpacePermission{}).Where(&model.SpacePermission{ SpaceID: uint(sID), - }).Preload("Space").First(&result).Error + }).First(&result).Error if err != nil { loggerx.Error(err) diff --git a/server/service/core/action/policy/composer.go b/server/service/core/action/policy/composer.go index 5e4188337..e70417abf 100644 --- a/server/service/core/action/policy/composer.go +++ b/server/service/core/action/policy/composer.go @@ -6,10 +6,10 @@ import ( "fmt" "net/http" - "github.com/spf13/viper" - "github.com/factly/dega-server/service/core/model" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/loggerx" + "github.com/spf13/viper" ) func contains(s []string, e string) bool { @@ -60,8 +60,7 @@ func Composer(oID int, sID int, inputPolicy policyReq) model.KetoPolicy { loggerx.Error(err) } req.Header.Set("Content-Type", "application/json") - - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { diff --git a/server/service/core/action/policy/create.go b/server/service/core/action/policy/create.go index 33e196d0c..323d9f25d 100644 --- a/server/service/core/action/policy/create.go +++ b/server/service/core/action/policy/create.go @@ -1,17 +1,22 @@ package policy import ( + "bytes" "encoding/json" + "errors" + "fmt" "net/http" + "strconv" - "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/meilisearchx" "github.com/factly/x/middlewarex" "github.com/factly/x/renderx" + "github.com/spf13/viper" ) // create - Create policy @@ -51,7 +56,14 @@ func create(w http.ResponseWriter, r *http.Request) { return } - policyReq := policyReq{} + applicationID, err := util.GetApplicationID(uint(userID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + policyReq := kavachPolicy{} err = json.NewDecoder(r.Body).Decode(&policyReq) if err != nil { @@ -60,9 +72,48 @@ func create(w http.ResponseWriter, r *http.Request) { return } - result := Mapper(Composer(organisationID, spaceID, policyReq), author.Mapper(organisationID, userID)) + buf := new(bytes.Buffer) + err = json.NewEncoder(buf).Encode(policyReq) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + requrl := viper.GetString("kavach_url") + "/organisations/" + fmt.Sprintf("%d", organisationID) + "/applications/" + fmt.Sprintf("%d", applicationID) + "/spaces/" + fmt.Sprintf("%d", spaceID) + "/policy" + req, err := http.NewRequest(http.MethodPost, requrl, buf) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } - err = insertIntoMeili(result) + req.Header.Set("X-User", strconv.Itoa(userID)) + req.Header.Set("Content-Type", "application/json") + + client := httpx.CustomHttpClient() + resp, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + loggerx.Error(errors.New("internal server error on kavach server")) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + result := &model.KavachPolicy{} + err = json.NewDecoder(resp.Body).Decode(result) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + err = insertIntoMeili(*result) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) @@ -80,7 +131,7 @@ func create(w http.ResponseWriter, r *http.Request) { renderx.JSON(w, http.StatusOK, result) } -func insertIntoMeili(result model.Policy) error { +func insertIntoMeili(result model.KavachPolicy) error { // Insert into meili index meiliObj := map[string]interface{}{ "id": result.ID, diff --git a/server/service/core/action/policy/default.go b/server/service/core/action/policy/default.go index e42280866..5277aeeb1 100644 --- a/server/service/core/action/policy/default.go +++ b/server/service/core/action/policy/default.go @@ -1,22 +1,28 @@ package policy import ( + "bytes" "encoding/json" + "errors" + "fmt" "io/ioutil" "net/http" "os" - "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/middlewarex" "github.com/factly/x/renderx" + "github.com/factly/x/slugx" + "github.com/spf13/viper" ) -// DataFile default json data file -var DataFile = "./data/policies.json" +// policyDataFile default json data file +var policyDataFile = "./data/policies.json" +var rolesDataFile = "./data/roles.json" // createDefaults - Create Default Policies // @Summary Create Default Policies @@ -52,18 +58,20 @@ func createDefaults(w http.ResponseWriter, r *http.Request) { return } - jsonFile, err := os.Open(DataFile) + policyJsonFile, err := os.Open(policyDataFile) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - - defer jsonFile.Close() - + defer policyJsonFile.Close() policies := make([]policyReq, 0) - - byteValue, _ := ioutil.ReadAll(jsonFile) + byteValue, err := ioutil.ReadAll(policyJsonFile) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } err = json.Unmarshal(byteValue, &policies) if err != nil { loggerx.Error(err) @@ -71,23 +79,106 @@ func createDefaults(w http.ResponseWriter, r *http.Request) { return } - authors := author.Mapper(oID, uID) - - result := paging{} - result.Nodes = make([]model.Policy, 0) - - for _, policy := range policies { - res := Mapper(Composer(oID, sID, policy), authors) - result.Nodes = append(result.Nodes, res) + rolesJsonFile, err := os.Open(rolesDataFile) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + defer rolesJsonFile.Close() + roles := make([]roleReq, 0) + byteValue, err = ioutil.ReadAll(rolesJsonFile) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + err = json.Unmarshal(byteValue, &roles) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } - if err = insertIntoMeili(res); err != nil { + defaultPolicies := make([]model.KavachPolicy, 0) + for index, role := range roles { + policy, err := createRoleandPolicyonKavach(role, policies[index], uint(oID), uint(sID), uint(uID)) + if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } + + defaultPolicies = append(defaultPolicies, *policy) + } + renderx.JSON(w, http.StatusCreated, defaultPolicies) +} + +func createRoleandPolicyonKavach(role roleReq, policy policyReq, orgID, spaceID, userID uint) (*model.KavachPolicy, error) { + buf := new(bytes.Buffer) + err := json.NewEncoder(buf).Encode(&role) + if err != nil { + return nil, err + } + + applicationID, err := util.GetApplicationID(uint(userID), "dega") + if err != nil { + return nil, err + } + + client := httpx.CustomHttpClient() + req, err := http.NewRequest(http.MethodPost, viper.GetString("kavach_url")+"/organisations/"+fmt.Sprintf("%d", orgID)+"/applications/"+fmt.Sprintf("%d", applicationID)+"/spaces/"+fmt.Sprintf("%d", spaceID)+"/roles", buf) + if err != nil { + return nil, err + } + req.Header.Set("X-User", fmt.Sprintf("%d", userID)) + resp, err := client.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + if resp.StatusCode != 200 { + return nil, errors.New("could not create role in the space") + } + + spaceRole := model.SpaceRole{} + err = json.NewDecoder(resp.Body).Decode(&spaceRole) + if err != nil { + return nil, errors.New("unable to decode default space role response body") + } + policyReqBody := map[string]interface{}{ + "name": policy.Name, + "description": policy.Description, + "slug": slugx.Make(policy.Name), + "permissions": policy.Permissions, + "roles": append([]int{}, int(spaceRole.ID)), + } + + err = json.NewEncoder(buf).Encode(&policyReqBody) + if err != nil { + return nil, err + } + + req, err = http.NewRequest(http.MethodPost, viper.GetString("kavach_url")+"/organisations/"+fmt.Sprintf("%d", orgID)+"/applications/"+fmt.Sprintf("%d", applicationID)+"/spaces/"+fmt.Sprintf("%d", spaceID)+"/policy", buf) + if err != nil { + return nil, err + } + req.Header.Set("X-User", fmt.Sprintf("%d", userID)) + resp, err = client.Do(req) + if err != nil { + return nil, err } - result.Total = len(result.Nodes) + if resp.StatusCode != 200 { + return nil, errors.New("error in creating policy on kavach") + } + + spacePolicy := &model.KavachPolicy{} + err = json.NewDecoder(resp.Body).Decode(spacePolicy) + if err != nil { + return nil, errors.New("unable to decode default space policy response body") + } - renderx.JSON(w, http.StatusCreated, result) + return spacePolicy, nil } diff --git a/server/service/core/action/policy/delete.go b/server/service/core/action/policy/delete.go index b3405848c..c5ff0465f 100644 --- a/server/service/core/action/policy/delete.go +++ b/server/service/core/action/policy/delete.go @@ -1,10 +1,12 @@ package policy import ( + "errors" "fmt" "net/http" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" meilisearchx "github.com/factly/x/meilisearchx" @@ -35,6 +37,13 @@ func delete(w http.ResponseWriter, r *http.Request) { return } + userID, err := middlewarex.GetUser(r.Context()) + + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } organisationID, err := util.GetOrganisation(r.Context()) if err != nil { @@ -43,20 +52,27 @@ func delete(w http.ResponseWriter, r *http.Request) { return } + applicationID, err := util.GetApplicationID(uint(userID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + /* delete old policy */ policyId := chi.URLParam(r, "policy_id") - policyID := fmt.Sprint("id:org:", organisationID, ":app:dega:space:", spaceID, ":"+policyId) - - req, err := http.NewRequest("DELETE", viper.GetString("keto_url")+"/engines/acp/ory/regex/policies/"+policyID, nil) + reqURL := viper.GetString("kavach_url") + fmt.Sprintf("/organisations/%d/applications/%d/spaces/%d/policy/%s", organisationID, applicationID, spaceID, policyId) + req, err := http.NewRequest(http.MethodDelete, reqURL, nil) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } + req.Header.Set("X-User", fmt.Sprintf("%d", userID)) req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { @@ -66,6 +82,11 @@ func delete(w http.ResponseWriter, r *http.Request) { } defer resp.Body.Close() + if resp.StatusCode != 200 { + loggerx.Error(errors.New("unable to delete policy on kavach")) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } objectID := fmt.Sprint("policy_", policyId) _, err = meilisearchx.Client.Index("dega").Delete(objectID) diff --git a/server/service/core/action/policy/details.go b/server/service/core/action/policy/details.go index 873e283fd..a040025c2 100644 --- a/server/service/core/action/policy/details.go +++ b/server/service/core/action/policy/details.go @@ -5,9 +5,9 @@ import ( "fmt" "net/http" - "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/middlewarex" @@ -53,19 +53,26 @@ func details(w http.ResponseWriter, r *http.Request) { return } - policyID := chi.URLParam(r, "policy_id") + applicationID, err := util.GetApplicationID(uint(userID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } - ketoPolicyID := fmt.Sprint("id:org:", organisationID, ":app:dega:space:", spaceID, ":", policyID) + policyID := chi.URLParam(r, "policy_id") - req, err := http.NewRequest("GET", viper.GetString("keto_url")+"/engines/acp/ory/regex/policies/"+ketoPolicyID, nil) + reqURL := viper.GetString("kavach_url") + fmt.Sprintf("/organisations/%d/applications/%d/spaces/%d/policy/%s", organisationID, applicationID, spaceID, policyID) + req, err := http.NewRequest("GET", reqURL, nil) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } + req.Header.Set("X-User", fmt.Sprintf("%d", userID)) req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { @@ -76,19 +83,19 @@ func details(w http.ResponseWriter, r *http.Request) { defer resp.Body.Close() - ketoPolicy := model.KetoPolicy{} - err = json.NewDecoder(resp.Body).Decode(&ketoPolicy) + if resp.StatusCode != http.StatusOK { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + policy := model.KavachPolicy{} + err = json.NewDecoder(resp.Body).Decode(&policy) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } - /* User req */ - userMap := author.Mapper(organisationID, userID) - - result := Mapper(ketoPolicy, userMap) - - renderx.JSON(w, http.StatusOK, result) + renderx.JSON(w, http.StatusOK, policy) } diff --git a/server/service/core/action/policy/list.go b/server/service/core/action/policy/list.go index baadeb415..16556c95c 100644 --- a/server/service/core/action/policy/list.go +++ b/server/service/core/action/policy/list.go @@ -4,11 +4,10 @@ import ( "encoding/json" "fmt" "net/http" - "strings" - "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/middlewarex" @@ -18,8 +17,8 @@ import ( ) type paging struct { - Total int `json:"total"` - Nodes []model.Policy `json:"nodes"` + Total int `json:"total"` + Nodes []model.KavachPolicy `json:"nodes"` } // list - Get all policies @@ -55,17 +54,25 @@ func list(w http.ResponseWriter, r *http.Request) { return } - req, err := http.NewRequest("GET", viper.GetString("keto_url")+"/engines/acp/ory/regex/policies", nil) + applicationID, err := util.GetApplicationID(uint(userID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + reqURL := viper.GetString("kavach_url") + fmt.Sprintf("/organisations/%d/applications/%d/spaces/%d/policy", organisationID, applicationID, spaceID) + req, err := http.NewRequest("GET", reqURL, nil) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } + req.Header.Set("X-User", fmt.Sprintf("%d", userID)) req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) - if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.NetworkError())) @@ -74,32 +81,24 @@ func list(w http.ResponseWriter, r *http.Request) { defer resp.Body.Close() - var polices []model.KetoPolicy + if resp.StatusCode != http.StatusOK { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } - err = json.NewDecoder(resp.Body).Decode(&polices) + var polices []model.KavachPolicy + err = json.NewDecoder(resp.Body).Decode(&polices) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } - prefixName := fmt.Sprint("id:org:", organisationID, ":app:dega:space:", spaceID, ":") - var onlyOrgPolicy []model.KetoPolicy - - for _, each := range polices { - if strings.HasPrefix(each.ID, prefixName) { - onlyOrgPolicy = append(onlyOrgPolicy, each) - } - } - - for i, j := 0, len(onlyOrgPolicy)-1; i < j; i, j = i+1, j-1 { - onlyOrgPolicy[i], onlyOrgPolicy[j] = onlyOrgPolicy[j], onlyOrgPolicy[i] - } - offset, limit := paginationx.Parse(r.URL.Query()) - total := len(onlyOrgPolicy) + total := len(polices) lowerLimit := offset upperLimit := offset + limit if offset > total { @@ -110,19 +109,9 @@ func list(w http.ResponseWriter, r *http.Request) { upperLimit = total } - onlyOrgPolicy = onlyOrgPolicy[lowerLimit:upperLimit] - - /* User req */ - userMap := author.Mapper(organisationID, userID) - - pagePolicies := make([]model.Policy, 0) - - for _, each := range onlyOrgPolicy { - pagePolicies = append(pagePolicies, Mapper(each, userMap)) - } - + polices = polices[lowerLimit:upperLimit] var result paging - result.Nodes = pagePolicies + result.Nodes = polices result.Total = total renderx.JSON(w, http.StatusOK, result) } diff --git a/server/service/core/action/policy/mapper.go b/server/service/core/action/policy/mapper.go index d440efc22..40c50cc1f 100644 --- a/server/service/core/action/policy/mapper.go +++ b/server/service/core/action/policy/mapper.go @@ -1,11 +1,9 @@ package policy import ( - "encoding/json" "strings" "github.com/factly/dega-server/service/core/model" - "github.com/factly/dega-server/util" ) // Mapper map policy @@ -74,16 +72,6 @@ func GetPermissions(ketoPolicy model.KetoPolicy, userID uint) []model.Permission // GetAllPolicies gives list of all keto policies func GetAllPolicies() ([]model.KetoPolicy, error) { - resp, err := util.KetoGetRequest("/engines/acp/ory/regex/policies") - if err != nil { - return nil, err - } - defer resp.Body.Close() - - var policyList []model.KetoPolicy - err = json.NewDecoder(resp.Body).Decode(&policyList) - if err != nil { - return nil, err - } - return policyList, nil + policyLists := []model.KetoPolicy{} + return policyLists, nil } diff --git a/server/service/core/action/policy/route.go b/server/service/core/action/policy/route.go index c1be8c260..a35c78681 100644 --- a/server/service/core/action/policy/route.go +++ b/server/service/core/action/policy/route.go @@ -13,6 +13,19 @@ type policyReq struct { Users []string `json:"users"` } +type kavachPolicy struct { + Name string `json:"name"` + Description string `json:"description"` + Permissions []model.Permission `json:"permissions"` + Roles []uint `json:"roles"` +} + +type roleReq struct { + Name string `json:"name"` + Slug string `json:"slug"` + Description string `json:"description"` +} + // Router - Group of medium router func Router() chi.Router { r := chi.NewRouter() diff --git a/server/service/core/action/policy/update.go b/server/service/core/action/policy/update.go index 26d662ba5..588f5109b 100644 --- a/server/service/core/action/policy/update.go +++ b/server/service/core/action/policy/update.go @@ -1,12 +1,16 @@ package policy import ( + "bytes" "encoding/json" + "errors" "fmt" "net/http" + "strconv" - "github.com/factly/dega-server/service/core/action/author" + "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/meilisearchx" @@ -51,32 +55,43 @@ func update(w http.ResponseWriter, r *http.Request) { return } - /* create new policy */ - policyReq := policyReq{} + applicationID, err := util.GetApplicationID(uint(userID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + policyID := chi.URLParam(r, "policy_id") + /* create new policy */ + policyReq := kavachPolicy{} err = json.NewDecoder(r.Body).Decode(&policyReq) - if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } - /* delete old policy */ - commanPolicyString := fmt.Sprint(":org:", organisationID, ":app:dega:space:", spaceID, ":") - policyID := chi.URLParam(r, "policy_id") - - policyID = "id" + commanPolicyString + policyID + buf := new(bytes.Buffer) + err = json.NewEncoder(buf).Encode(policyReq) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } - req, err := http.NewRequest("DELETE", viper.GetString("keto_url")+"/engines/acp/ory/regex/policies/"+policyID, nil) + requrl := viper.GetString("kavach_url") + "/organisations/" + fmt.Sprintf("%d", organisationID) + "/applications/" + fmt.Sprintf("%d", applicationID) + "/spaces/" + fmt.Sprintf("%d", spaceID) + "/policy/" + policyID + req, err := http.NewRequest(http.MethodPut, requrl, buf) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } + + req.Header.Set("X-User", strconv.Itoa(userID)) req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { @@ -84,11 +99,21 @@ func update(w http.ResponseWriter, r *http.Request) { errorx.Render(w, errorx.Parser(errorx.NetworkError())) return } - defer resp.Body.Close() - /* User req */ - result := Mapper(Composer(organisationID, spaceID, policyReq), author.Mapper(organisationID, userID)) + if resp.StatusCode != http.StatusOK { + loggerx.Error(errors.New("internal server error on kavach server")) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + result := &model.KavachPolicy{} + err = json.NewDecoder(resp.Body).Decode(result) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } // Update into meili index meiliObj := map[string]interface{}{ diff --git a/server/service/core/action/post/create.go b/server/service/core/action/post/create.go index 7b7314a4b..1dd030c59 100644 --- a/server/service/core/action/post/create.go +++ b/server/service/core/action/post/create.go @@ -6,14 +6,12 @@ import ( "errors" "fmt" "net/http" - "reflect" "time" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/core/model" factCheckModel "github.com/factly/dega-server/service/fact-check/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -135,6 +133,11 @@ func createPost(ctx context.Context, post post, status string) (*postData, error return nil, errorx.Unauthorized() } + orgID, err := util.GetOrganisation(ctx) + if err != nil { + loggerx.Error(err) + return nil, errorx.Unauthorized() + } if viper.GetBool("create_super_organisation") { // Fetch space permissions permission := model.SpacePermission{} @@ -174,12 +177,19 @@ func createPost(ctx context.Context, post post, status string) (*postData, error featuredMediumID = nil } - // Store HTML description - var description string - if len(post.Description.RawMessage) > 0 && !reflect.DeepEqual(post.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(post.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(post.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(post.Description) + if err != nil { + loggerx.Error(err) + return nil, errorx.GetMessage("could not get html description", 422) + } + + jsonDescription, err = util.GetJSONDescription(post.Description) if err != nil { - return nil, errorx.GetMessage("cannot parse post description", http.StatusUnprocessableEntity) + loggerx.Error(err) + return nil, errorx.GetMessage("could not get json description", 422) } } @@ -194,8 +204,8 @@ func createPost(ctx context.Context, post post, status string) (*postData, error IsPage: post.IsPage, Subtitle: post.Subtitle, Excerpt: post.Excerpt, - Description: post.Description, - HTMLDescription: description, + Description: jsonDescription, + HTMLDescription: htmlDescription, IsHighlighted: post.IsHighlighted, IsSticky: post.IsSticky, FeaturedMediumID: featuredMediumID, @@ -235,7 +245,7 @@ func createPost(ctx context.Context, post post, status string) (*postData, error return nil, errorx.DBError() } - tx.Model(&model.Post{}).Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").Preload("Space").Preload("Space.Logo").First(&result.Post) + tx.Model(&model.Post{}).Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").Preload("Space.Logo").First(&result.Post) if result.Format.Slug == "fact-check" { // create post claim @@ -289,6 +299,12 @@ func createPost(ctx context.Context, post post, status string) (*postData, error } } + spaceObjectforDega, err := util.GetSpacefromKavach(uint(uID), uint(orgID), uint(sID)) + if err != nil { + loggerx.Error(err) + return nil, errorx.InternalServerError() + } + ratings := make([]factCheckModel.Rating, 0) config.DB.Model(&factCheckModel.Rating{}).Where(factCheckModel.Rating{ SpaceID: uint(sID), @@ -298,7 +314,7 @@ func createPost(ctx context.Context, post post, status string) (*postData, error Post: result.Post, Authors: result.Authors, Claims: result.Claims, - }, *result.Space, ratings) + }, *spaceObjectforDega, ratings) byteArr, err := json.Marshal(schemas) if err != nil { diff --git a/server/service/core/action/post/details.go b/server/service/core/action/post/details.go index 311f5ed4f..976b30a45 100644 --- a/server/service/core/action/post/details.go +++ b/server/service/core/action/post/details.go @@ -53,7 +53,7 @@ func details(w http.ResponseWriter, r *http.Request) { postClaims := []factCheckModel.PostClaim{} result.ID = uint(id) - err = config.DB.Model(&model.Post{}).Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").Preload("Space").Where(&model.Post{ + err = config.DB.Model(&model.Post{}).Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").Where(&model.Post{ SpaceID: uint(sID), }).Where("is_page = ?", false).First(&result.Post).Error diff --git a/server/service/core/action/post/list.go b/server/service/core/action/post/list.go index db8462cd6..b4633ba9a 100644 --- a/server/service/core/action/post/list.go +++ b/server/service/core/action/post/list.go @@ -70,7 +70,7 @@ func list(w http.ResponseWriter, r *http.Request) { sort = "desc" } - tx := config.DB.Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").Preload("Space").Model(&model.Post{}).Where(&model.Post{ + tx := config.DB.Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").Model(&model.Post{}).Where(&model.Post{ SpaceID: uint(sID), }).Where("is_page = ?", false).Order("posts.created_at " + sort) var statusTemplate bool = false diff --git a/server/service/core/action/post/template.go b/server/service/core/action/post/template.go index 8ba67e209..c66700bac 100644 --- a/server/service/core/action/post/template.go +++ b/server/service/core/action/post/template.go @@ -97,7 +97,7 @@ func createTemplate(w http.ResponseWriter, r *http.Request) { return } - tx.Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").Preload("Space").First(&template.Post) + tx.Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").First(&template.Post) if template.Post.Format.Slug == "fact-check" { diff --git a/server/service/core/action/post/update.go b/server/service/core/action/post/update.go index b2af5ef13..56fd8d16f 100644 --- a/server/service/core/action/post/update.go +++ b/server/service/core/action/post/update.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "net/http" - "reflect" "strconv" "time" @@ -14,7 +13,6 @@ import ( "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/core/model" factCheckModel "github.com/factly/dega-server/service/fact-check/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/dega-server/util/arrays" "github.com/factly/x/errorx" @@ -135,13 +133,20 @@ func update(w http.ResponseWriter, r *http.Request) { postSlug = slugx.Approve(&config.DB, slugx.Make(post.Title), sID, tableName) } - // Store HTML description - var description string - if len(post.Description.RawMessage) > 0 && !reflect.DeepEqual(post.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(post.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(post.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(post.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse post description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(post.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -182,8 +187,8 @@ func update(w http.ResponseWriter, r *http.Request) { "slug": postSlug, "subtitle": post.Subtitle, "excerpt": post.Excerpt, - "description": post.Description, - "html_description": description, + "description": jsonDescription, + "html_description": htmlDescription, "is_highlighted": post.IsHighlighted, "is_sticky": post.IsSticky, "is_featured": post.IsFeatured, @@ -261,7 +266,7 @@ func update(w http.ResponseWriter, r *http.Request) { updateMap["status"] = "draft" } - err = tx.Model(&result.Post).Updates(&updateMap).Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").Preload("Space").Preload("Space.Logo").First(&result.Post).Error + err = tx.Model(&result.Post).Updates(&updateMap).Preload("Medium").Preload("Format").Preload("Tags").Preload("Categories").First(&result.Post).Error if err != nil { tx.Rollback() @@ -388,6 +393,13 @@ func update(w http.ResponseWriter, r *http.Request) { } } + spaceObjectforDega, err := util.GetSpacefromKavach(uint(uID), uint(oID), uint(sID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + ratings := make([]factCheckModel.Rating, 0) config.DB.Model(&factCheckModel.Rating{}).Where(factCheckModel.Rating{ SpaceID: uint(sID), @@ -397,7 +409,7 @@ func update(w http.ResponseWriter, r *http.Request) { Post: result.Post, Authors: result.Authors, Claims: result.Claims, - }, *result.Space, ratings) + }, *spaceObjectforDega, ratings) byteArr, err := json.Marshal(schemas) if err != nil { @@ -480,18 +492,8 @@ func update(w http.ResponseWriter, r *http.Request) { } func getPublishPermissions(oID, sID, uID int) (int, error) { - commonString := fmt.Sprint(":org:", oID, ":app:dega:space:", sID, ":") - - kresource := fmt.Sprint("resources", commonString, "posts") - kaction := fmt.Sprint("actions", commonString, "posts:publish") - - result := util.KetoAllowed{} - - result.Action = kaction - result.Resource = kresource - result.Subject = fmt.Sprint(uID) - resStatus, err := util.IsAllowed(result) + resStatus, err := util.IsAllowed("posts", "publish", uint(oID), uint(sID), uint(uID)) if err != nil { return 0, err } diff --git a/server/service/core/action/role/create.go b/server/service/core/action/role/create.go new file mode 100644 index 000000000..8b20abdd3 --- /dev/null +++ b/server/service/core/action/role/create.go @@ -0,0 +1,101 @@ +package role + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + + "github.com/factly/dega-server/service/core/model" + "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" + "github.com/factly/x/renderx" + "github.com/go-chi/chi" + "github.com/spf13/viper" +) + +func create(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + spaceID := chi.URLParam(r, "space_id") + sID, err := strconv.Atoi(spaceID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + spaceRole := new(model.SpaceRole) + err = json.NewDecoder(r.Body).Decode(spaceRole) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + buf := new(bytes.Buffer) + err = json.NewEncoder(buf).Encode(spaceRole) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + requrl := viper.GetString("kavach_url") + "/organisations/" + fmt.Sprintf("%d", orgID) + "/applications/" + fmt.Sprintf("%d", applicationID) + "/spaces/" + spaceID + "/roles" + req, err := http.NewRequest("POST", requrl, buf) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + req.Header.Set("X-User", strconv.Itoa(uID)) + req.Header.Set("Content-Type", "application/json") + + client := httpx.CustomHttpClient() + resp, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + loggerx.Error(errors.New("internal server error on kavach server")) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + result := &model.SpaceRole{} + err = json.NewDecoder(resp.Body).Decode(result) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + renderx.JSON(w, http.StatusCreated, result) +} diff --git a/server/service/core/action/role/delete.go b/server/service/core/action/role/delete.go new file mode 100644 index 000000000..498088476 --- /dev/null +++ b/server/service/core/action/role/delete.go @@ -0,0 +1,73 @@ +package role + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" + "github.com/factly/x/renderx" + "github.com/go-chi/chi" + "github.com/spf13/viper" +) + +func delete(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + spaceID := chi.URLParam(r, "space_id") + sID, err := strconv.Atoi(spaceID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + roleID := chi.URLParam(r, "role_id") + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + reqURL := viper.GetString("kavach_url") + fmt.Sprintf("/organisations/%d/applications/%d/spaces/%s/roles/%s", orgID, applicationID, spaceID, roleID) + client := httpx.CustomHttpClient() + req, err := http.NewRequest(http.MethodDelete, reqURL, nil) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + req.Header.Set("X-User", fmt.Sprintf("%d", uID)) + req.Header.Set("Content-type", "application/json") + response, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + defer response.Body.Close() + if response.StatusCode != 200 { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + renderx.JSON(w, http.StatusOK, nil) +} diff --git a/server/service/core/action/role/details.go b/server/service/core/action/role/details.go new file mode 100644 index 000000000..b6a4854f5 --- /dev/null +++ b/server/service/core/action/role/details.go @@ -0,0 +1,84 @@ +package role + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + + "github.com/factly/dega-server/service/core/model" + "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" + "github.com/factly/x/renderx" + "github.com/go-chi/chi" + "github.com/spf13/viper" +) + +func details(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + spaceID := chi.URLParam(r, "space_id") + sID, err := strconv.Atoi(spaceID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + roleID := chi.URLParam(r, "role_id") + + reqURL := viper.GetString("kavach_url") + fmt.Sprintf("/organisations/%d/applications/%d/spaces/%s/roles/%s", orgID, applicationID, spaceID, roleID) + client := httpx.CustomHttpClient() + req, err := http.NewRequest(http.MethodGet, reqURL, nil) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + req.Header.Set("X-User", fmt.Sprintf("%d", uID)) + req.Header.Set("Content-type", "application/json") + response, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + defer response.Body.Close() + if response.StatusCode != 200 { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + spaceRole := model.SpaceRole{} + err = json.NewDecoder(response.Body).Decode(&spaceRole) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + renderx.JSON(w, http.StatusOK, spaceRole) +} diff --git a/server/service/core/action/role/list.go b/server/service/core/action/role/list.go new file mode 100644 index 000000000..c61039475 --- /dev/null +++ b/server/service/core/action/role/list.go @@ -0,0 +1,78 @@ +package role + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + + "github.com/factly/x/middlewarex" + "github.com/spf13/viper" + + "github.com/factly/dega-server/service/core/model" + "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + + "github.com/factly/x/renderx" + "github.com/go-chi/chi" +) + +func list(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + spaceID := chi.URLParam(r, "space_id") + sID, err := strconv.Atoi(spaceID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + req, err := http.NewRequest("GET", viper.GetString("kavach_url")+"/organisations/"+fmt.Sprintf("%d", orgID)+"/applications/"+fmt.Sprintf("%d", applicationID)+"/spaces/"+spaceID+"/roles", nil) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + req.Header.Set("X-User", strconv.Itoa(uID)) + req.Header.Set("Content-Type", "application/json") + + client := httpx.CustomHttpClient() + resp, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + defer resp.Body.Close() + result := []model.SpaceRole{} + err = json.NewDecoder(resp.Body).Decode(&result) + if err != nil { + loggerx.Error(err) + return + } + + renderx.JSON(w, http.StatusOK, result) + +} diff --git a/server/service/core/action/role/route.go b/server/service/core/action/role/route.go new file mode 100644 index 000000000..663503eca --- /dev/null +++ b/server/service/core/action/role/route.go @@ -0,0 +1,21 @@ +package role + +import ( + "github.com/factly/dega-server/service/core/action/role/user" + "github.com/go-chi/chi" +) + +func Router() chi.Router { + r := chi.NewRouter() + + r.Get("/", list) + r.Post("/", create) + r.Route("/{role_id}", func(r chi.Router) { + r.Put("/", update) + r.Delete("/", delete) + r.Get("/", details) + r.Mount("/users", user.Router()) + }) + + return r +} diff --git a/server/service/core/action/role/update.go b/server/service/core/action/role/update.go new file mode 100644 index 000000000..8a51b35a9 --- /dev/null +++ b/server/service/core/action/role/update.go @@ -0,0 +1,104 @@ +package role + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + + "github.com/factly/dega-server/service/core/model" + "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" + "github.com/spf13/viper" + + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" + "github.com/factly/x/renderx" + "github.com/go-chi/chi" +) + +func update(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + spaceID := chi.URLParam(r, "space_id") + sID, err := strconv.Atoi(spaceID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + roleID := chi.URLParam(r, "role_id") + spaceRole := new(model.SpaceRole) + err = json.NewDecoder(r.Body).Decode(spaceRole) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + buf := new(bytes.Buffer) + err = json.NewEncoder(buf).Encode(spaceRole) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + requrl := viper.GetString("kavach_url") + "/organisations/" + fmt.Sprintf("%d", orgID) + "/applications/" + fmt.Sprintf("%d", applicationID) + "/spaces/" + spaceID + "/roles/" + roleID + req, err := http.NewRequest(http.MethodPut, requrl, buf) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + req.Header.Set("X-User", strconv.Itoa(uID)) + req.Header.Set("Content-Type", "application/json") + + client := httpx.CustomHttpClient() + resp, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + loggerx.Error(errors.New("internal server error on kavach server")) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + result := &model.SpaceRole{} + err = json.NewDecoder(resp.Body).Decode(result) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + renderx.JSON(w, http.StatusCreated, result) +} diff --git a/server/service/core/action/role/user/create.go b/server/service/core/action/role/user/create.go new file mode 100644 index 000000000..7fdc47df6 --- /dev/null +++ b/server/service/core/action/role/user/create.go @@ -0,0 +1,98 @@ +package user + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + + "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" + "github.com/factly/x/renderx" + "github.com/go-chi/chi" + "github.com/spf13/viper" +) + +type requestModel struct { + UserID int `json:"user_id" validate:"required"` +} + +func create(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + spaceID := chi.URLParam(r, "space_id") + sID, err := strconv.Atoi(spaceID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + roleID := chi.URLParam(r, "role_id") + + // decoding the requestBody + userReqModel := &requestModel{} + err = json.NewDecoder(r.Body).Decode(userReqModel) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + buf := new(bytes.Buffer) + err = json.NewEncoder(buf).Encode(userReqModel) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + reqURL := viper.GetString("kavach_url") + fmt.Sprintf("/organisations/%d/applications/%d/spaces/%s/roles/%s/users", orgID, applicationID, spaceID, roleID) + req, err := http.NewRequest(http.MethodPost, reqURL, buf) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + req.Header.Set("X-User", fmt.Sprintf("%d", uID)) + req.Header.Set("Content-type", "application/json") + client := httpx.CustomHttpClient() + response, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + defer response.Body.Close() + if response.StatusCode != http.StatusOK { + loggerx.Error(errors.New("internal server error on kavach server")) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + renderx.JSON(w, http.StatusCreated, nil) +} diff --git a/server/service/core/action/role/user/delete.go b/server/service/core/action/role/user/delete.go new file mode 100644 index 000000000..750b43c2f --- /dev/null +++ b/server/service/core/action/role/user/delete.go @@ -0,0 +1,77 @@ +package user + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" + "github.com/factly/x/renderx" + "github.com/go-chi/chi" + "github.com/spf13/viper" +) + +func delete(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + spaceID := chi.URLParam(r, "space_id") + sID, err := strconv.Atoi(spaceID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + roleID := chi.URLParam(r, "role_id") + userID := chi.URLParam(r, "user_id") + + reqURL := viper.GetString("kavach_url") + fmt.Sprintf("/organisations/%d/applications/%d/spaces/%s/roles/%s/users/%s", orgID, applicationID, spaceID, roleID, userID) + client := httpx.CustomHttpClient() + req, err := http.NewRequest(http.MethodDelete, reqURL, nil) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + req.Header.Set("X-User", fmt.Sprintf("%d", uID)) + req.Header.Set("Content-type", "application/json") + response, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + defer response.Body.Close() + if response.StatusCode != 200 { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + renderx.JSON(w, http.StatusOK, nil) + +} diff --git a/server/service/core/action/role/user/list.go b/server/service/core/action/role/user/list.go new file mode 100644 index 000000000..be3b86fb3 --- /dev/null +++ b/server/service/core/action/role/user/list.go @@ -0,0 +1,93 @@ +package user + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + + "github.com/factly/dega-server/service/core/model" + "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" + "github.com/factly/x/renderx" + "github.com/go-chi/chi" + "github.com/spf13/viper" +) + +func list(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + sID := chi.URLParam(r, "space_id") + spaceID, err := strconv.Atoi(sID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InvalidID())) + return + } + + orgID, err := util.GetOrganisationIDfromSpaceID(uint(spaceID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + rID := chi.URLParam(r, "role_id") + roleID, err := strconv.Atoi(rID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InvalidID())) + return + } + + req, err := http.NewRequest("GET", viper.GetString("kavach_url")+fmt.Sprintf("/organisations/%d/applications/", orgID)+fmt.Sprintf("%d", applicationID)+"/spaces/"+fmt.Sprintf("%d", spaceID)+fmt.Sprintf("/roles/%d", roleID)+"/users", nil) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + req.Header.Set("X-User", fmt.Sprintf("%d", uID)) + req.Header.Set("Content-Type", "application/json") + + client := httpx.CustomHttpClient() + resp, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + if resp.StatusCode != 200 { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + defer resp.Body.Close() + + var users []model.User + + err = json.NewDecoder(resp.Body).Decode(&users) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + renderx.JSON(w, http.StatusOK, users) +} diff --git a/server/service/core/action/role/user/route.go b/server/service/core/action/role/user/route.go new file mode 100644 index 000000000..b83faf869 --- /dev/null +++ b/server/service/core/action/role/user/route.go @@ -0,0 +1,14 @@ +package user + +import ( + "github.com/go-chi/chi" +) + +func Router() chi.Router { + r := chi.NewRouter() + + r.Get("/", list) + r.Post("/", create) + r.Delete("/{user_id}", delete) + return r +} diff --git a/server/service/core/action/space/create.go b/server/service/core/action/space/create.go index 27e109ee1..5fe2443c4 100644 --- a/server/service/core/action/space/create.go +++ b/server/service/core/action/space/create.go @@ -1,9 +1,12 @@ package space import ( + "bytes" "context" "encoding/json" "errors" + "fmt" + "net/http" "strconv" @@ -12,13 +15,14 @@ import ( "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/meilisearchx" "github.com/factly/x/middlewarex" "github.com/factly/x/renderx" - "github.com/factly/x/slugx" "github.com/factly/x/validationx" + //"github.com/factly/x/slugx" ) // create - Create space @@ -40,10 +44,15 @@ func create(w http.ResponseWriter, r *http.Request) { return } - space := &space{} + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + space := space{} err = json.NewDecoder(r.Body).Decode(&space) - if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DecodeError())) @@ -59,25 +68,39 @@ func create(w http.ResponseWriter, r *http.Request) { } if space.OrganisationID == 0 { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) return } - err = util.CheckSpaceKetoPermission("create", uint(space.OrganisationID), uint(uID)) + buf := new(bytes.Buffer) + + requestBody := map[string]interface{}{ + "name": space.Name, + "description": space.Description, + "slug": space.Slug, + "metadata": map[string]interface{}{ + "meta_fields": space.MetaFields, + "site_address": space.SiteAddress, + "tag_line": space.TagLine, + "site_title": space.SiteTitle, + }, + } + err = json.NewEncoder(buf).Encode(&requestBody) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage(err.Error(), http.StatusUnauthorized))) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } var superOrgID int - if viper.GetBool("create_super_organisation") { - superOrgID, err = middlewarex.GetSuperOrganisationID("dega") - if err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.InternalServerError())) - return - } - + superOrgID, err = middlewarex.GetSuperOrganisationID("Kavach") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + if viper.GetBool("create_super_organisation") && viper.GetBool("organisation_permission_enabled") { // Fetch organisation permissions permission := model.OrganisationPermission{} err = config.DB.Model(&model.OrganisationPermission{}).Where(&model.OrganisationPermission{ @@ -90,64 +113,84 @@ func create(w http.ResponseWriter, r *http.Request) { return } - if err == nil { - // Fetch total number of spaces in organisation - var totSpaces int64 - config.DB.Model(&model.Space{}).Where(&model.Space{ - OrganisationID: space.OrganisationID, - }).Count(&totSpaces) - - if totSpaces >= permission.Spaces && permission.Spaces > 0 { - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot create more spaces", http.StatusUnprocessableEntity))) - return - } - } + // if err == nil { + // // Fetch total number of spaces in organisation + // // var totSpaces int64 + // // err = config.DB.Model(&model.Space{}).Where(&model.Space{ + // // OrganisationID: space.OrganisationID, + // // }).Count(&totSpaces).Error + // // if err != nil { + // // loggerx.Error(err) + // // errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + // // return + // // } + // // if totSpaces >= permission.Spaces && permission.Spaces > 0 { + // // errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot create more spaces", http.StatusUnprocessableEntity))) + // // return + // // } + // } } - var spaceSlug string - if space.Slug != "" && slugx.Check(space.Slug) { - spaceSlug = space.Slug - } else { - spaceSlug = slugx.Make(space.Name) + req, err := http.NewRequest("POST", viper.GetString("kavach_url")+"/organisations/"+fmt.Sprintf("%d", space.OrganisationID)+"/applications/"+fmt.Sprintf("%d", applicationID)+"/spaces", buf) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return } - result := model.Space{ - Base: config.Base{ - CreatedAt: space.CreatedAt, - UpdatedAt: space.UpdatedAt, - }, - Name: space.Name, - SiteTitle: space.SiteTitle, - Slug: approveSpaceSlug(spaceSlug), - Description: space.Description, - TagLine: space.TagLine, - SiteAddress: space.SiteAddress, - Analytics: space.Analytics, - VerificationCodes: space.VerificationCodes, - SocialMediaURLs: space.SocialMediaURLs, - OrganisationID: space.OrganisationID, - ContactInfo: space.ContactInfo, - HeaderCode: space.HeaderCode, - FooterCode: space.FooterCode, - MetaFields: space.MetaFields, + req.Header.Set("X-User", strconv.Itoa(uID)) + req.Header.Set("Content-Type", "application/json") + + client := httpx.CustomHttpClient() + resp, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return } - tx := config.DB.WithContext(context.WithValue(r.Context(), userContext, uID)).Begin() - err = tx.Create(&result).Error + defer resp.Body.Close() + if resp.StatusCode != 201 { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + spaceObjectfromKavach := &model.KavachSpace{} + err = json.NewDecoder(resp.Body).Decode(spaceObjectfromKavach) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + spaceObjectforDega := model.Space{} + spaceObjectforDega.ID = spaceObjectfromKavach.ID + spaceObjectforDega.CreatedAt = spaceObjectfromKavach.CreatedAt + spaceObjectforDega.UpdatedAt = spaceObjectfromKavach.UpdatedAt + spaceObjectforDega.DeletedAt = spaceObjectfromKavach.DeletedAt + spaceObjectforDega.CreatedByID = spaceObjectfromKavach.CreatedByID + spaceObjectforDega.UpdatedByID = spaceObjectfromKavach.UpdatedByID + spaceObjectforDega.Name = spaceObjectfromKavach.Name + spaceObjectforDega.Slug = spaceObjectfromKavach.Slug + spaceObjectforDega.Description = spaceObjectfromKavach.Description + spaceObjectforDega.ApplicationID = spaceObjectfromKavach.ApplicationID + spaceObjectforDega.OrganisationID = int(spaceObjectfromKavach.OrganisationID) + err = json.Unmarshal(spaceObjectfromKavach.Metadata.RawMessage, &spaceObjectforDega) if err != nil { - tx.Rollback() loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DBError())) return } + tx := config.DB.WithContext(context.WithValue(r.Context(), userContext, uID)).Begin() + if viper.GetBool("create_super_organisation") { - // Create SpacePermission for super organisation + //Create SpacePermission for super organisation var spacePermission model.SpacePermission if superOrgID == space.OrganisationID { spacePermission = model.SpacePermission{ - SpaceID: result.ID, + SpaceID: spaceObjectforDega.ID, Media: -1, Posts: -1, Podcast: true, @@ -157,7 +200,7 @@ func create(w http.ResponseWriter, r *http.Request) { } } else { spacePermission = model.SpacePermission{ - SpaceID: result.ID, + SpaceID: spaceObjectforDega.ID, Media: viper.GetInt64("default_number_of_media"), Posts: viper.GetInt64("default_number_of_posts"), Episodes: viper.GetInt64("default_number_of_episodes"), @@ -166,72 +209,46 @@ func create(w http.ResponseWriter, r *http.Request) { FactCheck: false, } } - var spacePermContext config.ContextKey = "space_perm_user" - if err = tx.WithContext(context.WithValue(r.Context(), spacePermContext, uID)).Create(&spacePermission).Error; err != nil { + if err = tx.Model(&model.SpacePermission{}).Create(&spacePermission).Error; err != nil { tx.Rollback() loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DBError())) return } - } // Insert into meili index meiliObj := map[string]interface{}{ - "id": result.ID, + "id": spaceObjectforDega.ID, "kind": "space", - "name": result.Name, - "slug": result.Slug, - "description": result.Description, - "site_title": result.SiteTitle, - "site_address": result.SiteAddress, - "tag_line": result.TagLine, - "organisation_id": result.OrganisationID, - "analytics": result.Analytics, + "name": spaceObjectforDega.Name, + "slug": spaceObjectforDega.Slug, + "description": spaceObjectforDega.Description, + "site_title": spaceObjectforDega.SiteTitle, + "site_address": spaceObjectforDega.SiteAddress, + "tag_line": spaceObjectforDega.TagLine, + "organisation_id": spaceObjectforDega.OrganisationID, + "analytics": spaceObjectforDega.Analytics, } if config.SearchEnabled() { - _ = meilisearchx.AddDocument("dega", meiliObj) + err = meilisearchx.AddDocument("dega", meiliObj) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } } tx.Commit() if util.CheckNats() { - if err = util.NC.Publish("space.created", result); err != nil { + if err = util.NC.Publish("space.created", spaceObjectforDega); err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } } - renderx.JSON(w, http.StatusCreated, result) -} - -func approveSpaceSlug(slug string) string { - spaceList := make([]model.Space, 0) - config.DB.Model(&model.Space{}).Where("slug LIKE ? AND deleted_at IS NULL", slug+"%").Find(&spaceList) - - count := 0 - for { - flag := true - for _, each := range spaceList { - temp := slug - if count != 0 { - temp = temp + "-" + strconv.Itoa(count) - } - if each.Slug == temp { - flag = false - break - } - } - if flag { - break - } - count++ - } - temp := slug - if count != 0 { - temp = temp + "-" + strconv.Itoa(count) - } - return temp + renderx.JSON(w, http.StatusCreated, spaceObjectforDega) } diff --git a/server/service/core/action/space/delete.go b/server/service/core/action/space/delete.go index 6ce76f2ef..276fdc188 100644 --- a/server/service/core/action/space/delete.go +++ b/server/service/core/action/space/delete.go @@ -1,18 +1,23 @@ package space import ( + // "encoding/json" + + "fmt" "net/http" "strconv" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/meilisearchx" "github.com/factly/x/middlewarex" "github.com/factly/x/renderx" "github.com/go-chi/chi" + "github.com/spf13/viper" ) // delete - Delete space @@ -22,6 +27,7 @@ import ( // @ID delete-space // @Consume json // @Produce json +// @Param org_id header string true "Organisation ID" // @Param X-User header string true "User ID" // @Param space_id path string true "Space ID" // @Success 200 @@ -42,44 +48,73 @@ func delete(w http.ResponseWriter, r *http.Request) { return } - result := &model.Space{} - result.ID = uint(sID) - - // check record exists or not - err = config.DB.First(&result).Error + orgID, err := util.GetOrganisationIDfromSpaceID(uint(sID), uint(uID)) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.RecordNotFound())) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - if result.OrganisationID == 0 { + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) return } - err = util.CheckSpaceKetoPermission("delete", uint(result.OrganisationID), uint(uID)) + spaceObjectforDega, err := util.GetSpacefromKavach(uint(uID), uint(orgID), uint(sID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + // getting the details for the space to be deleted + client := httpx.CustomHttpClient() + req, err := http.NewRequest("DELETE", viper.GetString("kavach_url")+fmt.Sprintf("/organisations/%d/applications/", orgID)+fmt.Sprintf("%d", applicationID)+"/spaces/"+spaceID, nil) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage(err.Error(), http.StatusUnauthorized))) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - tx := config.DB.Begin() - tx.Model(&model.Space{}).Delete(&result) + req.Header.Set("X-User", strconv.Itoa(uID)) + req.Header.Set("Content-Type", "application/json") + resp, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + if resp.StatusCode != 200 { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + defer resp.Body.Close() if config.SearchEnabled() { - _ = meilisearchx.DeleteDocument("dega", result.ID, "space") + err = meilisearchx.DeleteDocument("dega", uint(sID), "space") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + } } - tx.Commit() - if util.CheckNats() { - if err = util.NC.Publish("space.deleted", result); err != nil { + if err = util.NC.Publish("space.deleted", spaceObjectforDega); err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } } + err = config.DB.Model(&model.SpacePermission{}).Where(&model.SpacePermission{ + SpaceID: uint(sID), + }).Delete(&model.SpacePermission{}).Error + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DBError())) + return + } renderx.JSON(w, http.StatusOK, nil) } diff --git a/server/service/core/action/space/details.go b/server/service/core/action/space/details.go index d4ef53161..f8bbb52bf 100644 --- a/server/service/core/action/space/details.go +++ b/server/service/core/action/space/details.go @@ -4,8 +4,11 @@ import ( "net/http" "strconv" - "github.com/factly/dega-server/config" - "github.com/factly/dega-server/service/core/model" + "github.com/factly/x/middlewarex" + + // "github.com/factly/dega-server/config" + + "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/renderx" @@ -24,27 +27,33 @@ import ( // @Success 200 {object} model.Space // @Router /core/spaces/{space_id} [get] func details(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } - spaceID := chi.URLParam(r, "space_id") - id, err := strconv.Atoi(spaceID) - + sID := chi.URLParam(r, "space_id") + spaceID, err := strconv.Atoi(sID) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InvalidID())) return } - result := &model.Space{} - - result.ID = uint(id) - - err = config.DB.Model(&model.Space{}).Preload("Logo").Preload("LogoMobile").Preload("FavIcon").Preload("MobileIcon").First(&result).Error - + orgID, err := util.GetOrganisationIDfromSpaceID(uint(spaceID), uint(uID)) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.RecordNotFound())) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - renderx.JSON(w, http.StatusOK, result) + spaceObjectforDega, err := util.GetSpacefromKavach(uint(uID), uint(orgID), uint(spaceID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + renderx.JSON(w, http.StatusOK, spaceObjectforDega) } diff --git a/server/service/core/action/space/my.go b/server/service/core/action/space/my.go index 0014471e4..295eb904b 100644 --- a/server/service/core/action/space/my.go +++ b/server/service/core/action/space/my.go @@ -2,16 +2,17 @@ package space import ( "encoding/json" + "fmt" "io/ioutil" "net/http" "strconv" - "github.com/factly/dega-server/service/core/action/user" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/spf13/viper" "github.com/factly/dega-server/config" - "github.com/factly/dega-server/service/core/action/policy" + //"github.com/factly/dega-server/service/core/action/policy" "github.com/factly/dega-server/service/core/model" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -24,10 +25,14 @@ type organisationUser struct { Role string `gorm:"column:role" json:"role"` } -type orgWithSpace struct { +type organisation struct { config.Base - Title string `gorm:"column:title" json:"title"` - Slug string `gorm:"column:slug;unique_index" json:"slug"` + Title string `json:"title"` + Slug string `json:"slug"` +} + +type orgWithSpace struct { + Organisation organisation `json:"organisation"` Permission organisationUser `json:"permission"` Applications []application `json:"applications"` Spaces []spaceWithPermissions `json:"spaces"` @@ -66,8 +71,15 @@ func my(w http.ResponseWriter, r *http.Request) { return } + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + // Fetched all organisations of the user - req, err := http.NewRequest("GET", viper.GetString("kavach_url")+"/organisations/my", nil) + req, err := http.NewRequest(http.MethodGet, viper.GetString("kavach_url")+"/organisations/my", nil) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) @@ -76,7 +88,7 @@ func my(w http.ResponseWriter, r *http.Request) { req.Header.Set("X-User", strconv.Itoa(uID)) req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { @@ -86,75 +98,94 @@ func my(w http.ResponseWriter, r *http.Request) { } defer resp.Body.Close() - body, _ := ioutil.ReadAll(resp.Body) - - allOrg := []orgWithSpace{} - err = json.Unmarshal(body, &allOrg) - + body, err := ioutil.ReadAll(resp.Body) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } + allOrg := []orgWithSpace{} - var allOrgIDs []int - - for _, each := range allOrg { - allOrgIDs = append(allOrgIDs, int(each.ID)) - } - - // Fetched all the spaces related to all the organisations - var allSpaces = make([]model.Space, 0) - - config.DB.Model(model.Space{}).Where("organisation_id IN (?)", allOrgIDs).Preload("Logo").Preload("LogoMobile").Preload("FavIcon").Preload("MobileIcon").Find(&allSpaces) - - // fetch all the keto policies - policyList, err := policy.GetAllPolicies() + err = json.Unmarshal(body, &allOrg) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } - adminPerm := model.Permission{ - Resource: "admin", - Actions: []string{"admin"}, - } - - result := make([]orgWithSpace, 0) + for index, organisation := range allOrg { + req, err := http.NewRequest("GET", viper.GetString("kavach_url")+"/organisations/"+strconv.Itoa(int(organisation.Organisation.ID))+"/applications/"+fmt.Sprintf("%d", applicationID)+"/spaces/", nil) + req.Header.Set("X-User", strconv.Itoa(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + client := httpx.CustomHttpClient() + resp, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + defer resp.Body.Close() - for _, each := range allOrg { - spaceWithPermArr := []spaceWithPermissions{} - for _, space := range allSpaces { - if space.OrganisationID == int(each.ID) { - var services []string - services, err = util.GetAllowedServices(space.ID) + if resp.StatusCode != 200 { + continue + } + organisationSpacesfromKavach := make([]model.KavachSpace, 0) + err = json.NewDecoder(resp.Body).Decode(&organisationSpacesfromKavach) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + organisationSpacesforDega := make([]spaceWithPermissions, 0) + for _, eachSpace := range organisationSpacesfromKavach { + spaceWithPerm := spaceWithPermissions{} + spaceWithPerm.ID = eachSpace.ID + spaceWithPerm.CreatedAt = eachSpace.CreatedAt + spaceWithPerm.UpdatedAt = eachSpace.UpdatedAt + spaceWithPerm.DeletedAt = eachSpace.DeletedAt + spaceWithPerm.UpdatedByID = eachSpace.UpdatedByID + spaceWithPerm.CreatedByID = eachSpace.CreatedByID + spaceWithPerm.Name = eachSpace.Name + spaceWithPerm.Slug = eachSpace.Slug + spaceWithPerm.Description = eachSpace.Description + spaceWithPerm.ApplicationID = eachSpace.ApplicationID + spaceWithPerm.OrganisationID = int(eachSpace.OrganisationID) + err = json.Unmarshal(eachSpace.Metadata.RawMessage, &spaceWithPerm) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DBError())) + return + } + if organisation.Permission.Role == "owner" { + adminPerm := model.Permission{ + Resource: "admin", + Actions: []string{"admin"}, + } + spaceWithPerm.Permissions = append(spaceWithPerm.Permissions, adminPerm) + } else { + spaceWithPerm.Permissions, err = util.GetPermissions(organisation.Organisation.ID, eachSpace.ApplicationID, eachSpace.ID, uint(uID)) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.DBError())) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - if each.Permission.Role != "owner" { - permissions := user.GetPermissions(int(each.ID), int(space.ID), uID, policyList) - spaceWithPerm := spaceWithPermissions{ - Space: space, - Permissions: permissions, - AllowedServices: services, - } - spaceWithPermArr = append(spaceWithPermArr, spaceWithPerm) - } else { - adminSpaceWithPerm := spaceWithPermissions{ - Space: space, - Permissions: []model.Permission{adminPerm}, - AllowedServices: services, - } - spaceWithPermArr = append(spaceWithPermArr, adminSpaceWithPerm) - } } + + spaceWithPerm.AllowedServices, err = util.GetAllowedServices(eachSpace.ID, organisation.Organisation.ID, uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DBError())) + return + } + organisationSpacesforDega = append(organisationSpacesforDega, spaceWithPerm) } - each.Spaces = spaceWithPermArr - result = append(result, each) + organisation.Spaces = organisationSpacesforDega + allOrg[index] = organisation } - renderx.JSON(w, http.StatusOK, result) + renderx.JSON(w, http.StatusOK, allOrg) } diff --git a/server/service/core/action/space/route.go b/server/service/core/action/space/route.go index cedff6a2e..e084e3d27 100644 --- a/server/service/core/action/space/route.go +++ b/server/service/core/action/space/route.go @@ -4,6 +4,7 @@ import ( "time" "github.com/factly/dega-server/config" + "github.com/factly/dega-server/service/core/action/role" "github.com/go-chi/chi" "github.com/jinzhu/gorm/dialects/postgres" ) @@ -43,7 +44,9 @@ func Router() chi.Router { r.Route("/{space_id}", func(r chi.Router) { r.Get("/", details) r.Put("/", update) + r.Mount("/roles", role.Router()) r.Delete("/", delete) + r.Get("/users", users) }) return r diff --git a/server/service/core/action/space/update.go b/server/service/core/action/space/update.go index 9d1c38262..f84244f94 100644 --- a/server/service/core/action/space/update.go +++ b/server/service/core/action/space/update.go @@ -1,24 +1,26 @@ package space import ( - "context" + //"context" + "bytes" "encoding/json" "errors" + "fmt" "net/http" "strconv" - "time" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/meilisearchx" "github.com/factly/x/middlewarex" "github.com/factly/x/renderx" - "github.com/factly/x/slugx" "github.com/factly/x/validationx" "github.com/go-chi/chi" + "github.com/spf13/viper" ) // update - Update space @@ -41,8 +43,8 @@ func update(w http.ResponseWriter, r *http.Request) { return } - spaceID := chi.URLParam(r, "space_id") - id, err := strconv.Atoi(spaceID) + sID := chi.URLParam(r, "space_id") + spaceID, err := strconv.Atoi(sID) if err != nil { loggerx.Error(err) @@ -50,120 +52,104 @@ func update(w http.ResponseWriter, r *http.Request) { return } - space := &space{} + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + space := &model.Space{} err = json.NewDecoder(r.Body).Decode(&space) - if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } + space.ID = uint(spaceID) validationError := validationx.Check(space) - if validationError != nil { loggerx.Error(errors.New("validation error")) errorx.Render(w, validationError) return } - if space.OrganisationID == 0 { - return - } - - err = util.CheckSpaceKetoPermission("update", uint(space.OrganisationID), uint(uID)) + requestBody := map[string]interface{}{ + "name": space.Name, + "slug": space.Slug, + "description": space.Description, + "metadata": map[string]interface{}{ + "meta_fields": space.MetaFields, + "site_address": space.SiteAddress, + "tag_line": space.TagLine, + "site_title": space.SiteTitle, + "logo_id": space.LogoID, + "logo_mobile_id": space.LogoMobileID, + "fav_icon_id": space.FavIconID, + "mobile_icon_id": space.MobileIconID, + "verification_codes": space.VerificationCodes, + "social_media_urls": space.SocialMediaURLs, + "contact_info": space.ContactInfo, + "analytics": space.Analytics, + "header_code": space.HeaderCode, + "footer_code": space.FooterCode, + }, + } + buf := new(bytes.Buffer) + err = json.NewEncoder(buf).Encode(&requestBody) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage(err.Error(), http.StatusUnauthorized))) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - result := spaceWithPermissions{} - adminPerm := model.Permission{ - Resource: "admin", - Actions: []string{"admin"}, - } - result.Permissions = []model.Permission{adminPerm} - var services []string - services, err = util.GetAllowedServices(uint(id)) + client := httpx.CustomHttpClient() + req, err := http.NewRequest("PUT", viper.GetString("kavach_url")+"/organisations/"+fmt.Sprintf("%d", space.OrganisationID)+"/applications/"+fmt.Sprintf("%d", applicationID)+"/spaces/"+fmt.Sprintf("%d", spaceID), buf) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.DBError())) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - result.AllowedServices = services - result.ID = uint(id) - - // check record exists or not - err = config.DB.First(&result.Space).Error + req.Header.Set("X-User", strconv.Itoa(uID)) + req.Header.Set("Content-Type", "application/json") + resp, err := client.Do(req) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.RecordNotFound())) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - tx := config.DB.WithContext(context.WithValue(r.Context(), userContext, uID)).Begin() - var spaceSlug string - if result.Slug == space.Slug { - spaceSlug = result.Slug - } else if space.Slug != "" && slugx.Check(space.Slug) { - spaceSlug = approveSpaceSlug(space.Slug) - } else { - spaceSlug = approveSpaceSlug(slugx.Make(space.Name)) - } - updateMap := map[string]interface{}{ - "created_at": space.CreatedAt, - "updated_at": space.UpdatedAt, - "name": space.Name, - "slug": spaceSlug, - "site_title": space.SiteTitle, - "tag_line": space.TagLine, - "site_address": space.SiteAddress, - "description": space.Description, - "logo_id": space.LogoID, - "logo_mobile_id": space.LogoMobileID, - "fav_icon_id": space.FavIconID, - "mobile_icon_id": space.MobileIconID, - "header_code": space.HeaderCode, - "footer_code": space.FooterCode, - "meta_fields": space.MetaFields, - "verification_codes": space.VerificationCodes, - "social_media_urls": space.SocialMediaURLs, - "contact_info": space.ContactInfo, - "analytics": space.Analytics, - } - - // check if the id for all the mediums in space is 0 or not if it is zero then make it null - if space.LogoID == 0 { - updateMap["logo_id"] = nil - } - - if space.LogoMobileID == 0 { - updateMap["logo_mobile_id"] = nil - } + defer resp.Body.Close() - if space.FavIconID == 0 { - updateMap["fav_icon_id"] = nil - } - - if space.MobileIconID == 0 { - updateMap["mobile_icon_id"] = nil - } - - if space.CreatedAt.IsZero() { - updateMap["created_at"] = result.CreatedAt + if resp.StatusCode != 200 { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return } - - if space.UpdatedAt.IsZero() { - updateMap["updated_at"] = time.Now() + spaceObjectfromKavach := &model.KavachSpace{} + err = json.NewDecoder(resp.Body).Decode(spaceObjectfromKavach) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return } - err = tx.Model(&result.Space).Updates(&updateMap).Preload("Logo").Preload("LogoMobile").Preload("FavIcon").Preload("MobileIcon").First(&result.Space).Error - + spaceObjectforDega := model.Space{} + spaceObjectforDega.ID = spaceObjectfromKavach.ID + spaceObjectforDega.CreatedAt = spaceObjectfromKavach.CreatedAt + spaceObjectforDega.UpdatedAt = spaceObjectfromKavach.UpdatedAt + spaceObjectforDega.DeletedAt = spaceObjectfromKavach.DeletedAt + spaceObjectforDega.CreatedByID = spaceObjectfromKavach.CreatedByID + spaceObjectforDega.UpdatedByID = spaceObjectfromKavach.UpdatedByID + spaceObjectforDega.Name = spaceObjectfromKavach.Name + spaceObjectforDega.Slug = spaceObjectfromKavach.Slug + spaceObjectforDega.Description = spaceObjectfromKavach.Description + spaceObjectforDega.ApplicationID = spaceObjectfromKavach.ApplicationID + spaceObjectforDega.OrganisationID = int(spaceObjectfromKavach.OrganisationID) + err = json.Unmarshal(spaceObjectfromKavach.Metadata.RawMessage, &spaceObjectforDega) if err != nil { - tx.Rollback() loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DBError())) return @@ -171,31 +157,36 @@ func update(w http.ResponseWriter, r *http.Request) { // Update into meili index meiliObj := map[string]interface{}{ - "id": result.ID, + "id": sID, "kind": "space", - "name": result.Name, - "slug": result.Slug, - "description": result.Description, - "site_title": result.SiteTitle, - "site_address": result.SiteAddress, - "tag_line": result.TagLine, - "organisation_id": result.OrganisationID, - "analytics": result.Analytics, + "name": spaceObjectforDega.Name, + "slug": spaceObjectforDega.Slug, + "description": spaceObjectforDega.Description, + "site_title": spaceObjectforDega.SiteTitle, + "site_address": spaceObjectforDega.SiteAddress, + "tag_line": spaceObjectforDega.TagLine, + "organisation_id": spaceObjectforDega.OrganisationID, + "analytics": spaceObjectforDega.Analytics, } if config.SearchEnabled() { - _ = meilisearchx.UpdateDocument("dega", meiliObj) + err = meilisearchx.UpdateDocument("dega", meiliObj) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } } - tx.Commit() + //tx.Commit() if util.CheckNats() { - if err = util.NC.Publish("space.updated", result); err != nil { + if err = util.NC.Publish("space.updated", spaceObjectforDega); err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } } - renderx.JSON(w, http.StatusOK, result) + renderx.JSON(w, http.StatusOK, spaceObjectforDega) } diff --git a/server/service/core/action/space/user.go b/server/service/core/action/space/user.go new file mode 100644 index 000000000..e69491c1b --- /dev/null +++ b/server/service/core/action/space/user.go @@ -0,0 +1,84 @@ +package space + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + + "github.com/factly/dega-server/service/core/model" + "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" + "github.com/factly/x/renderx" + "github.com/go-chi/chi" + "github.com/spf13/viper" +) + +func users(w http.ResponseWriter, r *http.Request) { + uID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + applicationID, err := util.GetApplicationID(uint(uID), "dega") + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + + sID := chi.URLParam(r, "space_id") + spaceID, err := strconv.Atoi(sID) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InvalidID())) + return + } + + orgID, err := util.GetOrganisationIDfromSpaceID(uint(spaceID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + req, err := http.NewRequest("GET", viper.GetString("kavach_url")+fmt.Sprintf("/organisations/%d/applications/", orgID)+fmt.Sprintf("%d", applicationID)+"/spaces/"+fmt.Sprintf("%d", spaceID)+"/users", nil) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + req.Header.Set("X-User", fmt.Sprintf("%d", uID)) + req.Header.Set("Content-Type", "application/json") + client := httpx.CustomHttpClient() + resp, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + if resp.StatusCode != 200 { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + defer resp.Body.Close() + + var users []model.User + + err = json.NewDecoder(resp.Body).Decode(&users) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + renderx.JSON(w, http.StatusOK, users) +} diff --git a/server/service/core/action/tag/create.go b/server/service/core/action/tag/create.go index 169607fc2..54f13e76b 100644 --- a/server/service/core/action/tag/create.go +++ b/server/service/core/action/tag/create.go @@ -5,11 +5,9 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -18,6 +16,7 @@ import ( "github.com/factly/x/renderx" "github.com/factly/x/slugx" "github.com/factly/x/validationx" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -87,13 +86,20 @@ func create(w http.ResponseWriter, r *http.Request) { return } - // Store HTML description - var description string - if len(tag.Description.RawMessage) > 0 && !reflect.DeepEqual(tag.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(tag.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(tag.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(tag.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse tag description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(tag.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -111,8 +117,8 @@ func create(w http.ResponseWriter, r *http.Request) { Name: tag.Name, Slug: slugx.Approve(&config.DB, tagSlug, sID, tableName), BackgroundColour: tag.BackgroundColour, - Description: tag.Description, - HTMLDescription: description, + Description: jsonDescription, + HTMLDescription: htmlDescription, SpaceID: uint(sID), MediumID: mediumID, IsFeatured: tag.IsFeatured, diff --git a/server/service/core/action/tag/update.go b/server/service/core/action/tag/update.go index 87ce5170f..599b0cfda 100644 --- a/server/service/core/action/tag/update.go +++ b/server/service/core/action/tag/update.go @@ -4,13 +4,11 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "strconv" "time" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -20,6 +18,7 @@ import ( "github.com/factly/x/slugx" "github.com/factly/x/validationx" "github.com/go-chi/chi" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -114,12 +113,20 @@ func update(w http.ResponseWriter, r *http.Request) { } // Store HTML description - var description string - if len(tag.Description.RawMessage) > 0 && !reflect.DeepEqual(tag.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(tag.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(tag.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(tag.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse tag description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(tag.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -132,8 +139,8 @@ func update(w http.ResponseWriter, r *http.Request) { "updated_by_id": uint(uID), "name": tag.Name, "slug": tagSlug, - "description": tag.Description, - "html_description": description, + "description": jsonDescription, + "html_description": htmlDescription, "meta_fields": tag.MetaFields, "meta": tag.Meta, "header_code": tag.HeaderCode, diff --git a/server/service/core/action/user/list.go b/server/service/core/action/user/list.go index 438bc64a8..af47c0b95 100644 --- a/server/service/core/action/user/list.go +++ b/server/service/core/action/user/list.go @@ -1,156 +1,144 @@ package user -import ( - "encoding/json" - "fmt" - "net/http" - "strconv" - "strings" - - "github.com/factly/dega-server/service/core/action/author" - "github.com/factly/dega-server/service/core/action/policy" - - "github.com/factly/dega-server/service/core/model" - "github.com/factly/dega-server/util" - "github.com/factly/x/middlewarex" - - "github.com/factly/x/errorx" - "github.com/factly/x/loggerx" - "github.com/factly/x/renderx" -) - -// list response -type paging struct { - Total int `json:"total"` - Nodes []userPolicy `json:"nodes"` -} - -type userPolicy struct { - model.Author - Policies []policyRes `json:"policies"` -} - -type policyRes struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` -} - -// list - Get users with space access -// @Summary Get users with space access -// @Description Get users with space access -// @Tags Users -// @ID get-space-users -// @Consume json -// @Produce json -// @Param X-User header string true "User ID" -// @Param X-Space header string true "Space ID" -// @Success 200 {object} paging -// @Router /core/users [get] -func list(w http.ResponseWriter, r *http.Request) { - sID, err := middlewarex.GetSpace(r.Context()) - if err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.Unauthorized())) - return - } - - oID, err := util.GetOrganisation(r.Context()) - if err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.Unauthorized())) - return - } - - userIDsMap := make(map[uint][]policyRes) - - // get all the admins of the organisation - adminRoleID := fmt.Sprint("roles:org:", oID, ":admin") - - resp, err := util.KetoGetRequest("/engines/acp/ory/regex/roles/" + adminRoleID) - if err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.NetworkError())) - return - } - - defer resp.Body.Close() - - adminRole := model.KetoRole{} - err = json.NewDecoder(resp.Body).Decode(&adminRole) - if err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.DecodeError())) - return - } - - for _, member := range adminRole.Members { - memid, _ := strconv.Atoi(member) - userIDsMap[uint(memid)] = []policyRes{ - policyRes{ - ID: "admin", - Name: "admin", - }, - } - } - - // Get all policies - policyList, err := policy.GetAllPolicies() - if err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.InternalServerError())) - return - } - - prefixName := fmt.Sprint("id:org:", oID, ":app:dega:space:", sID, ":") - - // append subjects whose id has prefix for our space - for _, policy := range policyList { - if strings.HasPrefix(policy.ID, prefixName) { - policyNameTokens := strings.Split(policy.ID, ":") - policyID := policyNameTokens[len(policyNameTokens)-1] - - for _, subject := range policy.Subjects { - subid, _ := strconv.Atoi(subject) - - if _, found := userIDsMap[uint(subid)]; !found { - userIDsMap[uint(subid)] = make([]policyRes, 0) - } - polRes := policyRes{ - ID: policyID, - Name: policyID, - Description: policy.Description, - } - userIDsMap[uint(subid)] = append(userIDsMap[uint(subid)], polRes) - } - } - } - - // Fetch all the users - userMap, err := author.All(r.Context()) - if err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.InternalServerError())) - return - } - - result := paging{} - - userlist := make([]userPolicy, 0) - - for usrID, pol := range userIDsMap { - userIDStr := fmt.Sprint(usrID) - if user, found := userMap[userIDStr]; found { - usrpol := userPolicy{ - Policies: pol, - Author: user, - } - userlist = append(userlist, usrpol) - } - } - - result.Nodes = userlist - result.Total = len(userlist) - - renderx.JSON(w, http.StatusOK, result) -} +// import ( +// "encoding/json" +// "fmt" +// "net/http" +// "strconv" +// "strings" + +// "github.com/factly/dega-server/service/core/action/author" +// "github.com/factly/dega-server/service/core/action/policy" + +// "github.com/factly/dega-server/service/core/model" +// "github.com/factly/dega-server/util" +// "github.com/factly/x/middlewarex" + +// "github.com/factly/x/errorx" +// "github.com/factly/x/loggerx" +// "github.com/factly/x/renderx" +// ) + +// // list response +// type paging struct { +// Total int `json:"total"` +// Nodes []userPolicy `json:"nodes"` +// } + +// type userPolicy struct { +// model.Author +// Policies []policyRes `json:"policies"` +// } + +// type policyRes struct { +// ID string `json:"id"` +// Name string `json:"name"` +// Description string `json:"description"` +// } + +// // list - Get users with space access +// // @Summary Get users with space access +// // @Description Get users with space access +// // @Tags Users +// // @ID get-space-users +// // @Consume json +// // @Produce json +// // @Param X-User header string true "User ID" +// // @Param X-Space header string true "Space ID" +// // @Success 200 {object} paging +// // @Router /core/users [get] +// func list(w http.ResponseWriter, r *http.Request) { +// sID, err := middlewarex.GetSpace(r.Context()) +// if err != nil { +// loggerx.Error(err) +// errorx.Render(w, errorx.Parser(errorx.Unauthorized())) +// return +// } + +// oID, err := util.GetOrganisation(r.Context()) +// if err != nil { +// loggerx.Error(err) +// errorx.Render(w, errorx.Parser(errorx.Unauthorized())) +// return +// } + +// // defer resp.Body.Close() + +// // adminRole := model.User{} +// // err = json.NewDecoder(resp.Body).Decode(&adminRole) +// // if err != nil { +// // loggerx.Error(err) +// // errorx.Render(w, errorx.Parser(errorx.DecodeError())) +// // return +// // } + +// // for _, member := range adminRole.Members { +// // memid, _ := strconv.Atoi(member) +// // userIDsMap[uint(memid)] = []policyRes{ +// // policyRes{ +// // ID: "admin", +// // Name: "admin", +// // }, +// // } +// // } + +// // Get all policies +// policyList, err := policy.GetAllPolicies() +// if err != nil { +// loggerx.Error(err) +// errorx.Render(w, errorx.Parser(errorx.InternalServerError())) +// return +// } + +// prefixName := fmt.Sprint("id:org:", oID, ":app:dega:space:", sID, ":") + +// // append subjects whose id has prefix for our space +// for _, policy := range policyList { +// if strings.HasPrefix(policy.ID, prefixName) { +// policyNameTokens := strings.Split(policy.ID, ":") +// policyID := policyNameTokens[len(policyNameTokens)-1] + +// for _, subject := range policy.Subjects { +// subid, _ := strconv.Atoi(subject) + +// if _, found := userIDsMap[uint(subid)]; !found { +// userIDsMap[uint(subid)] = make([]policyRes, 0) +// } +// polRes := policyRes{ +// ID: policyID, +// Name: policyID, +// Description: policy.Description, +// } +// userIDsMap[uint(subid)] = append(userIDsMap[uint(subid)], polRes) +// } +// } +// } + +// // Fetch all the users +// userMap, err := author.All(r.Context()) +// if err != nil { +// loggerx.Error(err) +// errorx.Render(w, errorx.Parser(errorx.InternalServerError())) +// return +// } + +// result := paging{} + +// userlist := make([]userPolicy, 0) + +// for usrID, pol := range userIDsMap { +// userIDStr := fmt.Sprint(usrID) +// if user, found := userMap[userIDStr]; found { +// usrpol := userPolicy{ +// Policies: pol, +// Author: user, +// } +// userlist = append(userlist, usrpol) +// } +// } + +// result.Nodes = userlist +// result.Total = len(userlist) + +// renderx.JSON(w, http.StatusOK, result) +// } diff --git a/server/service/core/action/user/permission.go b/server/service/core/action/user/permission.go index e78b4ad99..9dc31151a 100644 --- a/server/service/core/action/user/permission.go +++ b/server/service/core/action/user/permission.go @@ -32,6 +32,7 @@ import ( // @Param user_id path string true "User ID" // @Success 200 {object} []model.Permission // @Router /core/users/{user_id}/permissions [get] + func userpermissions(w http.ResponseWriter, r *http.Request) { uID, err := middlewarex.GetUser(r.Context()) if err != nil { @@ -62,12 +63,14 @@ func userpermissions(w http.ResponseWriter, r *http.Request) { errorx.Render(w, errorx.Parser(errorx.InvalidID())) return } - + isAdmin, err := util.CheckAdmin(uint(oID), uint(uID)) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InvalidID())) + return + } var result []model.Permission - // check if the user is admin of organisation - isAdmin := util.CheckSpaceKetoPermission("all", uint(oID), uint(uID)) - // fetch all the keto policies policyList, err := policy.GetAllPolicies() if err != nil { @@ -76,15 +79,15 @@ func userpermissions(w http.ResponseWriter, r *http.Request) { return } - if isAdmin == nil { + if isAdmin { // logged user is admin and user_id is also admin's if id == uID { - allPermission := []model.Permission{ + allPermission := append([]model.Permission{}, model.Permission{ Resource: "admin", Actions: []string{"admin"}, - }, - } + }) + renderx.JSON(w, http.StatusOK, allPermission) return } diff --git a/server/service/core/action/user/route.go b/server/service/core/action/user/route.go index abf115df6..5441828ab 100644 --- a/server/service/core/action/user/route.go +++ b/server/service/core/action/user/route.go @@ -6,7 +6,7 @@ import "github.com/go-chi/chi" func Router() chi.Router { r := chi.NewRouter() - r.Get("/", list) + // r.Get("/", list) r.Get("/{user_id}/permissions", userpermissions) return r diff --git a/server/service/core/model/category.go b/server/service/core/model/category.go index a616485b3..10f9b9e63 100644 --- a/server/service/core/model/category.go +++ b/server/service/core/model/category.go @@ -24,7 +24,6 @@ type Category struct { IsFeatured bool `gorm:"column:is_featured" json:"is_featured"` SpaceID uint `gorm:"column:space_id" json:"space_id"` Posts []*Post `gorm:"many2many:post_categories;" json:"posts"` - Space *Space `json:"space,omitempty"` MetaFields postgres.Jsonb `gorm:"column:meta_fields" json:"meta_fields" swaggertype:"primitive,string"` Meta postgres.Jsonb `gorm:"column:meta" json:"meta" swaggertype:"primitive,string"` HeaderCode string `gorm:"column:header_code" json:"header_code"` diff --git a/server/service/core/model/format.go b/server/service/core/model/format.go index 6206ecbd2..63f2c94c1 100644 --- a/server/service/core/model/format.go +++ b/server/service/core/model/format.go @@ -19,7 +19,6 @@ type Format struct { HeaderCode string `gorm:"column:header_code" json:"header_code"` FooterCode string `gorm:"column:footer_code" json:"footer_code"` SpaceID uint `gorm:"column:space_id" json:"space_id"` - Space *Space `json:"space,omitempty"` } var formatUser config.ContextKey = "format_user" diff --git a/server/service/core/model/menu.go b/server/service/core/model/menu.go index 555fb8097..d137f72df 100644 --- a/server/service/core/model/menu.go +++ b/server/service/core/model/menu.go @@ -14,7 +14,6 @@ type Menu struct { Menu postgres.Jsonb `gorm:"column:menu" json:"menu" swaggertype:"primitive,string"` SpaceID uint `gorm:"column:space_id" json:"space_id"` MetaFields postgres.Jsonb `gorm:"column:meta_fields" json:"meta_fields" swaggertype:"primitive,string"` - Space *Space `json:"space,omitempty"` } var menuUser config.ContextKey = "menu_user" diff --git a/server/service/core/model/migration.go b/server/service/core/model/migration.go index 33aa72442..26f582db6 100644 --- a/server/service/core/model/migration.go +++ b/server/service/core/model/migration.go @@ -8,7 +8,6 @@ func Migration() { &Medium{}, &Category{}, &Tag{}, - &Space{}, &Format{}, &Post{}, &PostAuthor{}, diff --git a/server/service/core/model/policy.go b/server/service/core/model/policy.go index df2407895..2e6f52f96 100644 --- a/server/service/core/model/policy.go +++ b/server/service/core/model/policy.go @@ -1,5 +1,7 @@ package model +import "github.com/factly/dega-server/config" + // KetoPolicy model type KetoPolicy struct { ID string `json:"id"` @@ -30,3 +32,12 @@ type Policy struct { Permissions []Permission `json:"permissions"` Users []Author `json:"users"` } + +type KavachPolicy struct { + config.Base + Name string `json:"name"` + Description string `json:"description"` + Permissions []Permission `json:"permissions"` + Roles []SpaceRole `json:"roles"` + SpaceID uint `json:"space_id"` +} diff --git a/server/service/core/model/post.go b/server/service/core/model/post.go index 4c5331f8e..f79800e3d 100644 --- a/server/service/core/model/post.go +++ b/server/service/core/model/post.go @@ -37,7 +37,6 @@ type Post struct { MetaFields postgres.Jsonb `gorm:"column:meta_fields" json:"meta_fields" swaggertype:"primitive,string"` Tags []Tag `gorm:"many2many:post_tags;" json:"tags"` Categories []Category `gorm:"many2many:post_categories;" json:"categories"` - Space *Space `json:"space,omitempty"` } // PostAuthor model diff --git a/server/service/core/model/request.go b/server/service/core/model/request.go index 54ff60935..f819883c6 100644 --- a/server/service/core/model/request.go +++ b/server/service/core/model/request.go @@ -31,7 +31,6 @@ type SpacePermissionRequest struct { Podcast bool `gorm:"column:podcast" json:"podcast"` FactCheck bool `gorm:"column:fact_check" json:"fact_check"` SpaceID uint `gorm:"column:space_id" json:"space_id"` - Space *Space `gorm:"foreignKey:space_id" json:"space,omitempty"` } var spaceRequestUser config.ContextKey = "space_perm_user" diff --git a/server/service/core/model/role.go b/server/service/core/model/role.go new file mode 100644 index 000000000..ff3a3c82e --- /dev/null +++ b/server/service/core/model/role.go @@ -0,0 +1,31 @@ +package model + +import ( + "github.com/factly/dega-server/config" + "github.com/jinzhu/gorm/dialects/postgres" +) + +type SpaceRole struct { + config.Base + Name string `json:"name"` + Description string `json:"description"` + Slug string `json:"slug"` + SpaceID uint `json:"space_id"` + Users []User `json:"users"` +} + +type User struct { + config.Base + Email string `json:"email"` + KID string `json:"kid"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + Slug string `json:"slug"` + DisplayName string `json:"display_name"` + Gender string `json:"gender"` + FeaturedMediumID *uint `json:"featured_medium_id"` + Medium *Medium `json:"medium"` + SocialMediaURLs postgres.Jsonb `json:"social_media_urls" swaggertype:"primitive,string"` + Description string `json:"description"` + Meta postgres.Jsonb `json:"meta"` +} diff --git a/server/service/core/model/space.go b/server/service/core/model/space.go index e4682f211..38a1fd3f6 100644 --- a/server/service/core/model/space.go +++ b/server/service/core/model/space.go @@ -34,6 +34,7 @@ type Space struct { FooterCode string `gorm:"column:footer_code" json:"footer_code"` MetaFields postgres.Jsonb `gorm:"column:meta_fields" json:"meta_fields" swaggertype:"primitive,string"` OrganisationID int `gorm:"column:organisation_id" json:"organisation_id"` + ApplicationID uint `gorm:"application_id" json:"application_id"` } // SpacePermission model @@ -41,7 +42,6 @@ type SpacePermission struct { config.Base FactCheck bool `gorm:"column:fact_check" json:"fact_check"` SpaceID uint `gorm:"column:space_id" json:"space_id"` - Space *Space `gorm:"foreignKey:space_id" json:"space,omitempty"` Media int64 `gorm:"column:media" json:"media"` Posts int64 `gorm:"column:posts" json:"posts"` Podcast bool `gorm:"column:podcast" json:"podcast"` @@ -49,6 +49,15 @@ type SpacePermission struct { Videos int64 `gorm:"column:videos" json:"videos"` } +type KavachSpace struct { + config.Base + Name string `json:"name"` + Slug string `json:"slug"` + Description string `json:"description"` + ApplicationID uint `json:"application_id"` + OrganisationID uint `json:"organisation_id"` + Metadata postgres.Jsonb `json:"metadata"` +} var spaceUser config.ContextKey = "space_user" var spacePermissionUser config.ContextKey = "space_perm_user" diff --git a/server/service/core/model/tag.go b/server/service/core/model/tag.go index 4971d3162..5a960a137 100644 --- a/server/service/core/model/tag.go +++ b/server/service/core/model/tag.go @@ -19,7 +19,6 @@ type Tag struct { MediumID *uint `gorm:"column:medium_id;default:NULL" json:"medium_id"` Medium *Medium `json:"medium"` SpaceID uint `gorm:"column:space_id" json:"space_id"` - Space *Space `json:"space,omitempty"` Posts []*Post `gorm:"many2many:post_tags;" json:"posts"` Meta postgres.Jsonb `gorm:"column:meta" json:"meta" swaggertype:"primitive,string"` HeaderCode string `gorm:"column:header_code" json:"header_code"` diff --git a/server/service/fact-check/action/claim/create.go b/server/service/fact-check/action/claim/create.go index bf533f735..a5f24c41a 100644 --- a/server/service/fact-check/action/claim/create.go +++ b/server/service/fact-check/action/claim/create.go @@ -5,11 +5,9 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/fact-check/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -18,6 +16,7 @@ import ( "github.com/factly/x/renderx" "github.com/factly/x/slugx" "github.com/factly/x/validationx" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -88,13 +87,20 @@ func create(w http.ResponseWriter, r *http.Request) { } } - // Store HTML description - var description string - if len(claim.Description.RawMessage) > 0 && !reflect.DeepEqual(claim.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(claim.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(claim.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(claim.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse claim description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(claim.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -114,8 +120,8 @@ func create(w http.ResponseWriter, r *http.Request) { ClaimDate: claim.ClaimDate, CheckedDate: claim.CheckedDate, ClaimSources: claim.ClaimSources, - Description: claim.Description, - HTMLDescription: description, + Description: jsonDescription, + HTMLDescription: htmlDescription, ClaimantID: claim.ClaimantID, RatingID: claim.RatingID, Fact: claim.Fact, diff --git a/server/service/fact-check/action/claim/update.go b/server/service/fact-check/action/claim/update.go index 9645eace4..736e1b511 100644 --- a/server/service/fact-check/action/claim/update.go +++ b/server/service/fact-check/action/claim/update.go @@ -4,12 +4,10 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "strconv" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/fact-check/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -19,6 +17,7 @@ import ( "github.com/factly/x/slugx" "github.com/factly/x/validationx" "github.com/go-chi/chi" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -115,13 +114,20 @@ func update(w http.ResponseWriter, r *http.Request) { } } - // Store HTML description - var description string - if len(claim.Description.RawMessage) > 0 && !reflect.DeepEqual(claim.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(claim.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(claim.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(claim.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse claim description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(claim.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -135,8 +141,8 @@ func update(w http.ResponseWriter, r *http.Request) { "claim": claim.Claim, "slug": claimSlug, "claim_sources": claim.ClaimSources, - "description": claim.Description, - "html_description": description, + "description": jsonDescription, + "html_description": htmlDescription, "claimant_id": claim.ClaimantID, "rating_id": claim.RatingID, "fact": claim.Fact, diff --git a/server/service/fact-check/action/claimant/create.go b/server/service/fact-check/action/claimant/create.go index b603f07cb..007e6ac60 100644 --- a/server/service/fact-check/action/claimant/create.go +++ b/server/service/fact-check/action/claimant/create.go @@ -5,11 +5,9 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/fact-check/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -18,6 +16,7 @@ import ( "github.com/factly/x/renderx" "github.com/factly/x/slugx" "github.com/factly/x/validationx" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -92,13 +91,20 @@ func create(w http.ResponseWriter, r *http.Request) { return } - var description string - // Store HTML description - if len(claimant.Description.RawMessage) > 0 && !reflect.DeepEqual(claimant.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(claimant.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(claimant.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(claimant.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse claimant description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(claimant.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -110,8 +116,8 @@ func create(w http.ResponseWriter, r *http.Request) { }, Name: claimant.Name, Slug: slugx.Approve(&config.DB, claimantSlug, sID, tableName), - Description: claimant.Description, - HTMLDescription: description, + Description: jsonDescription, + HTMLDescription: htmlDescription, MediumID: mediumID, IsFeatured: claimant.IsFeatured, SpaceID: uint(sID), diff --git a/server/service/fact-check/action/claimant/update.go b/server/service/fact-check/action/claimant/update.go index 82d514654..b1fa011c8 100644 --- a/server/service/fact-check/action/claimant/update.go +++ b/server/service/fact-check/action/claimant/update.go @@ -4,13 +4,11 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "strconv" "time" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/fact-check/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -20,6 +18,7 @@ import ( "github.com/factly/x/slugx" "github.com/factly/x/validationx" "github.com/go-chi/chi" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -114,13 +113,20 @@ func update(w http.ResponseWriter, r *http.Request) { return } - // Store HTML description - var description string - if len(claimant.Description.RawMessage) > 0 && !reflect.DeepEqual(claimant.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(claimant.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(claimant.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(claimant.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse claimant description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(claimant.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -132,8 +138,8 @@ func update(w http.ResponseWriter, r *http.Request) { "updated_by_id": uID, "name": claimant.Name, "slug": claimantSlug, - "description": claimant.Description, - "html_description": description, + "description": jsonDescription, + "html_description": htmlDescription, "medium_id": claimant.MediumID, "tag_line": claimant.TagLine, "is_featured": claimant.IsFeatured, diff --git a/server/service/fact-check/action/google/list.go b/server/service/fact-check/action/google/list.go index 78da1624d..324bb9c2b 100644 --- a/server/service/fact-check/action/google/list.go +++ b/server/service/fact-check/action/google/list.go @@ -7,11 +7,11 @@ import ( "net/http" "net/url" - "github.com/spf13/viper" - + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/renderx" + "github.com/spf13/viper" ) // googleapis for factchecks @@ -71,8 +71,7 @@ func list(w http.ResponseWriter, r *http.Request) { } req.URL.RawQuery = q.Encode() - - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { diff --git a/server/service/fact-check/action/rating/create.go b/server/service/fact-check/action/rating/create.go index b8da4f8bb..f5f08f98c 100644 --- a/server/service/fact-check/action/rating/create.go +++ b/server/service/fact-check/action/rating/create.go @@ -5,11 +5,9 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/fact-check/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -18,6 +16,7 @@ import ( "github.com/factly/x/renderx" "github.com/factly/x/slugx" "github.com/factly/x/validationx" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -105,13 +104,20 @@ func create(w http.ResponseWriter, r *http.Request) { mediumID = nil } - // Store HTML description - var description string - if len(rating.Description.RawMessage) > 0 && !reflect.DeepEqual(rating.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(rating.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(rating.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(rating.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse rating description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(rating.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -125,8 +131,8 @@ func create(w http.ResponseWriter, r *http.Request) { Slug: slugx.Approve(&config.DB, ratingSlug, sID, tableName), BackgroundColour: rating.BackgroundColour, TextColour: rating.TextColour, - Description: rating.Description, - HTMLDescription: description, + Description: jsonDescription, + HTMLDescription: htmlDescription, MediumID: mediumID, SpaceID: uint(sID), NumericValue: rating.NumericValue, diff --git a/server/service/fact-check/action/rating/default.go b/server/service/fact-check/action/rating/default.go index a6451bf36..8508d3c69 100644 --- a/server/service/fact-check/action/rating/default.go +++ b/server/service/fact-check/action/rating/default.go @@ -69,7 +69,15 @@ func createDefaults(w http.ResponseWriter, r *http.Request) { for i := range ratings { ratings[i].SpaceID = uint(sID) - ratings[i].HTMLDescription, err = util.HTMLDescription(ratings[i].Description) + ratings[i].HTMLDescription, err = util.GetHTMLDescription(ratings[i].Description) + if err != nil { + tx.Rollback() + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse rating description", http.StatusUnprocessableEntity))) + return + } + + ratings[i].Description, err = util.GetJSONDescription(ratings[i].Description) if err != nil { tx.Rollback() loggerx.Error(err) diff --git a/server/service/fact-check/action/rating/list.go b/server/service/fact-check/action/rating/list.go index 7e4dc1047..e4332c2df 100644 --- a/server/service/fact-check/action/rating/list.go +++ b/server/service/fact-check/action/rating/list.go @@ -32,7 +32,6 @@ type paging struct { // @Success 200 {object} paging // @Router /fact-check/ratings [get] func list(w http.ResponseWriter, r *http.Request) { - sID, err := middlewarex.GetSpace(r.Context()) if err != nil { loggerx.Error(err) diff --git a/server/service/fact-check/action/rating/route.go b/server/service/fact-check/action/rating/route.go index 20232fbe3..fbf09d192 100644 --- a/server/service/fact-check/action/rating/route.go +++ b/server/service/fact-check/action/rating/route.go @@ -42,7 +42,7 @@ func Router() chi.Router { r.With(util.CheckKetoPolicy(entity, "get")).Get("/", details) r.With(util.CheckKetoPolicy(entity, "update")).Put("/", update) r.With(util.CheckKetoPolicy(entity, "delete")).Delete("/", delete) - }) + }) return r diff --git a/server/service/fact-check/action/rating/update.go b/server/service/fact-check/action/rating/update.go index c2f873b18..b628aed6b 100644 --- a/server/service/fact-check/action/rating/update.go +++ b/server/service/fact-check/action/rating/update.go @@ -4,12 +4,10 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "strconv" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/fact-check/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -19,6 +17,7 @@ import ( "github.com/factly/x/slugx" "github.com/factly/x/validationx" "github.com/go-chi/chi" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -127,13 +126,20 @@ func update(w http.ResponseWriter, r *http.Request) { } } - // Store HTML description - var description string - if len(rating.Description.RawMessage) > 0 && !reflect.DeepEqual(rating.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(rating.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(rating.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(rating.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse rating description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(rating.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -149,8 +155,8 @@ func update(w http.ResponseWriter, r *http.Request) { "background_colour": rating.BackgroundColour, "text_colour": rating.TextColour, "medium_id": rating.MediumID, - "description": rating.Description, - "html_description": description, + "description": jsonDescription, + "html_description": htmlDescription, "numeric_value": rating.NumericValue, "meta_fields": rating.MetaFields, "meta": rating.Meta, diff --git a/server/service/fact-check/model/claim.go b/server/service/fact-check/model/claim.go index 798f866ce..74b123dbf 100644 --- a/server/service/fact-check/model/claim.go +++ b/server/service/fact-check/model/claim.go @@ -32,7 +32,6 @@ type Claim struct { ReviewSources postgres.Jsonb `gorm:"column:review_sources" json:"review_sources" swaggertype:"primitive,string"` MetaFields postgres.Jsonb `gorm:"column:meta_fields" json:"meta_fields" swaggertype:"primitive,string"` SpaceID uint `gorm:"column:space_id" json:"space_id"` - Space *model.Space `json:"space,omitempty"` VideoID *uint `gorm:"column:video_id" json:"video_id"` Video *Video `json:"video"` EndTime int `gorm:"column:end_time" json:"end_time"` diff --git a/server/service/fact-check/model/claimant.go b/server/service/fact-check/model/claimant.go index 462043551..24516865b 100644 --- a/server/service/fact-check/model/claimant.go +++ b/server/service/fact-check/model/claimant.go @@ -23,7 +23,6 @@ type Claimant struct { Medium *model.Medium `json:"medium"` MetaFields postgres.Jsonb `gorm:"column:meta_fields" json:"meta_fields" swaggertype:"primitive,string"` SpaceID uint `gorm:"column:space_id" json:"space_id"` - Space *model.Space `json:"space,omitempty"` Meta postgres.Jsonb `gorm:"column:meta" json:"meta" swaggertype:"primitive,string"` HeaderCode string `gorm:"column:header_code" json:"header_code"` FooterCode string `gorm:"column:footer_code" json:"footer_code"` diff --git a/server/service/fact-check/model/rating.go b/server/service/fact-check/model/rating.go index 178bf1b9c..1308edbcb 100644 --- a/server/service/fact-check/model/rating.go +++ b/server/service/fact-check/model/rating.go @@ -24,7 +24,6 @@ type Rating struct { Medium *model.Medium `json:"medium"` MetaFields postgres.Jsonb `gorm:"column:meta_fields" json:"meta_fields" swaggertype:"primitive,string"` SpaceID uint `gorm:"column:space_id" json:"space_id"` - Space *model.Space `json:"space,omitempty"` Meta postgres.Jsonb `gorm:"column:meta" json:"meta" swaggertype:"primitive,string"` HeaderCode string `gorm:"column:header_code" json:"header_code"` FooterCode string `gorm:"column:footer_code" json:"footer_code"` diff --git a/server/service/podcast/action/create.go b/server/service/podcast/action/create.go index 6624ca51d..a59d4de48 100644 --- a/server/service/podcast/action/create.go +++ b/server/service/podcast/action/create.go @@ -5,12 +5,10 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "github.com/factly/dega-server/config" coreModel "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/service/podcast/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -19,6 +17,7 @@ import ( "github.com/factly/x/renderx" "github.com/factly/x/slugx" "github.com/factly/x/validationx" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -99,12 +98,20 @@ func create(w http.ResponseWriter, r *http.Request) { } // Store HTML description - var description string - if len(podcast.Description.RawMessage) > 0 && !reflect.DeepEqual(podcast.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(podcast.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(podcast.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(podcast.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse podcast description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(podcast.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -115,8 +122,8 @@ func create(w http.ResponseWriter, r *http.Request) { UpdatedAt: podcast.UpdatedAt, }, Title: podcast.Title, - Description: podcast.Description, - HTMLDescription: description, + Description: jsonDescription, + HTMLDescription: htmlDescription, Slug: slugx.Approve(&config.DB, podcastSlug, sID, tableName), Language: podcast.Language, MediumID: mediumID, diff --git a/server/service/podcast/action/episode/create.go b/server/service/podcast/action/episode/create.go index 0ba92c18f..46fa1e12c 100644 --- a/server/service/podcast/action/episode/create.go +++ b/server/service/podcast/action/episode/create.go @@ -6,13 +6,12 @@ import ( "errors" "fmt" "net/http" - "reflect" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/podcast/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" + "github.com/jinzhu/gorm/dialects/postgres" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -92,16 +91,24 @@ func create(w http.ResponseWriter, r *http.Request) { podcastID = nil } - // Store HTML description - var description string - if len(episode.Description.RawMessage) > 0 && !reflect.DeepEqual(episode.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(episode.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(episode.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(episode.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse episode description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(episode.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } + result := &episodeData{} result.Episode = model.Episode{ Base: config.Base{ @@ -109,8 +116,8 @@ func create(w http.ResponseWriter, r *http.Request) { UpdatedAt: episode.UpdatedAt, }, Title: episode.Title, - Description: episode.Description, - HTMLDescription: description, + Description: jsonDescription, + HTMLDescription: htmlDescription, Slug: slugx.Approve(&config.DB, episodeSlug, sID, tableName), Season: episode.Season, Episode: episode.Episode, diff --git a/server/service/podcast/action/episode/update.go b/server/service/podcast/action/episode/update.go index 7450926b8..742913f94 100644 --- a/server/service/podcast/action/episode/update.go +++ b/server/service/podcast/action/episode/update.go @@ -5,13 +5,11 @@ import ( "errors" "fmt" "net/http" - "reflect" "strconv" "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/action/author" "github.com/factly/dega-server/service/podcast/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/dega-server/util/arrays" "github.com/factly/x/errorx" @@ -22,6 +20,7 @@ import ( "github.com/factly/x/slugx" "github.com/factly/x/validationx" "github.com/go-chi/chi" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -109,131 +108,138 @@ func update(w http.ResponseWriter, r *http.Request) { episodeSlug = slugx.Approve(&config.DB, slugx.Make(episode.Title), sID, tableName) } - // Store HTML description - var description string - if len(episode.Description.RawMessage) > 0 && !reflect.DeepEqual(episode.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(episode.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(episode.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(episode.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse episode description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } - } - tx := config.DB.Begin() - - updateMap := map[string]interface{}{ - "created_at": episode.CreatedAt, - "updated_at": episode.UpdatedAt, - "updated_by_id": uint(uID), - "title": episode.Title, - "slug": episodeSlug, - "html_description": description, - "description": episode.Description, - "season": episode.Season, - "episode": episode.Episode, - "audio_url": episode.AudioURL, - "podcast_id": episode.PodcastID, - "published_date": episode.PublishedDate, - "medium_id": episode.MediumID, - "meta_fields": episode.MetaFields, - } + jsonDescription, err = util.GetJSONDescription(episode.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } - if episode.MediumID == 0 { - updateMap["medium_id"] = nil - } + tx := config.DB.Begin() + + updateMap := map[string]interface{}{ + "created_at": episode.CreatedAt, + "updated_at": episode.UpdatedAt, + "updated_by_id": uint(uID), + "title": episode.Title, + "slug": episodeSlug, + "html_description": htmlDescription, + "description": jsonDescription, + "season": episode.Season, + "episode": episode.Episode, + "audio_url": episode.AudioURL, + "podcast_id": episode.PodcastID, + "published_date": episode.PublishedDate, + "medium_id": episode.MediumID, + "meta_fields": episode.MetaFields, + } - if episode.PodcastID == 0 { - updateMap["podcast_id"] = nil - } + if episode.MediumID == 0 { + updateMap["medium_id"] = nil + } - tx.Model(&result.Episode).Updates(&updateMap).Preload("Medium").Preload("Podcast").Preload("Podcast.Medium").First(&result.Episode) + if episode.PodcastID == 0 { + updateMap["podcast_id"] = nil + } - // fetch old authors - prevEpisodeAuthors := make([]model.EpisodeAuthor, 0) - tx.Model(&model.EpisodeAuthor{}).Where(&model.EpisodeAuthor{ - EpisodeID: uint(result.Episode.ID), - }).Find(&prevEpisodeAuthors) + tx.Model(&result.Episode).Updates(&updateMap).Preload("Medium").Preload("Podcast").Preload("Podcast.Medium").First(&result.Episode) - prevAuthorIDs := make([]uint, 0) - for _, each := range prevEpisodeAuthors { - prevAuthorIDs = append(prevAuthorIDs, each.AuthorID) - } + // fetch old authors + prevEpisodeAuthors := make([]model.EpisodeAuthor, 0) + tx.Model(&model.EpisodeAuthor{}).Where(&model.EpisodeAuthor{ + EpisodeID: uint(result.Episode.ID), + }).Find(&prevEpisodeAuthors) - toCreateIDs, toDeleteIDs := arrays.Difference(prevAuthorIDs, episode.AuthorIDs) + prevAuthorIDs := make([]uint, 0) + for _, each := range prevEpisodeAuthors { + prevAuthorIDs = append(prevAuthorIDs, each.AuthorID) + } - if len(toDeleteIDs) > 0 { - tx.Model(&model.EpisodeAuthor{}).Where("author_id IN (?)", toDeleteIDs).Delete(&model.EpisodeAuthor{}) - } + toCreateIDs, toDeleteIDs := arrays.Difference(prevAuthorIDs, episode.AuthorIDs) - if len(toCreateIDs) > 0 { - createEpisodeAuthors := make([]model.EpisodeAuthor, 0) - for _, each := range toCreateIDs { - epiAuth := model.EpisodeAuthor{ - EpisodeID: uint(result.Episode.ID), - AuthorID: each, + if len(toDeleteIDs) > 0 { + tx.Model(&model.EpisodeAuthor{}).Where("author_id IN (?)", toDeleteIDs).Delete(&model.EpisodeAuthor{}) + } + + if len(toCreateIDs) > 0 { + createEpisodeAuthors := make([]model.EpisodeAuthor, 0) + for _, each := range toCreateIDs { + epiAuth := model.EpisodeAuthor{ + EpisodeID: uint(result.Episode.ID), + AuthorID: each, + } + createEpisodeAuthors = append(createEpisodeAuthors, epiAuth) + } + + if err = tx.Model(&model.EpisodeAuthor{}).Create(&createEpisodeAuthors).Error; err != nil { + tx.Rollback() + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DBError())) + return } - createEpisodeAuthors = append(createEpisodeAuthors, epiAuth) } - if err = tx.Model(&model.EpisodeAuthor{}).Create(&createEpisodeAuthors).Error; err != nil { - tx.Rollback() + // Fetch current authors + authorMap, err := author.All(r.Context()) + if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.DBError())) return } - } - // Fetch current authors - authorMap, err := author.All(r.Context()) - if err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.DBError())) - return - } + authorEpisodes := make([]model.EpisodeAuthor, 0) + tx.Model(&model.EpisodeAuthor{}).Where(&model.EpisodeAuthor{ + EpisodeID: uint(id), + }).Find(&authorEpisodes) - authorEpisodes := make([]model.EpisodeAuthor, 0) - tx.Model(&model.EpisodeAuthor{}).Where(&model.EpisodeAuthor{ - EpisodeID: uint(id), - }).Find(&authorEpisodes) - - for _, each := range authorEpisodes { - result.Authors = append(result.Authors, authorMap[fmt.Sprint(each.AuthorID)]) - } + for _, each := range authorEpisodes { + result.Authors = append(result.Authors, authorMap[fmt.Sprint(each.AuthorID)]) + } - // Update into meili index - var publishedDate int64 - if result.PublishedDate == nil { - publishedDate = 0 - } else { - publishedDate = result.PublishedDate.Unix() - } - meiliObj := map[string]interface{}{ - "id": result.Episode.ID, - "kind": "episode", - "title": result.Title, - "slug": result.Slug, - "season": result.Season, - "episode": result.Episode, - "audio_url": result.AudioURL, - "podcast_id": result.PodcastID, - "description": result.Description, - "published_date": publishedDate, - "space_id": result.SpaceID, - "medium_id": result.MediumID, - } + // Update into meili index + var publishedDate int64 + if result.PublishedDate == nil { + publishedDate = 0 + } else { + publishedDate = result.PublishedDate.Unix() + } + meiliObj := map[string]interface{}{ + "id": result.Episode.ID, + "kind": "episode", + "title": result.Title, + "slug": result.Slug, + "season": result.Season, + "episode": result.Episode, + "audio_url": result.AudioURL, + "podcast_id": result.PodcastID, + "description": result.Description, + "published_date": publishedDate, + "space_id": result.SpaceID, + "medium_id": result.MediumID, + } - if config.SearchEnabled() { - _ = meilisearchx.UpdateDocument("dega", meiliObj) - } + if config.SearchEnabled() { + _ = meilisearchx.UpdateDocument("dega", meiliObj) + } - tx.Commit() - if util.CheckNats() { - if err = util.NC.Publish("episode.updated", result); err != nil { - loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.InternalServerError())) - return + tx.Commit() + if util.CheckNats() { + if err = util.NC.Publish("episode.updated", result); err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } } + renderx.JSON(w, http.StatusOK, result) } - renderx.JSON(w, http.StatusOK, result) } diff --git a/server/service/podcast/action/update.go b/server/service/podcast/action/update.go index 39d92a7b5..2b056207b 100644 --- a/server/service/podcast/action/update.go +++ b/server/service/podcast/action/update.go @@ -4,13 +4,11 @@ import ( "encoding/json" "errors" "net/http" - "reflect" "strconv" "github.com/factly/dega-server/config" coreModel "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/service/podcast/model" - "github.com/factly/dega-server/test" "github.com/factly/dega-server/util" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" @@ -20,6 +18,7 @@ import ( "github.com/factly/x/slugx" "github.com/factly/x/validationx" "github.com/go-chi/chi" + "github.com/jinzhu/gorm/dialects/postgres" "gorm.io/gorm" ) @@ -114,13 +113,20 @@ func update(w http.ResponseWriter, r *http.Request) { return } - // Store HTML description - var description string - if len(podcast.Description.RawMessage) > 0 && !reflect.DeepEqual(podcast.Description, test.NilJsonb()) { - description, err = util.HTMLDescription(podcast.Description) + var htmlDescription string + var jsonDescription postgres.Jsonb + if len(podcast.Description.RawMessage) > 0 { + htmlDescription, err = util.GetHTMLDescription(podcast.Description) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage("cannot parse podcast description", http.StatusUnprocessableEntity))) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) + return + } + + jsonDescription, err = util.GetJSONDescription(podcast.Description) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.DecodeError())) return } } @@ -146,8 +152,8 @@ func update(w http.ResponseWriter, r *http.Request) { "updated_by_id": uint(uID), "title": podcast.Title, "slug": podcastSlug, - "html_description": description, - "description": podcast.Description, + "html_description": htmlDescription, + "description": jsonDescription, "medium_id": podcast.MediumID, "meta_fields": podcast.MetaFields, "language": podcast.Language, diff --git a/server/service/podcast/model/episode.go b/server/service/podcast/model/episode.go index 4c4b30199..0125c832d 100644 --- a/server/service/podcast/model/episode.go +++ b/server/service/podcast/model/episode.go @@ -30,7 +30,6 @@ type Episode struct { HeaderCode string `gorm:"column:header_code" json:"header_code"` FooterCode string `gorm:"column:footer_code" json:"footer_code"` SpaceID uint `gorm:"column:space_id" json:"space_id"` - Space *model.Space `json:"space,omitempty"` } // EpisodeAuthor model diff --git a/server/service/podcast/model/podcast.go b/server/service/podcast/model/podcast.go index ddb56b978..4252425d9 100644 --- a/server/service/podcast/model/podcast.go +++ b/server/service/podcast/model/podcast.go @@ -27,7 +27,6 @@ type Podcast struct { MetaFields postgres.Jsonb `gorm:"column:meta_fields" json:"meta_fields" swaggertype:"primitive,string"` Meta postgres.Jsonb `gorm:"column:meta" json:"meta" swaggertype:"primitive,string"` SpaceID uint `gorm:"column:space_id" json:"space_id"` - Space *model.Space `json:"space,omitempty"` } // BeforeSave - validation for medium diff --git a/server/service/reindex/all.go b/server/service/reindex/all.go index 0faa2e2a0..fc4f7a178 100644 --- a/server/service/reindex/all.go +++ b/server/service/reindex/all.go @@ -27,13 +27,18 @@ func all(w http.ResponseWriter, r *http.Request) { return } - err = util.CheckSpaceKetoPermission("create", uint(oID), uint(uID)) + isAdmin, err := util.CheckAdmin(uint(oID), uint(uID)) if err != nil { loggerx.Error(err) - errorx.Render(w, errorx.Parser(errorx.GetMessage(err.Error(), http.StatusUnauthorized))) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) return } + if !isAdmin { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } _, err = meilisearchx.Client.Index("dega").DeleteAllDocuments() if err != nil { loggerx.Error(err) diff --git a/server/service/reindex/space.go b/server/service/reindex/space.go index 081439e65..9b40539b7 100644 --- a/server/service/reindex/space.go +++ b/server/service/reindex/space.go @@ -1,6 +1,7 @@ package reindex import ( + "encoding/json" "fmt" "log" "net/http" @@ -9,6 +10,7 @@ import ( "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" "github.com/factly/dega-server/util" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/loggerx" "github.com/factly/x/meilisearchx" @@ -16,10 +18,10 @@ import ( "github.com/factly/x/renderx" "github.com/go-chi/chi" "github.com/meilisearch/meilisearch-go" + "github.com/spf13/viper" ) func space(w http.ResponseWriter, r *http.Request) { - spaceID := chi.URLParam(r, "space_id") sID, err := strconv.Atoi(spaceID) if err != nil { @@ -35,6 +37,30 @@ func space(w http.ResponseWriter, r *http.Request) { return } + req, err := http.NewRequest(http.MethodGet, viper.GetString("kavach_url")+fmt.Sprintf("/util/space/%d/getOrganisation", sID), nil) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + + req.Header.Set("X-User", fmt.Sprintf("%d", uID)) + client := httpx.CustomHttpClient() + response, err := client.Do(req) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + defer response.Body.Close() + responseBody := map[string]interface{}{} + err = json.NewDecoder(response.Body).Decode(&responseBody) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + oID := int(responseBody["organisation_id"].(float64)) space := model.Space{} space.ID = uint(sID) @@ -45,24 +71,29 @@ func space(w http.ResponseWriter, r *http.Request) { return } - err = util.CheckSpaceKetoPermission("create", uint(space.OrganisationID), uint(uID)) + isAdmin, err := util.CheckAdmin(uint(oID), uint(uID)) if err != nil { loggerx.Error(err) errorx.Render(w, errorx.Parser(errorx.Unauthorized())) return } + if !isAdmin { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.Unauthorized())) + return + } + res, err := meilisearchx.Client.Index("dega").Search("", &meilisearch.SearchRequest{ Filter: "space_id=" + fmt.Sprint(sID), - Limit: 100000, + Limit: 100000, }) if err != nil { log.Println(err) } - - if res!=nil{ + if res != nil { hits := res.Hits if len(hits) > 0 { diff --git a/server/service/route.go b/server/service/route.go index 66396cc43..d8f6e8393 100644 --- a/server/service/route.go +++ b/server/service/route.go @@ -60,6 +60,15 @@ func RegisterRoutes() http.Handler { "meilisearch": util.MeiliChecker, }) + //Earlier + // r.With(middlewarex.CheckUser, middlewarex.CheckSpace(1), util.GenerateOrganisation, middlewarex.CheckAccess("dega", 1, util.GetOrganisation)).Group(func(r chi.Router) { + // r.Mount("/core", core.Router()) + // r.With(util.FactCheckPermission).Mount("/fact-check", factCheck.Router()) + // r.With(util.PodcastPermission).Mount("/podcast", podcast.Router()) + // r.Mount("/reindex", reindex.Router()) + // }) + + // After Latest kavach changes r.With(middlewarex.CheckUser, middlewarex.CheckSpace(1), util.GenerateOrganisation, middlewarex.CheckAccess("dega", 1, util.GetOrganisation)).Group(func(r chi.Router) { r.Mount("/core", core.Router()) r.With(util.FactCheckPermission).Mount("/fact-check", factCheck.Router()) diff --git a/server/service/user/route.go b/server/service/user/route.go index 7400f6da2..880a1f5ab 100644 --- a/server/service/user/route.go +++ b/server/service/user/route.go @@ -4,6 +4,7 @@ import ( "encoding/json" "net/http" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/errorx" "github.com/factly/x/renderx" "github.com/go-chi/chi" @@ -44,8 +45,7 @@ func redirectToKavach(w http.ResponseWriter, r *http.Request) { req.Header.Set("Content-Type", "application/json") req.Header.Set("X-USer", header.Get("X-User")) - - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { errorx.Render(w, errorx.Parser(errorx.InternalServerError())) diff --git a/server/util/applicationID.go b/server/util/applicationID.go new file mode 100644 index 000000000..c12f06f42 --- /dev/null +++ b/server/util/applicationID.go @@ -0,0 +1,38 @@ +package util + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + + httpx "github.com/factly/dega-server/util/http" + "github.com/spf13/viper" +) + +func GetApplicationID(userID uint, appSlug string) (uint, error) { + req, err := http.NewRequest(http.MethodGet, viper.GetString("kavach_url")+fmt.Sprintf("/util/application/%s", appSlug), nil) + if err != nil { + return 0, err + } + + req.Header.Set("X-User", fmt.Sprintf("%d", userID)) + client := httpx.CustomHttpClient() + response, err := client.Do(req) + if err != nil { + return 0, err + } + + defer response.Body.Close() + responseBody := map[string]interface{}{} + err = json.NewDecoder(response.Body).Decode(&responseBody) + if err != nil { + return 0, err + } + + if response.StatusCode != 200 { + return 0, errors.New("internal server error on kavach while getting application id from space id") + } + appID := int(responseBody["application_id"].(float64)) + return uint(appID), nil +} diff --git a/server/util/description.go b/server/util/description.go deleted file mode 100644 index 7268a097f..000000000 --- a/server/util/description.go +++ /dev/null @@ -1,21 +0,0 @@ -package util - -import ( - "encoding/json" - - "github.com/factly/x/editorx" - "github.com/jinzhu/gorm/dialects/postgres" -) - -func HTMLDescription(jsonData postgres.Jsonb) (string, error) { - editorjsBlocks := make(map[string]interface{}) - err := json.Unmarshal(jsonData.RawMessage, &editorjsBlocks) - if err != nil { - return "", err - } - description, err := editorx.EditorjsToHTML(editorjsBlocks) - if err != nil { - return "", err - } - return description, nil -} diff --git a/server/util/fact_check_permission.go b/server/util/fact_check_permission.go index 18d9b88a2..77a12720a 100644 --- a/server/util/fact_check_permission.go +++ b/server/util/fact_check_permission.go @@ -18,7 +18,6 @@ func FactCheckPermission(h http.Handler) http.Handler { w.WriteHeader(http.StatusUnauthorized) return } - permission := model.SpacePermission{} err = config.DB.Model(&model.SpacePermission{}).Where(&model.SpacePermission{ SpaceID: uint(sID), diff --git a/server/util/getPermission.go b/server/util/getPermission.go new file mode 100644 index 000000000..8b70e5566 --- /dev/null +++ b/server/util/getPermission.go @@ -0,0 +1,142 @@ +package util + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/factly/dega-server/service/core/model" + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/loggerx" + "github.com/spf13/viper" +) + +type RelationTuplesWithSubjectID struct { + Tuples []TupleWithSubjectID `json:"relation_tuples"` +} + +type RelationTuplesWithSubjectSet struct { + Tuples []TupleWithSubjectSet `json:"relation_tuples"` +} + +type SubjectSet struct { + Namespace string `json:"namespace"` + Object string `json:"object"` + Relation string `json:"relation"` +} +type TupleWithSubjectID struct { + SubjectSet + SubjectID string `json:"subject_id"` +} + +type TupleWithSubjectSet struct { + SubjectSet + TuplesSubjectSet SubjectSet `json:"subject_set"` +} + +const namespace string = "spaces" + +func GetPermissions(orgID, appID, spaceID, uID uint) ([]model.Permission, error) { + client := httpx.CustomHttpClient() + roleObject := fmt.Sprintf("roles:org:%d:app:%d:space:%d", orgID, appID, spaceID) + requestURL := viper.GetString("keto_url") + "/relation-tuples?namespace=spaces&" + fmt.Sprintf("subject_id=%d&object=%s", uID, roleObject) + req, err := http.NewRequest("GET", requestURL, nil) + if err != nil { + return nil, err + } + + response, err := client.Do(req) + if err != nil { + return nil, err + } + + if response.StatusCode != 200 { + return nil, err + } + + tuples := RelationTuplesWithSubjectID{} + err = json.NewDecoder(response.Body).Decode(&tuples) + if err != nil { + return nil, err + } + var permissions []model.Permission + var ketoResponse = make(map[string][]string) + for _, tuple := range tuples.Tuples { + if strings.HasPrefix(tuple.Object, "roles") { + baseURL, err := url.Parse(viper.GetString("keto_url")) + if err != nil { + return nil, err + } + + //adding the path + baseURL.Path += "relation-tuples" + params := url.Values{} + params.Add("namespace", namespace) + params.Add("subject_set.namespace", namespace) + params.Add("subject_set.object", roleObject) + params.Add("subject_set.relation", tuple.Relation) + baseURL.RawQuery = params.Encode() + reqUrl := baseURL.String() + req, err = http.NewRequest("GET", reqUrl, nil) + if err != nil { + return nil, err + } + response, err = client.Do(req) + if err != nil { + return nil, err + } + + if response.StatusCode != 200 { + continue + } + + resourceTuples := RelationTuplesWithSubjectSet{} + err = json.NewDecoder(response.Body).Decode(&resourceTuples) + if err != nil { + return nil, err + } + + for _, resourceTuple := range resourceTuples.Tuples { + splittedObject := strings.Split(resourceTuple.Object, ":") + var entity string + if len(splittedObject) > 0 { + entity = splittedObject[len(splittedObject)-1] + } + actions, ok := ketoResponse[entity] + if ok { + if !contains(actions, resourceTuple.Relation) { + ketoResponse[entity] = append(actions, resourceTuple.Relation) + } + } else { + ketoResponse[entity] = append([]string{}, resourceTuple.Relation) + } + } + } + } + + _, err = json.Marshal(ketoResponse) + if err != nil { + loggerx.Error(err) + return nil, err + } + + for resource, action := range ketoResponse { + var permission model.Permission + permission.Resource = resource + permission.Actions = action + permissions = append(permissions, permission) + } + + return permissions, nil +} + +func contains(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} diff --git a/server/util/getservices.go b/server/util/getservices.go index 5382fce8a..22587c61a 100644 --- a/server/util/getservices.go +++ b/server/util/getservices.go @@ -4,16 +4,29 @@ import ( "github.com/factly/dega-server/config" "github.com/factly/dega-server/service/core/model" "github.com/factly/x/loggerx" + "github.com/factly/x/middlewarex" + "github.com/spf13/viper" ) -func GetAllowedServices(spaceID uint) ([]string, error) { +func GetAllowedServices(spaceID, orgID, userID uint) ([]string, error) { + superOrgID, err := middlewarex.GetSuperOrganisationID("Kavach") + if err != nil { + return nil, err + } + err = createSpacePermissionIfDoesNotExist(spaceID, superOrgID == int(orgID), userID) + if err != nil { + return nil, err + } + spacePermission := new(model.SpacePermission) - err := config.DB.Model(&model.SpacePermission{}).Where(&model.SpacePermission{ + err = config.DB.Model(&model.SpacePermission{}).Where(&model.SpacePermission{ SpaceID: spaceID, - }).First(spacePermission).Error + }).Find(spacePermission).Error if err != nil { - loggerx.Error(err) - return nil, err + if !(err.Error() == "record not found") { + loggerx.Error(err) + return nil, err + } } var serviceList []string @@ -27,3 +40,52 @@ func GetAllowedServices(spaceID uint) ([]string, error) { return serviceList, nil } + +func createSpacePermissionIfDoesNotExist(spaceID uint, isSuperOrg bool, userID uint) error { + var count int64 + err := config.DB.Model(&model.SpacePermission{}).Where(&model.SpacePermission{ + SpaceID: spaceID, + }).Count(&count).Error + if err != nil { + return err + } + + if count == 1 { + return nil + } + + if count == 0 { + var spacePermission model.SpacePermission + if isSuperOrg { + spacePermission = model.SpacePermission{ + Base: config.Base{ + CreatedByID: userID, + }, + SpaceID: spaceID, + Media: -1, + Posts: -1, + Podcast: true, + Episodes: -1, + FactCheck: true, + Videos: -1, + } + } else { + spacePermission = model.SpacePermission{ + Base: config.Base{ + CreatedByID: userID, + }, + SpaceID: spaceID, + Media: viper.GetInt64("default_number_of_media"), + Posts: viper.GetInt64("default_number_of_posts"), + Episodes: viper.GetInt64("default_number_of_episodes"), + Videos: viper.GetInt64("default_number_of_videos"), + Podcast: false, + FactCheck: false, + } + } + if err = config.DB.Model(&model.SpacePermission{}).Create(&spacePermission).Error; err != nil { + return err + } + } + return nil +} diff --git a/server/util/healthCheckers.go b/server/util/healthCheckers.go index 7aa9b58be..7cde5197b 100644 --- a/server/util/healthCheckers.go +++ b/server/util/healthCheckers.go @@ -4,6 +4,7 @@ import ( "errors" "net/http" + httpx "github.com/factly/dega-server/util/http" "github.com/spf13/viper" ) @@ -36,7 +37,7 @@ func GetRequest(url string) error { } req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() res, err := client.Do(req) if err != nil { return err diff --git a/server/util/http/client.go b/server/util/http/client.go new file mode 100644 index 000000000..d9d70ad9e --- /dev/null +++ b/server/util/http/client.go @@ -0,0 +1,12 @@ +package httpx + +import ( + "net/http" + "time" +) + +const httpTimeout = 10 + +func CustomHttpClient() *http.Client { + return &http.Client{Timeout: time.Minute * time.Duration(httpTimeout)} +} diff --git a/server/util/keto.go b/server/util/keto.go index fc6f38aa0..fb7080c72 100644 --- a/server/util/keto.go +++ b/server/util/keto.go @@ -3,6 +3,7 @@ package util import ( "net/http" + httpx "github.com/factly/dega-server/util/http" "github.com/spf13/viper" ) @@ -14,7 +15,7 @@ func KetoGetRequest(path string) (*http.Response, error) { } req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { return nil, err diff --git a/server/util/organisation.go b/server/util/organisation.go index 46c41c94a..0226158de 100644 --- a/server/util/organisation.go +++ b/server/util/organisation.go @@ -7,6 +7,7 @@ import ( "net/http" "github.com/factly/dega-server/service/core/model" + httpx "github.com/factly/dega-server/util/http" "github.com/spf13/viper" ) @@ -29,7 +30,7 @@ func GetAllOrganisationsMap(q string) (map[uint]model.Organisation, error) { } req.Header.Set("Content-Type", "application/json") - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { return nil, err @@ -72,7 +73,7 @@ func CheckOwnerFromKavach(uID, oID int) (bool, error) { req.Header.Set("Content-Type", "application/json") req.Header.Set("X-User", fmt.Sprint(uID)) - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) if err != nil { return false, err diff --git a/server/util/organisationID.go b/server/util/organisationID.go index a2b501bab..8059d690d 100644 --- a/server/util/organisationID.go +++ b/server/util/organisationID.go @@ -2,13 +2,19 @@ package util import ( "context" + "encoding/json" "errors" + "fmt" "net/http" "strings" - "github.com/factly/dega-server/config" - "github.com/factly/dega-server/service/core/model" + // "github.com/factly/dega-server/config" + + httpx "github.com/factly/dega-server/util/http" + "github.com/factly/x/errorx" + "github.com/factly/x/loggerx" "github.com/factly/x/middlewarex" + "github.com/spf13/viper" ) type ctxKeyOrganisationID int @@ -29,17 +35,22 @@ func GenerateOrganisation(h http.Handler) http.Handler { return } - space := &model.Space{} - space.ID = uint(sID) - - err = config.DB.First(&space).Error + userID, err := middlewarex.GetUser(r.Context()) + if err != nil { + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) + return + } + //** need to make change in x-package if space id is given organisation should be returned + organisationID, err := GetOrganisationIDfromSpaceID(uint(sID), uint(userID)) if err != nil { - w.WriteHeader(http.StatusUnauthorized) + loggerx.Error(err) + errorx.Render(w, errorx.Parser(errorx.InternalServerError())) return } - ctx = context.WithValue(ctx, OrganisationIDKey, space.OrganisationID) + ctx = context.WithValue(ctx, OrganisationIDKey, organisationID) h.ServeHTTP(w, r.WithContext(ctx)) return } @@ -58,3 +69,29 @@ func GetOrganisation(ctx context.Context) (int, error) { } return 0, errors.New("something went wrong") } + +func GetOrganisationIDfromSpaceID(spaceID, userID uint) (int, error) { + //** need to make change in x-package if space id is given organisation should be returned + req, err := http.NewRequest(http.MethodGet, viper.GetString("kavach_url")+fmt.Sprintf("/util/space/%d/getOrganisation", spaceID), nil) + if err != nil { + return 0, err + } + req.Header.Set("X-User", fmt.Sprintf("%d", userID)) + client := httpx.CustomHttpClient() + response, err := client.Do(req) + if err != nil { + return 0, err + } + defer response.Body.Close() + responseBody := map[string]interface{}{} + err = json.NewDecoder(response.Body).Decode(&responseBody) + if err != nil { + return 0, err + } + + if response.StatusCode != 200 { + return 0, errors.New("internal server error on kavach while getting organisation id from space id") + } + organisationID := int(responseBody["organisation_id"].(float64)) + return organisationID, nil +} diff --git a/server/util/policy.go b/server/util/policy.go index e7579ceb6..6017be3a7 100644 --- a/server/util/policy.go +++ b/server/util/policy.go @@ -7,15 +7,17 @@ import ( "fmt" "net/http" + httpx "github.com/factly/dega-server/util/http" "github.com/factly/x/middlewarex" "github.com/spf13/viper" ) // KetoAllowed is request object to check permissions of user type KetoAllowed struct { - Subject string `json:"subject"` - Action string `json:"action"` - Resource string `json:"resource"` + Subject string `json:"subject"` + Action string `json:"action"` + Resource string `json:"resource"` + SubjectType string `json:"subject_type"` } // CheckKetoPolicy returns middleware that checks the permissions of user from keto server @@ -36,81 +38,86 @@ func CheckKetoPolicy(entity, action string) func(h http.Handler) http.Handler { return } oID, err := GetOrganisation(ctx) - if err != nil { w.WriteHeader(http.StatusUnauthorized) return } - commonString := fmt.Sprint(":org:", oID, ":app:dega:space:", sID, ":") - - kresource := fmt.Sprint("resources", commonString, entity) - kaction := fmt.Sprint("actions", commonString, entity, ":", action) - - result := KetoAllowed{} - - result.Action = kaction - result.Resource = kresource - result.Subject = fmt.Sprint(uID) - - resStatus, err := IsAllowed(result) + resStatus, err := IsAllowed(entity, action, uint(oID), uint(sID), uint(uID)) if err != nil { w.WriteHeader(http.StatusUnauthorized) return } - if resStatus != 200 { + if resStatus != http.StatusOK { w.WriteHeader(http.StatusUnauthorized) return } - h.ServeHTTP(w, r) }) } } -// CheckSpaceKetoPermission checks keto policy for operations on space -func CheckSpaceKetoPermission(action string, oID, uID uint) error { - commonString := fmt.Sprint(":org:", oID, ":app:dega:spaces") - - kresource := fmt.Sprint("resources", commonString) - kaction := fmt.Sprint("actions", commonString, ":", action) - - result := KetoAllowed{} +func CheckAdmin(orgID, uID uint) (bool, error) { + requestBody := map[string]interface{}{ + "namespace": "organisations", + "object": fmt.Sprintf("org:%d", orgID), + "relation": "owner", + "subject_id": fmt.Sprintf("%d", uID), + } - result.Action = kaction - result.Resource = kresource - result.Subject = fmt.Sprint(uID) + buf := new(bytes.Buffer) + err := json.NewEncoder(buf).Encode(&requestBody) + if err != nil { + return false, err + } + req, err := http.NewRequest("POST", viper.GetString("keto_url")+"/relation-tuples/check", buf) + if err != nil { + return false, err + } - resStatus, err := IsAllowed(result) + client := httpx.CustomHttpClient() + response, err := client.Do(req) + if err != nil { + return false, err + } + defer response.Body.Close() + responseBody := make(map[string]interface{}) + err = json.NewDecoder(response.Body).Decode(&responseBody) if err != nil { - return err + return false, err } - if resStatus != 200 { - return errors.New("Permission not granted") + if !(response.StatusCode == 200 || response.StatusCode == 403) { + return false, errors.New("error in checking the authorization the relation tuple") } - return nil + return responseBody["allowed"].(bool), nil } // IsAllowed checks if keto policy allows user to action on resource -func IsAllowed(result KetoAllowed) (int, error) { - buf := new(bytes.Buffer) +func IsAllowed(entity, action string, orgID, spaceID, userID uint) (int, error) { + isAdmin, err := CheckAdmin(orgID, userID) + if err != nil { + return 0, err + } + if isAdmin { + return http.StatusOK, nil + } - err := json.NewEncoder(buf).Encode(&result) + applicationID, err := GetApplicationID(uint(userID), "dega") if err != nil { return 0, err } - req, err := http.NewRequest("POST", viper.GetString("keto_url")+"/engines/acp/ory/regex/allowed", buf) + object := fmt.Sprintf("resource:org:%d:app:%d:space:%d:%s", orgID, applicationID, spaceID, entity) + reqURL := viper.GetString("keto_url") + "/relation-tuples/check?" + fmt.Sprintf("namespace=%s&object=%s&relation=%s&subject_id=%d", namespace, object, action, userID) + req, err := http.NewRequest(http.MethodGet, reqURL, nil) if err != nil { return 0, err } req.Header.Set("Content-Type", "application/json") - - client := &http.Client{} + client := httpx.CustomHttpClient() resp, err := client.Do(req) - if err != nil { return 0, err } diff --git a/server/util/scooter.go b/server/util/scooter.go new file mode 100644 index 000000000..27a9391d8 --- /dev/null +++ b/server/util/scooter.go @@ -0,0 +1,30 @@ +package util + +import ( + "encoding/json" + + "github.com/jinzhu/gorm/dialects/postgres" +) + +type Description struct { + HTML string + JSON postgres.Jsonb +} + +func GetHTMLDescription(jsonData postgres.Jsonb) (string, error) { + var description Description + err := json.Unmarshal(jsonData.RawMessage, &description) + if err != nil { + return "", err + } + return description.HTML, nil +} + +func GetJSONDescription(jsonData postgres.Jsonb) (postgres.Jsonb, error) { + var description Description + err := json.Unmarshal(jsonData.RawMessage, &description) + if err != nil { + return postgres.Jsonb{}, err + } + return description.JSON, nil +} diff --git a/server/util/space.go b/server/util/space.go new file mode 100644 index 000000000..4da58485e --- /dev/null +++ b/server/util/space.go @@ -0,0 +1,55 @@ +package util + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/factly/dega-server/service/core/model" + httpx "github.com/factly/dega-server/util/http" + "github.com/spf13/viper" +) + +func GetSpacefromKavach(userID, orgID, spaceID uint) (*model.Space, error) { + applicationID, err := GetApplicationID(uint(userID), "dega") + if err != nil { + return nil, err + } + req, err := http.NewRequest("GET", viper.GetString("kavach_url")+fmt.Sprintf("/organisations/%d/applications/", orgID)+fmt.Sprintf("%d", applicationID)+"/spaces/"+fmt.Sprintf("%d", spaceID), nil) + if err != nil { + return nil, err + } + + req.Header.Set("X-User", fmt.Sprintf("%d", userID)) + req.Header.Set("Content-Type", "application/json") + + client := httpx.CustomHttpClient() + resp, err := client.Do(req) + if err != nil { + return nil, err + } + spaceObjectfromKavach := &model.KavachSpace{} + err = json.NewDecoder(resp.Body).Decode(spaceObjectfromKavach) + if err != nil { + return nil, err + } + + spaceObjectforDega := &model.Space{} + spaceObjectforDega.ID = spaceObjectfromKavach.ID + spaceObjectforDega.CreatedAt = spaceObjectfromKavach.CreatedAt + spaceObjectforDega.UpdatedAt = spaceObjectfromKavach.UpdatedAt + spaceObjectforDega.DeletedAt = spaceObjectfromKavach.DeletedAt + spaceObjectforDega.CreatedByID = spaceObjectfromKavach.CreatedByID + spaceObjectforDega.UpdatedByID = spaceObjectfromKavach.UpdatedByID + spaceObjectforDega.Name = spaceObjectfromKavach.Name + spaceObjectforDega.Slug = spaceObjectfromKavach.Slug + spaceObjectforDega.Description = spaceObjectfromKavach.Description + spaceObjectforDega.ApplicationID = spaceObjectfromKavach.ApplicationID + spaceObjectforDega.OrganisationID = int(spaceObjectfromKavach.OrganisationID) + err = json.Unmarshal(spaceObjectfromKavach.Metadata.RawMessage, &spaceObjectforDega) + if err != nil { + return nil, err + + } + return spaceObjectforDega, nil +} diff --git a/studio/.npmrc b/studio/.npmrc new file mode 100644 index 000000000..521a9f7c0 --- /dev/null +++ b/studio/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true diff --git a/studio/package-lock.json b/studio/package-lock.json index 7ccd30bb4..4ec03bc5b 100644 --- a/studio/package-lock.json +++ b/studio/package-lock.json @@ -17,6 +17,7 @@ "@editorjs/quote": "^2.3.0", "@editorjs/raw": "^2.1.2", "@editorjs/table": "^1.2.2", + "@factly/scooter": "^0.0.7", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", @@ -2372,6 +2373,15 @@ "resolved": "https://registry.npmjs.org/@editorjs/table/-/table-1.3.0.tgz", "integrity": "sha512-3/gr7IkjsYuECndCpcBSKzZQHos7C9QU1ach8avyVbXB3VLAa1RyGjgocu0Ct4d02HdxirrYZ7+vP1Na+s04GQ==" }, + "node_modules/@factly/scooter": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@factly/scooter/-/scooter-0.0.7.tgz", + "integrity": "sha512-qkK7me6DuQUBMBBzGOq5xrFQdR8qae6r3S+QMFIoDCuzBZHRxgcTRpW3VQmHh+q6hQvn3uEY6WBtUiwgUHxjoA==", + "peerDependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, "node_modules/@hapi/address": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", @@ -11423,11 +11433,22 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "engines": { "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, "node_modules/for-in": { @@ -16015,6 +16036,10 @@ "dependencies": { "@babel/runtime": "^7.12.1", "tiny-warning": "^1.0.3" + }, + "peerDependencies": { + "prop-types": "^15.0.0", + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/mini-css-extract-plugin": { @@ -16314,9 +16339,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -20093,7 +20118,7 @@ "node_modules/path-to-regexp/node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" }, "node_modules/path-type": { "version": "3.0.0", @@ -22599,11 +22624,11 @@ } }, "node_modules/react-router": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", - "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.1.tgz", + "integrity": "sha512-v+zwjqb7bakqgF+wMVKlAPTca/cEmPOvQ9zt7gpSNyPXau1+0qvuYZ5BWzzNDP1y6s15zDwgb9rPN63+SIniRQ==", "dependencies": { - "@babel/runtime": "^7.1.2", + "@babel/runtime": "^7.12.13", "history": "^4.9.0", "hoist-non-react-statics": "^3.1.0", "loose-envify": "^1.3.1", @@ -22613,20 +22638,26 @@ "react-is": "^16.6.0", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" } }, "node_modules/react-router-dom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", - "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.1.tgz", + "integrity": "sha512-f0pj/gMAbv9e8gahTmCEY20oFhxhrmHwYeIwH5EO5xu0qme+wXtsdB8YfUOAZzUz4VaXmb58m3ceiLtjMhqYmQ==", "dependencies": { - "@babel/runtime": "^7.1.2", + "@babel/runtime": "^7.12.13", "history": "^4.9.0", "loose-envify": "^1.3.1", "prop-types": "^15.6.2", - "react-router": "5.2.0", + "react-router": "5.3.1", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" } }, "node_modules/react-scripts": { @@ -25918,9 +25949,9 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, "node_modules/tiny-invariant": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", - "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", + "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" }, "node_modules/tiny-warning": { "version": "1.0.3", @@ -29541,6 +29572,11 @@ "resolved": "https://registry.npmjs.org/@editorjs/table/-/table-1.3.0.tgz", "integrity": "sha512-3/gr7IkjsYuECndCpcBSKzZQHos7C9QU1ach8avyVbXB3VLAa1RyGjgocu0Ct4d02HdxirrYZ7+vP1Na+s04GQ==" }, + "@factly/scooter": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@factly/scooter/-/scooter-0.0.7.tgz", + "integrity": "sha512-qkK7me6DuQUBMBBzGOq5xrFQdR8qae6r3S+QMFIoDCuzBZHRxgcTRpW3VQmHh+q6hQvn3uEY6WBtUiwgUHxjoA==" + }, "@hapi/address": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", @@ -37114,9 +37150,9 @@ } }, "follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" }, "for-in": { "version": "1.0.2", @@ -41051,9 +41087,9 @@ "optional": true }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, "nanomatch": { "version": "1.2.13", @@ -43867,7 +43903,7 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" } } }, @@ -45979,11 +46015,11 @@ } }, "react-router": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", - "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.1.tgz", + "integrity": "sha512-v+zwjqb7bakqgF+wMVKlAPTca/cEmPOvQ9zt7gpSNyPXau1+0qvuYZ5BWzzNDP1y6s15zDwgb9rPN63+SIniRQ==", "requires": { - "@babel/runtime": "^7.1.2", + "@babel/runtime": "^7.12.13", "history": "^4.9.0", "hoist-non-react-statics": "^3.1.0", "loose-envify": "^1.3.1", @@ -45996,15 +46032,15 @@ } }, "react-router-dom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", - "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.1.tgz", + "integrity": "sha512-f0pj/gMAbv9e8gahTmCEY20oFhxhrmHwYeIwH5EO5xu0qme+wXtsdB8YfUOAZzUz4VaXmb58m3ceiLtjMhqYmQ==", "requires": { - "@babel/runtime": "^7.1.2", + "@babel/runtime": "^7.12.13", "history": "^4.9.0", "loose-envify": "^1.3.1", "prop-types": "^15.6.2", - "react-router": "5.2.0", + "react-router": "5.3.1", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" } @@ -46098,8 +46134,7 @@ "react-side-effect": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz", - "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==", - "requires": {} + "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==" }, "react-test-renderer": { "version": "16.14.0", @@ -48709,9 +48744,9 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, "tiny-invariant": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", - "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", + "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" }, "tiny-warning": { "version": "1.0.3", diff --git a/studio/package.json b/studio/package.json index ab6022149..37afaabd3 100644 --- a/studio/package.json +++ b/studio/package.json @@ -16,6 +16,7 @@ "@editorjs/quote": "^2.3.0", "@editorjs/raw": "^2.1.2", "@editorjs/table": "^1.2.2", + "@factly/scooter": "^0.0.7", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", diff --git a/studio/public/config.js b/studio/public/config.js index 7f685ccd9..af754abb5 100644 --- a/studio/public/config.js +++ b/studio/public/config.js @@ -4,3 +4,4 @@ window.REACT_APP_COMPANION_URL = 'http://127.0.0.1:3020'; window.PUBLIC_URL = 'http://127.0.0.1:4455/.factly/dega/studio'; window.REACT_APP_KAVACH_PUBLIC_URL = 'http://127.0.0.1:4455/.factly/kavach/web'; window.REACT_APP_SACH_API_URL = 'https://sach-server.factly.in'; +window.REACT_APP_IFRAMELY_URL = 'http://127.0.0.1:4455/.factly/dega/server/meta'; diff --git a/studio/src/App.js b/studio/src/App.js index dd3b49973..9d8fddfc5 100644 --- a/studio/src/App.js +++ b/studio/src/App.js @@ -1,6 +1,6 @@ import React from 'react'; import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom'; -import 'antd/dist/antd.less'; +import 'antd/dist/antd.css'; import BasicLayout from './layouts/basic'; //Routes import routes from './config/routesConfig'; diff --git a/studio/src/actions/roles.js b/studio/src/actions/roles.js new file mode 100644 index 000000000..3b8c9e4f6 --- /dev/null +++ b/studio/src/actions/roles.js @@ -0,0 +1,172 @@ +import axios from 'axios'; +import { + ADD_ROLES, + ADD_ROLES_REQUEST, + SET_ROLES_LOADING, + RESET_ROLES, + ROLES_API, + UPDATE_ROLE, + GET_ROLE, +} from '../constants/roles'; +import { addErrorNotification, addSuccessNotification } from './notifications'; +import getError from '../utils/getError'; + +// action to fetch all roles +export const getRoles = (query) => { + return (dispatch, getState) => { + const currentSpaceID = getState().spaces?.selected; + dispatch(loadingRoles()); + return axios + .get(ROLES_API(currentSpaceID)) + .then((response) => { + dispatch(addRoles(response.data)); + dispatch( + addRolesRequest({ + data: response.data.map((item) => item.id), + query: query, + total: response.data.total, + }), + ); + }) + .catch((error) => { + dispatch(addErrorNotification(getError(error))); + }) + .finally(() => dispatch(stopRolesLoading())); + }; +}; + +// action to fetch role by id +export const getRole = (id) => { + return (dispatch, getState) => { + const currentSpaceID = getState().spaces?.selected; + dispatch(loadingRoles()); + return axios + .get(ROLES_API(currentSpaceID) + '/' + id) + .then((response) => { + dispatch(addRole(GET_ROLE, response.data)); + }) + .catch((error) => { + dispatch(addErrorNotification(getError(error))); + }) + .finally(() => dispatch(stopRolesLoading())); + }; +}; + +// action to create role +export const createRole = (data) => { + return (dispatch, getState) => { + const currentSpaceID = getState().spaces?.selected; + dispatch(loadingRoles()); + return axios + .post(ROLES_API(currentSpaceID), data) + .then(() => { + dispatch(resetRoles()); + dispatch(addSuccessNotification('Role created')); + }) + .catch((error) => { + dispatch(addErrorNotification(getError(error))); + }); + }; +}; + +// action to update role by id +export const updateRole = (data) => { + return (dispatch, getState) => { + const currentSpaceID = getState().spaces?.selected; + dispatch(loadingRoles()); + return axios + .put(ROLES_API(currentSpaceID) + '/' + data.id, data) + .then((response) => { + dispatch(addRole(UPDATE_ROLE, response.data)); + dispatch(addSuccessNotification('Roles updated')); + }) + .catch((error) => { + dispatch(addErrorNotification(getError(error))); + }) + .finally(() => dispatch(stopRolesLoading())); + }; +}; + +// action to delete role by id +export const deleteRole = (id) => { + return (dispatch, getState) => { + const currentSpaceID = getState().spaces?.selected; + dispatch(loadingRoles()); + return axios + .delete(ROLES_API(currentSpaceID) + '/' + id) + .then(() => { + dispatch(resetRoles()); + dispatch(addSuccessNotification('Roles deleted')); + }) + .catch((error) => { + dispatch(addErrorNotification(getError(error))); + }) + .finally(() => { + dispatch(stopRolesLoading()); + }); + }; +}; + +export const addRoleUser = (roleID, data) => { + return (dispatch, getState) => { + const currentSpaceID = getState().spaces?.selected; + dispatch(loadingRoles()); + return axios + .post(`${ROLES_API(currentSpaceID)}/${roleID}/users`, data) + .then(() => { + dispatch(addSuccessNotification('User Added Succesfully')); + }) + .catch((error) => { + dispatch(addErrorNotification(getError(error))); + }) + .finally(() => { + dispatch(stopRolesLoading()); + }); + }; +}; + +export const deleteRoleUser = (roleID, userID) => { + return (dispatch, getState) => { + const currentSpaceID = getState().spaces?.selected; + dispatch(loadingRoles()); + return axios + .delete(`${ROLES_API(currentSpaceID)}/${roleID}/users/${userID}`) + .then(() => { + dispatch(addSuccessNotification('User Deleted Succesfully')); + }) + .catch((error) => { + dispatch(addErrorNotification(getError(error))); + }) + .finally(() => { + dispatch(stopRolesLoading()); + }); + }; +}; +export const loadingRoles = () => ({ + type: SET_ROLES_LOADING, + payload: true, +}); + +export const stopRolesLoading = () => ({ + type: SET_ROLES_LOADING, + payload: false, +}); + +export const addRole = (type, payload) => ({ + type, + payload, +}); + +export const addRoles = (payload) => ({ + type: ADD_ROLES, + payload, +}); + +export const addRolesRequest = (payload) => ({ + type: ADD_ROLES_REQUEST, + payload, +}); + +export const resetRoles = () => ({ + type: RESET_ROLES, +}); diff --git a/studio/src/actions/spaces.js b/studio/src/actions/spaces.js index 67585178a..716ef327a 100644 --- a/studio/src/actions/spaces.js +++ b/studio/src/actions/spaces.js @@ -10,6 +10,7 @@ import { SET_SELECTED_SPACE, DELETE_SPACE_SUCCESS, UPDATE_SPACE_SUCCESS, + ADD_SPACE_USERS, } from '../constants/spaces'; import { addErrorNotification, addSuccessNotification } from './notifications'; import getError from '../utils/getError'; @@ -20,7 +21,11 @@ export const getSpaces = () => { return axios .get(API_GET_SPACES) .then((response) => { - dispatch(getSpacesSuccess(response.data)); + dispatch( + getSpacesSuccess( + response.data.filter((org) => org.applications !== null && org.spaces !== null), + ), + ); return response.data; }) .catch((error) => { @@ -84,6 +89,20 @@ export const updateSpace = (data) => { }; }; +export const getSpaceUsers = () => { + return (dispatch, getState) => { + const currentSpaceID = getState().spaces?.selected; + dispatch(loadingSpaces()); + return axios + .get(`/core/spaces/${currentSpaceID}/users`) + .then((response) => { + dispatch(addSpaceUsers(currentSpaceID, response.data)); + }) + .catch((error) => { + dispatch(addErrorNotification(getError(error))); + }); + }; +}; export const loadingSpaces = () => ({ type: LOADING_SPACES, }); @@ -107,3 +126,11 @@ export const deleteSpaceSuccess = (id) => ({ type: DELETE_SPACE_SUCCESS, payload: id, }); + +export const addSpaceUsers = (id, data) => ({ + type: ADD_SPACE_USERS, + payload: { + id, + data, + }, +}); diff --git a/studio/src/components/FormItems/DescriptionInput.js b/studio/src/components/FormItems/DescriptionInput.js index db265cd9d..730f08ceb 100644 --- a/studio/src/components/FormItems/DescriptionInput.js +++ b/studio/src/components/FormItems/DescriptionInput.js @@ -1,21 +1,110 @@ import React from 'react'; import { Form } from 'antd'; -import Editor from '../Editor'; +import axios from 'axios'; +// import Editor from '../Editor'; +import { Editor } from '@factly/scooter'; +import { MEDIA_API } from '../../constants/media'; +import { useSelector } from 'react-redux'; const DescriptionInput = ({ name = 'description', label = 'Description', noLabel = false, - onChange = () => {}, + onChange = (data) => console.log({ data }), inputProps, formItemProps, + initialValue, }) => { + const space_slug = useSelector((state) => { + return state.spaces.details[state.spaces.selected]?.slug; + }); + inputProps = { ...inputProps, onChange }; formItemProps = noLabel ? formItemProps : { ...formItemProps, label }; return ( - + {/* */} + console.log({ json, html })} + {...inputProps} + initialValue={initialValue} + uploadEndpoint={window.REACT_APP_COMPANION_URL} + iframelyEndpoint={window.REACT_APP_IFRAMELY_URL} + imagesFetcher={(currentPage) => + axios + .get(MEDIA_API, { + params: { page: currentPage, limit: 12 }, + }) + .then((res) => res.data) + } + onFileAdded={(file) => { + const data = file.data; + const url = data.thumbnail ? data.thumbnail : URL.createObjectURL(data); + const image = new Image(); + image.src = url; + image.onload = () => { + // uppy.setFileMeta(file.id, { width: image.width, height: image.height }); + URL.revokeObjectURL(url); + }; + image.onerror = () => { + URL.revokeObjectURL(url); + }; + }} + onUploadComplete={(result) => { + const successful = result.successful[0]; + const { meta } = successful; + const upload = {}; + upload['alt_text'] = meta.caption; + upload['caption'] = meta.caption; + upload['description'] = meta.caption; + upload['dimensions'] = `${meta.width}x${meta.height}`; + upload['file_size'] = successful.size; + upload['name'] = successful.fileName; + upload['slug'] = successful.response.body.key; + upload['title'] = meta.caption ? meta.caption : ' '; + upload['type'] = successful.meta.type; + upload['url'] = {}; + upload['url']['raw'] = successful.uploadURL; + + axios.post(MEDIA_API, [upload]).catch((error) => { + console.error(error); + }); + }} + uploadConfig={{ + restrictions: { + maxFileSize: 5242880, + allowedFileTypes: ['.jpg', '.jpeg', '.png', '.gif'], + }, + onBeforeUpload: (files) => { + const updatedFiles = {}; + + Object.keys(files).forEach((fileID) => { + updatedFiles[fileID] = { + ...files[fileID], + fileName: files[fileID].meta.name, + meta: { + ...files[fileID].meta, + name: + space_slug + + '/' + + new Date().getFullYear() + + '/' + + new Date().getMonth() + + '/' + + Date.now().toString() + + '_' + + files[fileID].meta.name, + }, + }; + }); + return updatedFiles; + }, + }} + /> ); }; diff --git a/studio/src/components/GlobalNav/Sidebar.js b/studio/src/components/GlobalNav/Sidebar.js index aef378727..ce9079e46 100644 --- a/studio/src/components/GlobalNav/Sidebar.js +++ b/studio/src/components/GlobalNav/Sidebar.js @@ -166,7 +166,7 @@ function Sidebar({ superOrg, permission, orgs, loading, applications, services, : permission.filter((each) => each.resource === 'admin').length > 0 ? getSubMenuItems(menu, index, Icon) : null - : services.includes(maker(menu.title)) + : services?.includes(maker(menu.title)) ? getSubMenuItems(menu, index, Icon) : null; })} diff --git a/studio/src/components/Selector/index.js b/studio/src/components/Selector/index.js index d43e6d292..e41dd20a2 100644 --- a/studio/src/components/Selector/index.js +++ b/studio/src/components/Selector/index.js @@ -127,7 +127,7 @@ function Selector({ e.target.scrollTop + e.target.offsetHeight === e.target.scrollHeight || e.target.scrollTop + e.target.offsetHeight >= e.target.scrollHeight - 16 ) { - if (details.length < total) { + if (details.length < total && Math.ceil(total/query.limit) >= query.page+1 ) { setQuery({ ...query, page: query.page + 1 }); } } diff --git a/studio/src/config/routesConfig.js b/studio/src/config/routesConfig.js index b7cdcbc7a..419859470 100644 --- a/studio/src/config/routesConfig.js +++ b/studio/src/config/routesConfig.js @@ -71,6 +71,10 @@ import Policies from '../pages/policies'; import CreatePolicy from '../pages/policies/CreatePolicy'; import EditPolicy from '../pages/policies/EditPolicy'; +//Roles +import Roles from '../pages/roles'; +import CreateRole from '../pages/roles/CreateRole'; + //Fact Checks import GoogleFactCheck from '../pages/fact-checks/GoogleFactCheck'; import Factly from '../pages/fact-checks/Factly'; @@ -140,6 +144,10 @@ import Permissions from '../pages/permissions'; //Reindex import Reindex from '../pages/spaces/Reindex'; +import { Component } from 'react'; +import ViewPolicy from '../pages/policies/components/ViewPolicy'; +import RoleUsers from '../pages/roles/users'; +import EditRole from '../pages/roles/EditRole'; const routes = { dashboard: { @@ -405,6 +413,24 @@ const routes = { Component: Policies, title: 'Policies', }, + roles: { + path: '/members/roles', + menuKey: '/members', + Component: Roles, + title: 'Roles', + }, + roleEdit: { + path: '/members/roles/:id/edit', + Component: EditRole, + menuKey: '/members/roles/edit', + title: 'Edit Role', + }, + roleUsers: { + path: '/members/roles/:roleID/users', + menuKey: '/members', + Component: RoleUsers, + title: 'Role Users', + }, createPolicy: { path: '/members/policies/create', menuKey: '/members', @@ -415,6 +441,22 @@ const routes = { action: 'create', }, }, + ViewPolicy: { + path: '/members/policies/:policyID/view', + menuKey: '/members', + Component: ViewPolicy, + title: 'Policy', + }, + createRole: { + path: '/members/roles/create', + menuKey: '/members', + Component: CreateRole, + title: 'New Role', + // permission: { + // resource: 'policies', + // action: 'create', + // }, + }, editPolicy: { path: '/members/policies/:id/edit', menuKey: '/members', diff --git a/studio/src/constants/roles.js b/studio/src/constants/roles.js new file mode 100644 index 000000000..b8871db34 --- /dev/null +++ b/studio/src/constants/roles.js @@ -0,0 +1,10 @@ +//Actions +export const GET_ROLE = 'GET_ROLE'; +export const UPDATE_ROLE = 'UPDATE_ROLE'; +export const ADD_ROLES = 'ADD_ROLES'; +export const ADD_ROLES_REQUEST = 'ADD_ROLES_REQUEST'; +export const RESET_ROLES = 'RESET_ROLES'; +export const SET_ROLES_LOADING = 'SET_ROLES_LOADING'; + +//API +export const ROLES_API = (selectedSpace) => `core/spaces/${selectedSpace}/roles`; diff --git a/studio/src/constants/spaces.js b/studio/src/constants/spaces.js index ebba0dc53..df7669d7e 100644 --- a/studio/src/constants/spaces.js +++ b/studio/src/constants/spaces.js @@ -9,7 +9,7 @@ export const ADD_SPACE_SUCCESS = 'ADD_SPACE_SUCCESS'; export const ADD_SPACE_FAILURE = 'ADD_SPACE_SUCCESS'; export const DELETE_SPACE_SUCCESS = 'DELETE_SPACE_SUCCESS'; export const DELETE_SPACE_FAILURE = 'DELETE_SPACE_SUCCESS'; - +export const ADD_SPACE_USERS = 'ADD_SPACE_USERS'; //API export const API_GET_SPACES = '/core/spaces'; export const API_ADD_SPACE = '/core/spaces'; diff --git a/studio/src/layouts/basic.js b/studio/src/layouts/basic.js index 67a33cb4f..59f792d3f 100644 --- a/studio/src/layouts/basic.js +++ b/studio/src/layouts/basic.js @@ -78,9 +78,11 @@ function BasicLayout(props) { }); React.useEffect(() => { - dispatch(getSpaces()).then((org) => { - if (org && org.length > 0) dispatch(getSuperOrganisation(org[0].id)); - }); + dispatch(getSpaces()); + // .then((org) => { + // if (org && org.length > 0) dispatch(getSuperOrganisation(org[0].id)); + // } + // ); }, [dispatch, selected]); React.useEffect(() => { diff --git a/studio/src/pages/categories/components/CategoryForm.js b/studio/src/pages/categories/components/CategoryForm.js index 2f5f0cdf7..16bf14ed1 100644 --- a/studio/src/pages/categories/components/CategoryForm.js +++ b/studio/src/pages/categories/components/CategoryForm.js @@ -13,7 +13,7 @@ const CategoryForm = ({ onCreate, data = {} }) => { const siteAddress = useSelector( ({ spaces: { details, selected } }) => details[selected].site_address, ); - const setLoading = onCreate.name === 'onCreate' ? true : false; + const setLoading = data?.id ? false : true; if (data && data.meta_fields) { if (typeof data.meta_fields !== 'string') { data.meta_fields = JSON.stringify(data.meta_fields); @@ -198,6 +198,7 @@ const CategoryForm = ({ onCreate, data = {} }) => { placeholder: 'Enter Description...', basic: true, }} + initialValue={formData.description} /> diff --git a/studio/src/pages/claimants/components/ClaimantForm.js b/studio/src/pages/claimants/components/ClaimantForm.js index 51e62a0b6..ce0b298ab 100644 --- a/studio/src/pages/claimants/components/ClaimantForm.js +++ b/studio/src/pages/claimants/components/ClaimantForm.js @@ -90,6 +90,7 @@ const ClaimantForm = ({ onCreate, data = {} }) => { placeholder: 'Enter Description...', basic: true, }} + initialValue={data.description} /> diff --git a/studio/src/pages/claims/CreateClaim.js b/studio/src/pages/claims/CreateClaim.js index e698d743e..4f0d06350 100644 --- a/studio/src/pages/claims/CreateClaim.js +++ b/studio/src/pages/claims/CreateClaim.js @@ -1,9 +1,10 @@ import React from 'react'; import ClaimCreateForm from './components/ClaimForm'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { createClaim } from '../../actions/claims'; -import { useHistory } from 'react-router-dom'; +import { Link, useHistory } from 'react-router-dom'; import { Helmet } from 'react-helmet'; +import { Button, Result } from 'antd'; function CreateClaim() { const history = useHistory(); @@ -12,10 +13,33 @@ function CreateClaim() { const onCreate = (values) => { dispatch(createClaim(values)).then(() => history.push('/claims')); }; + + const { claimantsCount } = useSelector(({ claimants }) => { + return { + claimantsCount: (claimants?.req?.[0]?.data) ? claimants?.req?.[0]?.data : 0 + } + }) + return ( <> - + { + claimantsCount ? ( + + ) + : ( + + + + } + /> + ) + } ); } diff --git a/studio/src/pages/claims/components/ClaimForm.js b/studio/src/pages/claims/components/ClaimForm.js index e4f90395c..449225837 100644 --- a/studio/src/pages/claims/components/ClaimForm.js +++ b/studio/src/pages/claims/components/ClaimForm.js @@ -187,7 +187,10 @@ const ClaimForm = ({ onCreate, data = {} }) => { - + diff --git a/studio/src/pages/dashboard/components/Features.js b/studio/src/pages/dashboard/components/Features.js index 1c64353fb..2e3223af3 100644 --- a/studio/src/pages/dashboard/components/Features.js +++ b/studio/src/pages/dashboard/components/Features.js @@ -16,26 +16,6 @@ function Features() { return admin.organisation; }); - React.useEffect(() => { - fetchEntities(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - const fetchEntities = () => { - dispatch(getRatings()); - dispatch(getFormats()); - dispatch(getPolicies()); - }; - - React.useEffect(() => { - if (superOrg.is_admin) { - fetchEvents(); - } - }, [superOrg.is_admin]); - - const fetchEvents = () => { - dispatch(getEvents()); - }; - const { ratings, ratingsLoading, @@ -45,7 +25,9 @@ function Features() { policiesLoading, events, eventsLoading, - } = useSelector(({ ratings, formats, policies, events }) => { + validServices, + loadingServices, + } = useSelector(({ ratings, formats, policies, events, spaces }) => { return { ratings: Object.keys(ratings.details).length, ratingsLoading: ratings.loading, @@ -55,9 +37,35 @@ function Features() { policiesLoading: policies.loading, events: Object.keys(events.details).length, eventsLoading: events.loading, + validServices: spaces.details[spaces.selected].services?.length + ? spaces.details[spaces.selected].services + : [], + loadingServices: spaces.loading, }; }); + React.useEffect(() => { + fetchEntities(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const fetchEntities = () => { + if (!loadingServices && validServices?.includes('fact-checking')) { + dispatch(getRatings()); + } + dispatch(getFormats()); + dispatch(getPolicies()); + }; + + React.useEffect(() => { + if (superOrg.is_admin) { + fetchEvents(); + } + }, [superOrg.is_admin]); + + const fetchEvents = () => { + dispatch(getEvents()); + }; + return ( <> {!ratingsLoading && @@ -68,7 +76,8 @@ function Features() { ) : null} - {ratingsLoading ? null : ratings > 0 ? null : ( + {ratingsLoading && loadingServices ? null : ratings > 0 || + !validServices.includes('fact-checking') ? null : ( { diff --git a/studio/src/pages/fact-checks/components/FactCheckForm.js b/studio/src/pages/fact-checks/components/FactCheckForm.js index 5538e70b0..22410e47a 100644 --- a/studio/src/pages/fact-checks/components/FactCheckForm.js +++ b/studio/src/pages/fact-checks/components/FactCheckForm.js @@ -315,7 +315,11 @@ function FactCheckForm({ onCreate, data = {}, actions = {}, format }) { /> ) : null} - + Post Settings} placement="right" diff --git a/studio/src/pages/members/index.js b/studio/src/pages/members/index.js index a136c1a6b..781f7d0f8 100644 --- a/studio/src/pages/members/index.js +++ b/studio/src/pages/members/index.js @@ -1,6 +1,6 @@ import React from 'react'; import { Card, Avatar, Row, Col } from 'antd'; -import { UserOutlined, EyeTwoTone } from '@ant-design/icons'; +import { UserOutlined, EyeTwoTone, UsergroupAddOutlined } from '@ant-design/icons'; import { Link } from 'react-router-dom'; import { Helmet } from 'react-helmet'; const Members = () => { @@ -44,6 +44,22 @@ const Members = () => { + + + + } + style={{ backgroundColor: 'transparent', color: '#ffb41f' }} + /> + } + title="Roles" + description="Role settings" + /> + + + ); diff --git a/studio/src/pages/podcasts/components/PodcastForm.js b/studio/src/pages/podcasts/components/PodcastForm.js index b63179ad8..01a72bf2b 100644 --- a/studio/src/pages/podcasts/components/PodcastForm.js +++ b/studio/src/pages/podcasts/components/PodcastForm.js @@ -80,6 +80,7 @@ const PodcastForm = ({ onCreate, data = {} }) => { diff --git a/studio/src/pages/policies/CreatePolicy.js b/studio/src/pages/policies/CreatePolicy.js index 542d4d323..eefcc9328 100644 --- a/studio/src/pages/policies/CreatePolicy.js +++ b/studio/src/pages/policies/CreatePolicy.js @@ -10,8 +10,7 @@ function CreatePolicy() { const dispatch = useDispatch(); const onCreate = (values) => { - values.users = values.users.map((item) => item.toString()); - values.permissions = values.permissions.filter( + values.permissions = values.permissions?.filter( (item) => item && item.resource && item.actions.length > 0, ); dispatch(createPolicy(values)).then(() => history.push('/members/policies')); diff --git a/studio/src/pages/policies/EditPolicy.js b/studio/src/pages/policies/EditPolicy.js index 5d1bc056b..38ac68706 100644 --- a/studio/src/pages/policies/EditPolicy.js +++ b/studio/src/pages/policies/EditPolicy.js @@ -28,8 +28,8 @@ function EditPolicy() { (obj, item) => Object.assign(obj, { [item.resource]: item.actions }), {}, ), - users: state.policies.details[id].users - ? state.policies.details[id].users.map((item) => parseInt(item.id)) + roles: state.policies.details[id].roles?.length + ? state.policies.details[id].roles.map((item) => item.id) : [], }, loading: state.policies.loading, @@ -47,14 +47,12 @@ function EditPolicy() { } const onUpdate = (values) => { - dispatch(updatePolicy({ ...policy, ...values })).then(() => - history.push(`/members/policies/${id}/edit`), - ); + dispatch(updatePolicy({ ...policy, ...values })).then(() => history.push(`/members/policies/`)); }; return ( <> - + ); diff --git a/studio/src/pages/policies/components/PolicyForm.js b/studio/src/pages/policies/components/PolicyForm.js index 824757903..c5fd07505 100644 --- a/studio/src/pages/policies/components/PolicyForm.js +++ b/studio/src/pages/policies/components/PolicyForm.js @@ -105,7 +105,6 @@ function PolicyForm({ data = {}, onCreate }) { permissions: Object.keys(values.permissions) .filter((key) => values.permissions[key] && values.permissions[key].length > 0) .map((key) => ({ resource: key, actions: values.permissions[key] })), - users: values.users.map((item) => item.toString()), }) } onValuesChange={() => { @@ -120,8 +119,8 @@ function PolicyForm({ data = {}, onCreate }) { - - + + diff --git a/studio/src/pages/policies/components/PolicyList.js b/studio/src/pages/policies/components/PolicyList.js index 3cfc693c5..52289eef0 100644 --- a/studio/src/pages/policies/components/PolicyList.js +++ b/studio/src/pages/policies/components/PolicyList.js @@ -4,7 +4,7 @@ import { Popconfirm, Button, Typography, Table, Avatar, Tooltip } from 'antd'; import { useDispatch, useSelector } from 'react-redux'; import { deletePolicy } from '../../../actions/policies'; import { Link } from 'react-router-dom'; -import { DeleteOutlined } from '@ant-design/icons'; +import { DeleteOutlined, EyeOutlined } from '@ant-design/icons'; function PolicyList({ actions, data, filters, setFilters, fetchPolicies }) { const dispatch = useDispatch(); @@ -32,52 +32,56 @@ function PolicyList({ actions, data, filters, setFilters, fetchPolicies }) { title: 'Description', dataIndex: 'description', key: 'description', + width: '40%', render: (_, record) => { return ( {record.description} ); }, }, - { - title: 'Members', - dataIndex: 'members', - key: 'members', - width: '25%', - render: (_, record) => { - const members = record.users.map((user, index) => ( - - - {user.email.charAt(0).toUpperCase()} - - - )); - - return <>{members}; - }, - }, { title: 'Action', dataIndex: 'operation', fixed: 'right', align: 'center', - width: 150, + width: '40%', render: (_, record) => { return ( - dispatch(deletePolicy(record.id)).then(() => fetchPolicies())} - disabled={!(actions.includes('admin') || actions.includes('delete'))} +
- + + dispatch(deletePolicy(record.id)).then(() => fetchPolicies())} disabled={!(actions.includes('admin') || actions.includes('delete'))} - type="danger" - /> - + > + + +
); }, }, diff --git a/studio/src/pages/policies/components/ViewPolicy.js b/studio/src/pages/policies/components/ViewPolicy.js new file mode 100644 index 000000000..30bfc5c4f --- /dev/null +++ b/studio/src/pages/policies/components/ViewPolicy.js @@ -0,0 +1,84 @@ +import React from 'react'; +import { Descriptions, Tag, Table, Skeleton, Button, Divider } from 'antd'; +import { Link, useParams } from 'react-router-dom'; +import { useSelector } from 'react-redux'; + +export default function ViewPolicy() { + const { policyID } = useParams(); + const span = 2; + const nestedTableColumns = [ + { + title: 'Resource', + dataIndex: 'resource', + key: 'resource', + }, + { + title: 'Action', + dataIndex: 'action', + key: 'action', + render: (_, record) => { + return record.actions?.map((action) => { + return ( + + {action} + + ); + }); + }, + }, + ]; + + const { policy, loading } = useSelector((state) => { + return { + policy: state.policies.details?.[policyID], + loading: state.policies.loading, + }; + }); + + return ( +
+ + + +

Policy Details

+ {loading ? ( + + ) : ( + + + {policy?.name} + +
+ + {policy?.description} + +
+ + {policy?.roles.map((role) => { + return ( + + {role?.name} + + ); + })} + +
+ )} + +

Permissions

+ + + ); +} diff --git a/studio/src/pages/policies/index.js b/studio/src/pages/policies/index.js index 7389566a5..05621d782 100644 --- a/studio/src/pages/policies/index.js +++ b/studio/src/pages/policies/index.js @@ -58,7 +58,6 @@ function Policies() { - { placeholder: 'Enter Description...', basic: true, }} + initialValue={data.description} /> diff --git a/studio/src/pages/roles/CreateRole.js b/studio/src/pages/roles/CreateRole.js new file mode 100644 index 000000000..3fddf1429 --- /dev/null +++ b/studio/src/pages/roles/CreateRole.js @@ -0,0 +1,24 @@ +import React from 'react'; +import { createRole } from '../../actions/roles'; +import { useDispatch } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { Helmet } from 'react-helmet'; +import RoleCreateForm from './components/RoleForm'; + +function CreateRole() { + const history = useHistory(); + + const dispatch = useDispatch(); + const onCreate = (values) => { + dispatch(createRole(values)).then(() => history.push('/members/roles')); + }; + + return ( + <> + + + + ); +} + +export default CreateRole; diff --git a/studio/src/pages/roles/EditRole.js b/studio/src/pages/roles/EditRole.js new file mode 100644 index 000000000..46315e0ed --- /dev/null +++ b/studio/src/pages/roles/EditRole.js @@ -0,0 +1,35 @@ +import React from 'react'; +import { getRole, updateRole } from '../../actions/roles'; +import { useDispatch, useSelector } from 'react-redux'; +import { useHistory, useParams } from 'react-router-dom'; +import { Helmet } from 'react-helmet'; +import RoleEditForm from './components/RoleForm'; +import { Skeleton } from 'antd'; + +function EditRole() { + const history = useHistory(); + const { id } = useParams(); + const dispatch = useDispatch(); + const { role, loading } = useSelector((state) => { + return { + role: state.roles.details[id], + loading: state.roles.loading, + }; + }); + + React.useEffect(() => { + dispatch(getRole(id)); + }, [dispatch, id]); + const onCreate = (values) => { + dispatch(updateRole({ ...role, ...values })).then(() => history.push('/members/roles')); + }; + + return ( + <> + + {loading ? : } + + ); +} + +export default EditRole; diff --git a/studio/src/pages/roles/components/RoleForm.js b/studio/src/pages/roles/components/RoleForm.js new file mode 100644 index 000000000..ba0a85993 --- /dev/null +++ b/studio/src/pages/roles/components/RoleForm.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { Button, Card, Form, Input, Skeleton } from 'antd'; +import { SlugInput } from '../../../components/FormItems'; +import { maker } from '../../../utils/sluger'; + +function RoleForm({ data = {}, onCreate }) { + const [form] = Form.useForm(); + + const { TextArea } = Input; + const onTitleChange = (string) => { + form.setFieldsValue({ + slug: maker(string), + }); + }; + return ( + +
{ + onCreate(values); + }} + initialValues={data} + > + + onTitleChange(e.target.value)} placeholder="enter the name" /> + + + +