New routes
This commit is contained in:
parent
659b1eb2eb
commit
b1543c9e22
17
enshi_back/ABAC/AdminPolicies/AdminPolicy.go
Normal file
17
enshi_back/ABAC/AdminPolicies/AdminPolicy.go
Normal file
@ -0,0 +1,17 @@
|
||||
package adminpolicies
|
||||
|
||||
import (
|
||||
globalrules "enshi/ABAC/GlobalRules"
|
||||
"enshi/ABAC/rules"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func AdminPolicies(c *gin.Context) (bool, []error) {
|
||||
rulesToCheck := []rules.RuleFunction{
|
||||
globalrules.AuthorizedRule,
|
||||
globalrules.IsAdminRule,
|
||||
}
|
||||
|
||||
return rules.CheckRules(c, rulesToCheck, rules.ALL_RULES_MUST_BE_COMPLETED)
|
||||
}
|
||||
@ -8,10 +8,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DELETE_POST = "delete_post"
|
||||
UPDATE_POST = "update_post"
|
||||
CREATE_POST = "create_post"
|
||||
GET_POST = "get_post"
|
||||
DELETE_POST = "delete_post"
|
||||
UPDATE_POST = "update_post"
|
||||
UPDATE_POST_BLOG = "update_post_blog"
|
||||
CREATE_POST = "create_post"
|
||||
GET_POST = "get_post"
|
||||
)
|
||||
|
||||
func PostsPolicies(c *gin.Context) (bool, []error) {
|
||||
@ -28,6 +29,9 @@ func PostsPolicies(c *gin.Context) (bool, []error) {
|
||||
case UPDATE_POST:
|
||||
return rules.CheckRule(c, postRules.PostUpdateRule)
|
||||
|
||||
case UPDATE_POST_BLOG:
|
||||
return rules.CheckRule(c, postRules.UpdatePostBlogRule)
|
||||
|
||||
case GET_POST:
|
||||
return rules.CheckRule(c, postRules.PostReadRule)
|
||||
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package postRules
|
||||
|
||||
import (
|
||||
globalrules "enshi/ABAC/GlobalRules"
|
||||
"enshi/ABAC/rules"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Only user that own target post and blog can do that
|
||||
func UpdatePostBlogRule(c *gin.Context) (bool, []error) {
|
||||
rulesToCheck := []rules.RuleFunction{
|
||||
globalrules.AuthorizedRule,
|
||||
globalrules.IsOwnerOfThePostRule,
|
||||
globalrules.IsOwnerOfTheBlogRule,
|
||||
}
|
||||
|
||||
isAllowed, errors := rules.CheckRules(
|
||||
c,
|
||||
rulesToCheck,
|
||||
rules.ALL_RULES_MUST_BE_COMPLETED,
|
||||
)
|
||||
|
||||
return isAllowed, errors
|
||||
}
|
||||
@ -11,6 +11,7 @@ import (
|
||||
func PostUpdateRule(c *gin.Context) (bool, []error) {
|
||||
rulesToCheck := []rules.RuleFunction{
|
||||
globalrules.AuthorizedRule,
|
||||
globalrules.IsOwnerOfThePostRule,
|
||||
}
|
||||
|
||||
isAllowed, errors := rules.CheckRules(
|
||||
|
||||
28
enshi_back/ABAC/rules/ShouldAbortRequest.go
Normal file
28
enshi_back/ABAC/rules/ShouldAbortRequest.go
Normal file
@ -0,0 +1,28 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func ShouldAbortRequest(c *gin.Context, isAllowed bool, errors []error) bool {
|
||||
var errorsMap = map[int]string{}
|
||||
for i, error := range errors {
|
||||
errorsMap[i] = error.Error()
|
||||
}
|
||||
|
||||
if errors != nil {
|
||||
c.IndentedJSON(http.StatusUnauthorized, errorsMap)
|
||||
return true
|
||||
}
|
||||
|
||||
if !isAllowed {
|
||||
rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission"))
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@ -146,27 +146,38 @@ func (q *Queries) GetPostsByUserId(ctx context.Context, userID int64) ([]Post, e
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updatePostBlogId = `-- name: UpdatePostBlogId :exec
|
||||
UPDATE public.posts
|
||||
SET blog_id=$2, updated_at=CURRENT_TIMESTAMP
|
||||
WHERE post_id = $1
|
||||
RETURNING post_id, blog_id, user_id, title, content, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdatePostBlogIdParams struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
BlogID pgtype.Int8 `json:"blog_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdatePostBlogId(ctx context.Context, arg UpdatePostBlogIdParams) error {
|
||||
_, err := q.db.Exec(ctx, updatePostBlogId, arg.PostID, arg.BlogID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updatePostByPostId = `-- name: UpdatePostByPostId :one
|
||||
UPDATE public.posts
|
||||
SET blog_id=$1, title=$2, "content"=$3, updated_at=CURRENT_TIMESTAMP
|
||||
WHERE post_id = $4
|
||||
SET title=$1, "content"=$2, updated_at=CURRENT_TIMESTAMP
|
||||
WHERE post_id = $3
|
||||
RETURNING post_id, blog_id, user_id, title, content, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdatePostByPostIdParams struct {
|
||||
BlogID pgtype.Int8 `json:"blog_id"`
|
||||
Title pgtype.Text `json:"title"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
PostID int64 `json:"post_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdatePostByPostId(ctx context.Context, arg UpdatePostByPostIdParams) (Post, error) {
|
||||
row := q.db.QueryRow(ctx, updatePostByPostId,
|
||||
arg.BlogID,
|
||||
arg.Title,
|
||||
arg.Content,
|
||||
arg.PostID,
|
||||
)
|
||||
row := q.db.QueryRow(ctx, updatePostByPostId, arg.Title, arg.Content, arg.PostID)
|
||||
var i Post
|
||||
err := row.Scan(
|
||||
&i.PostID,
|
||||
|
||||
@ -21,10 +21,16 @@ RETURNING *;
|
||||
|
||||
-- name: UpdatePostByPostId :one
|
||||
UPDATE public.posts
|
||||
SET blog_id=$1, title=$2, "content"=$3, updated_at=CURRENT_TIMESTAMP
|
||||
WHERE post_id = $4
|
||||
SET title=$1, "content"=$2, updated_at=CURRENT_TIMESTAMP
|
||||
WHERE post_id = $3
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeletePostByPostId :exec
|
||||
DELETE FROM public.posts
|
||||
WHERE post_id=$1;
|
||||
WHERE post_id=$1;
|
||||
|
||||
-- name: UpdatePostBlogId :exec
|
||||
UPDATE public.posts
|
||||
SET blog_id=$2, updated_at=CURRENT_TIMESTAMP
|
||||
WHERE post_id = $1
|
||||
RETURNING *;
|
||||
|
||||
@ -2,9 +2,7 @@ package middleware
|
||||
|
||||
import (
|
||||
profilepolicies "enshi/ABAC/ProfilePolicies"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"enshi/ABAC/rules"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@ -18,19 +16,7 @@ func ProfileMiddleware() gin.HandlerFunc {
|
||||
|
||||
isAllowed, errors := profilepolicies.ProfilePolicies(c)
|
||||
|
||||
var errorsMap = map[int]string{}
|
||||
for i, error := range errors {
|
||||
errorsMap[i] = error.Error()
|
||||
}
|
||||
|
||||
if errors != nil {
|
||||
c.IndentedJSON(http.StatusUnauthorized, errorsMap)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if !isAllowed {
|
||||
rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission"))
|
||||
if rules.ShouldAbortRequest(c, isAllowed, errors) {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
@ -1,26 +1,19 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
"enshi/middleware/checkRole"
|
||||
"fmt"
|
||||
adminpolicies "enshi/ABAC/AdminPolicies"
|
||||
"enshi/ABAC/rules"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func AdminMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
isAllowed, errors := adminpolicies.AdminPolicies(c)
|
||||
|
||||
isAdmin, err := checkRole.IsAdmin(c)
|
||||
|
||||
if err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
if !isAdmin {
|
||||
rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("not allowed"))
|
||||
if rules.ShouldAbortRequest(c, isAllowed, errors) {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
|
||||
@ -2,9 +2,7 @@ package middleware
|
||||
|
||||
import (
|
||||
blogspolicies "enshi/ABAC/blogsPolicies"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"enshi/ABAC/rules"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@ -25,19 +23,7 @@ func BlogsMiddleware() gin.HandlerFunc {
|
||||
|
||||
isAllowed, errors := blogspolicies.BlogPolicies(c)
|
||||
|
||||
var errorsMap = map[int]string{}
|
||||
for i, error := range errors {
|
||||
errorsMap[i] = error.Error()
|
||||
}
|
||||
|
||||
if errors != nil {
|
||||
c.IndentedJSON(http.StatusUnauthorized, errorsMap)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if !isAllowed {
|
||||
rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission"))
|
||||
if rules.ShouldAbortRequest(c, isAllowed, errors) {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package utils
|
||||
package getters
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -2,12 +2,14 @@ package getters
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Returns -1, error if there is no such param or value invalid
|
||||
func GetInt64Param(c *gin.Context, paramName string) (int64, error) {
|
||||
int64ParamValue, err := strconv.ParseInt(c.Param(paramName), 10, 64)
|
||||
int64ParamValue, err := strconv.ParseInt(strings.Trim(c.Param(paramName), "/"), 10, 64)
|
||||
|
||||
if err != nil {
|
||||
return -1, err
|
||||
|
||||
@ -2,9 +2,8 @@ package middleware
|
||||
|
||||
import (
|
||||
postspolicies "enshi/ABAC/PostsPolicies"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"enshi/ABAC/rules"
|
||||
"enshi/middleware/getters"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@ -16,7 +15,15 @@ func PostsMiddleware() gin.HandlerFunc {
|
||||
case "DELETE":
|
||||
c.Set("target", postspolicies.DELETE_POST)
|
||||
case "PUT":
|
||||
c.Set("target", postspolicies.UPDATE_POST)
|
||||
blogId, _ := getters.GetInt64Param(c, "blog-id")
|
||||
postId, _ := getters.GetInt64Param(c, "post-id")
|
||||
|
||||
if postId > 0 && blogId > 0 {
|
||||
c.Set("target", postspolicies.UPDATE_POST_BLOG)
|
||||
} else if postId > 0 {
|
||||
c.Set("target", postspolicies.UPDATE_POST)
|
||||
}
|
||||
|
||||
case "POST":
|
||||
c.Set("target", postspolicies.CREATE_POST)
|
||||
case "GET":
|
||||
@ -25,19 +32,7 @@ func PostsMiddleware() gin.HandlerFunc {
|
||||
|
||||
isAllowed, errors := postspolicies.PostsPolicies(c)
|
||||
|
||||
var errorsMap = map[int]string{}
|
||||
for i, error := range errors {
|
||||
errorsMap[i] = error.Error()
|
||||
}
|
||||
|
||||
if errors != nil {
|
||||
c.IndentedJSON(http.StatusUnauthorized, errorsMap)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if !isAllowed {
|
||||
rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission"))
|
||||
if rules.ShouldAbortRequest(c, isAllowed, errors) {
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func CreateBlog(c *gin.Context) {
|
||||
blogParams, err := utils.GetContextPayload[db_repo.CreateBlogByUserIdParams](c)
|
||||
blogParams, err := getters.GetContextPayload[db_repo.CreateBlogByUserIdParams](c)
|
||||
if err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
|
||||
36
enshi_back/routes/blogRoutes/deleteBlog.go
Normal file
36
enshi_back/routes/blogRoutes/deleteBlog.go
Normal file
@ -0,0 +1,36 @@
|
||||
package blogRoutes
|
||||
|
||||
import (
|
||||
"context"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/middleware/getters"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func DeleteBlog(c *gin.Context) {
|
||||
blogId, err := getters.GetInt64Param(c, "blog-id")
|
||||
if err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
transaction, err := db_connection.Dbx.Begin(context.Background())
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
defer transaction.Rollback(context.Background())
|
||||
|
||||
err = db_repo.New(transaction).
|
||||
DeleteBlogByBlogId(context.Background(), blogId)
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
transaction.Commit(context.Background())
|
||||
rest_api_stuff.OkAnswer(c, "blog has been deleted")
|
||||
}
|
||||
29
enshi_back/routes/blogRoutes/getBlog.go
Normal file
29
enshi_back/routes/blogRoutes/getBlog.go
Normal file
@ -0,0 +1,29 @@
|
||||
package blogRoutes
|
||||
|
||||
import (
|
||||
"context"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/middleware/getters"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func GetBlog(c *gin.Context) {
|
||||
blogId, err := getters.GetInt64Param(c, "blog-id")
|
||||
if err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
blogData, err := db_repo.New(db_connection.Dbx).
|
||||
GetBlogByBlogId(context.Background(), blogId)
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.IndentedJSON(http.StatusOK, blogData)
|
||||
}
|
||||
46
enshi_back/routes/blogRoutes/updateBlog.go
Normal file
46
enshi_back/routes/blogRoutes/updateBlog.go
Normal file
@ -0,0 +1,46 @@
|
||||
package blogRoutes
|
||||
|
||||
import (
|
||||
"context"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/middleware/getters"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func UpdateBlog(c *gin.Context) {
|
||||
newBlogParams, err :=
|
||||
getters.GetContextPayload[db_repo.UpdateBlogInfoByBlogIdParams](c)
|
||||
if err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
blogId, err := getters.GetInt64Param(c, "blog-id")
|
||||
if err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
newBlogParams.BlogID = blogId
|
||||
|
||||
transaction, err := db_connection.Dbx.Begin(context.Background())
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
defer transaction.Rollback(context.Background())
|
||||
|
||||
_, err = db_repo.New(transaction).
|
||||
UpdateBlogInfoByBlogId(context.Background(), newBlogParams)
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
transaction.Commit(context.Background())
|
||||
|
||||
rest_api_stuff.OkAnswer(c, "blog has been updated")
|
||||
|
||||
}
|
||||
58
enshi_back/routes/postsRoutes/updatePostBlog.go
Normal file
58
enshi_back/routes/postsRoutes/updatePostBlog.go
Normal file
@ -0,0 +1,58 @@
|
||||
package postsRoutes
|
||||
|
||||
import (
|
||||
"context"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/middleware/getters"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
func UpdatePostBlog(c *gin.Context) {
|
||||
var UpdatedPostParams db_repo.UpdatePostBlogIdParams
|
||||
|
||||
postId, err := getters.GetInt64Param(c, "post-id")
|
||||
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
blogId, err := getters.GetInt64Param(c, "blog-id")
|
||||
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
UpdatedPostParams.PostID = postId
|
||||
UpdatedPostParams.BlogID = pgtype.Int8{
|
||||
Valid: true,
|
||||
Int64: blogId,
|
||||
}
|
||||
|
||||
transaction, err := db_connection.Dbx.Begin(context.Background())
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
defer transaction.Rollback(context.Background())
|
||||
|
||||
err = db_repo.New(
|
||||
transaction,
|
||||
).UpdatePostBlogId(
|
||||
context.Background(),
|
||||
UpdatedPostParams,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
transaction.Commit(context.Background())
|
||||
rest_api_stuff.OkAnswer(c, "post has been updated")
|
||||
}
|
||||
@ -49,6 +49,10 @@ func SetupRotes(g *gin.Engine) error {
|
||||
"posts/:post-id",
|
||||
postsRoutes.UpdatePost,
|
||||
)
|
||||
postsGroup.PUT(
|
||||
"posts/:post-id/blogs/:blog-id",
|
||||
postsRoutes.UpdatePostBlog,
|
||||
)
|
||||
postsGroup.POST(
|
||||
"posts",
|
||||
postsRoutes.CreatePost,
|
||||
@ -66,6 +70,21 @@ func SetupRotes(g *gin.Engine) error {
|
||||
blogRoutes.CreateBlog,
|
||||
)
|
||||
|
||||
blogGroup.PUT(
|
||||
"blogs/:blog-id",
|
||||
blogRoutes.UpdateBlog,
|
||||
)
|
||||
|
||||
blogGroup.DELETE(
|
||||
"blogs/:blog-id",
|
||||
blogRoutes.DeleteBlog,
|
||||
)
|
||||
|
||||
blogGroup.GET(
|
||||
"blogs/:blog-id",
|
||||
blogRoutes.GetBlog,
|
||||
)
|
||||
|
||||
profilesGroup := g.Group("/")
|
||||
profilesGroup.Use(middleware.ProfileMiddleware())
|
||||
|
||||
@ -74,12 +93,8 @@ func SetupRotes(g *gin.Engine) error {
|
||||
userProfileRoutes.UpdateUserProfile,
|
||||
)
|
||||
|
||||
// Auth group routes
|
||||
authGroup := g.Group("/")
|
||||
authGroup.Use(middleware.AuthMiddleware())
|
||||
|
||||
// Admin group routes
|
||||
adminGroup := authGroup.Group("/admin/")
|
||||
adminGroup := g.Group("/admin/")
|
||||
adminGroup.Use(middleware.AdminMiddleware())
|
||||
|
||||
adminGroup.GET("testAdmin", testAdmin)
|
||||
|
||||
@ -6,14 +6,13 @@ import (
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/middleware/getters"
|
||||
"enshi/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func UpdateUserProfile(c *gin.Context) {
|
||||
newProfile, err :=
|
||||
utils.GetContextPayload[db_repo.UpdateProfileByUserIdParams](c)
|
||||
getters.GetContextPayload[db_repo.UpdateProfileByUserIdParams](c)
|
||||
|
||||
if err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user