init gateway-service

This commit is contained in:
2023-09-27 17:19:25 +01:00
parent b725dae0f9
commit 4aa5cd6dfc
29 changed files with 6871 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
package api
import (
"fmt"
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/hexolan/panels/gateway-service/internal"
"github.com/hexolan/panels/gateway-service/internal/api/handlers"
)
func NewAPIApp(cfg internal.Config) *fiber.App {
app := fiber.New(fiber.Config{
AppName: "Panels REST Gateway",
ErrorHandler: handlers.ErrorHandler,
// Swap out the JSON encoder for faster marshaling
JSONEncoder: json.Marshal,
JSONDecoder: json.Unmarshal,
})
// Middleware
handlers.NewAuthMiddleware(cfg)
app.Use(cors.New())
app.Use(logger.New())
// Register the routes
RegisterRoutes(app)
return app
}
func ServeAPIApp(app *fiber.App) {
err := app.Listen(":3000")
if err != nil {
panic(fmt.Sprintf("failed to serve API: %v", err))
}
}

View File

@@ -0,0 +1,59 @@
package handlers
import (
"crypto/rsa"
"github.com/golang-jwt/jwt/v5"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/keyauth"
"github.com/hexolan/panels/gateway-service/internal"
)
var AuthMiddleware fiber.Handler
type TokenClaims struct {
jwt.RegisteredClaims
}
type tokenValidator struct {
pubKey *rsa.PublicKey
}
func NewAuthMiddleware(cfg internal.Config) {
tokenValidator := tokenValidator{pubKey: cfg.JWTPubKey}
AuthMiddleware = keyauth.New(keyauth.Config{
AuthScheme: "Bearer",
Validator: tokenValidator.ValidateToken,
})
}
func GetTokenClaims(c *fiber.Ctx) (TokenClaims, error) {
var tokenClaims TokenClaims
tokenClaims, ok := c.Locals("tokenClaims").(TokenClaims)
if !ok {
return TokenClaims{}, fiber.NewError(fiber.StatusUnauthorized, "unable to access token claims")
}
return tokenClaims, nil
}
func (tv tokenValidator) validateToken(token *jwt.Token) (interface{}, error) {
// Ensure token is signed with RSA
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, keyauth.ErrMissingOrMalformedAPIKey
}
// Validate token with public key
return tv.pubKey, nil
}
func (tv tokenValidator) ValidateToken(c *fiber.Ctx, userToken string) (bool, error) {
claims := TokenClaims{}
_, err := jwt.ParseWithClaims(userToken, &claims, tv.validateToken)
if err != nil {
return false, err
}
c.Locals("tokenClaims", claims)
return true, nil
}

View File

@@ -0,0 +1,55 @@
package handlers
import (
"errors"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/log"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func ErrorHandler(c *fiber.Ctx, err error) error {
c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSONCharsetUTF8)
code := fiber.StatusInternalServerError
msg := err.Error()
// Retrieval of codes from fiber.Errors
var e *fiber.Error
if errors.As(err, &e) {
code = e.Code
} else {
// Retrival of codes from gRPC errors.
status, ok := status.FromError(err)
if ok {
msg = status.Message()
switch status.Code() {
case codes.NotFound:
code = fiber.StatusNotFound
case codes.InvalidArgument:
code = fiber.StatusUnprocessableEntity
case codes.AlreadyExists:
code = fiber.StatusConflict
case codes.PermissionDenied:
code = fiber.StatusForbidden
case codes.Unauthenticated:
code = fiber.StatusUnauthorized
case codes.Internal:
code = fiber.StatusInternalServerError
case codes.Unavailable:
code = fiber.StatusBadGateway
msg = "Service unavaliable for request."
default:
code = fiber.StatusInternalServerError
msg = "Something went wrong."
log.Error(err)
}
} else {
msg = "Something unexpected went wrong."
log.Error(err)
}
}
return c.Status(code).JSON(fiber.Map{"status": "failure", "msg": msg})
}

View File

@@ -0,0 +1,66 @@
package api
import (
"github.com/gofiber/fiber/v2"
"github.com/hexolan/panels/gateway-service/internal/api/v1"
"github.com/hexolan/panels/gateway-service/internal/api/handlers"
)
func RegisterRoutes(app *fiber.App) {
apiV1 := app.Group("/v1")
panelV1 := apiV1.Group("/panels")
postV1 := apiV1.Group("/posts")
userV1 := apiV1.Group("/users")
authV1 := apiV1.Group("/auth")
commentV1 := postV1.Group("/:post_id/comments")
// Panel Service Routes
panelV1.Post("/", handlers.AuthMiddleware, v1.CreatePanel)
panelV1.Get("/id/:id", v1.GetPanelById)
panelV1.Patch("/id/:id", handlers.AuthMiddleware, v1.UpdatePanelById)
panelV1.Delete("/id/:id", handlers.AuthMiddleware, v1.DeletePanelById)
panelV1.Get("/name/:name", v1.GetPanelByName)
panelV1.Patch("/name/:name", handlers.AuthMiddleware, v1.UpdatePanelByName)
panelV1.Delete("/name/:name", handlers.AuthMiddleware, v1.DeletePanelByName)
// Post Service Routes
postV1.Get("/feed", v1.GetFeedPosts)
postV1.Patch("/:id", handlers.AuthMiddleware, v1.UpdatePost)
postV1.Delete("/:id", handlers.AuthMiddleware, v1.DeletePost)
userV1.Get("/id/:user_id/posts", v1.GetUserPostsFromId)
userV1.Get("/username/:username/posts", v1.GetUserPostsFromUsername)
panelV1.Get("/id/:panel_id/posts", v1.GetPanelPostsFromId)
panelV1.Get("/name/:panel_name/posts", v1.GetPanelPostsFromName)
panelV1.Get("/id/:panel_id/posts/:id", v1.GetPanelPostFromId)
panelV1.Get("/name/:panel_name/posts/:id", v1.GetPanelPostFromName)
panelV1.Post("/id/:panel_id", handlers.AuthMiddleware, v1.CreatePanelPostFromId)
panelV1.Post("/name/:panel_name", handlers.AuthMiddleware, v1.CreatePanelPostFromName)
// User Service Routes
userV1.Post("/", v1.UserSignup)
userV1.Get("/id/:id", v1.GetUserById)
userV1.Delete("/id/:id", handlers.AuthMiddleware, v1.DeleteUserById)
userV1.Get("/username/:username", v1.GetUserByUsername)
userV1.Delete("/username/:username", handlers.AuthMiddleware, v1.DeleteUserByUsername)
userV1.Get("/me", handlers.AuthMiddleware, v1.GetCurrentUser)
userV1.Delete("/me", handlers.AuthMiddleware, v1.DeleteCurrentUser)
// Auth Service Routes
authV1.Post("/login", v1.LoginWithPassword)
// Comment Service Routes
commentV1.Get("/", v1.GetPostComments)
commentV1.Post("/", handlers.AuthMiddleware, v1.CreateComment)
commentV1.Patch("/:id", handlers.AuthMiddleware, v1.UpdateComment)
commentV1.Delete("/:id", handlers.AuthMiddleware, v1.DeleteComment)
}

View File

@@ -0,0 +1,70 @@
package v1
import (
"time"
"context"
"github.com/gofiber/fiber/v2"
"github.com/hexolan/panels/gateway-service/internal/rpc"
"github.com/hexolan/panels/gateway-service/internal/rpc/authv1"
)
type userLoginForm struct {
Username string
Password string
}
func setAuthMethod(userId string, password string) error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err := rpc.Svcs.GetAuthSvc().SetPasswordAuth(
ctx,
&authv1.SetPasswordAuthMethod{
UserId: userId,
Password: password,
},
)
return err
}
func authWithPassword(userId string, password string) (*authv1.AuthToken, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
token, err := rpc.Svcs.GetAuthSvc().AuthWithPassword(
ctx,
&authv1.PasswordAuthRequest{
UserId: userId,
Password: password,
},
)
return token, err
}
func LoginWithPassword(c *fiber.Ctx) error {
// Parse the body data
form := new(userLoginForm)
if err := c.BodyParser(form); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
// username -> user ID
user, err := getUserByUsername(form.Username)
if err != nil {
return err
}
// attempt to auth
token, err := authWithPassword(user.Id, form.Password)
if err != nil {
return err
}
return c.JSON(fiber.Map{
"status": "success",
"data": fiber.Map{
"user": user,
"token": token,
},
})
}

View File

@@ -0,0 +1,143 @@
package v1
import (
"context"
"time"
"github.com/gofiber/fiber/v2"
"github.com/hexolan/panels/gateway-service/internal/api/handlers"
"github.com/hexolan/panels/gateway-service/internal/rpc"
"github.com/hexolan/panels/gateway-service/internal/rpc/commentv1"
)
func getComment(id string) (*commentv1.Comment, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
comment, err := rpc.Svcs.GetCommentSvc().GetComment(
ctx,
&commentv1.GetCommentRequest{Id: id},
)
return comment, err
}
func GetPostComments(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
comments, err := rpc.Svcs.GetCommentSvc().GetPostComments(
ctx,
&commentv1.GetPostCommentsRequest{PostId: c.Params("post_id")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": comments})
}
func UpdateComment(c *fiber.Ctx) error {
// check if user has permissions to update the comment
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
comment, err := getComment(c.Params("id"))
if err != nil {
return err
}
if (comment.AuthorId != currentUser.Id) {
return fiber.NewError(fiber.StatusForbidden, "no permissions to update that comment")
}
// Parse the body data
updatedComment := new(commentv1.CommentMutable)
if err := c.BodyParser(updatedComment); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
// Update the comment
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
comment, err = rpc.Svcs.GetCommentSvc().UpdateComment(
ctx,
&commentv1.UpdateCommentRequest{
Id: c.Params("id"),
Data: updatedComment,
},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": comment})
}
func DeleteComment(c *fiber.Ctx) error {
// check if user has permissions to delete the comment
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
comment, err := getComment(c.Params("id"))
if err != nil {
return err
}
if (comment.AuthorId != currentUser.Id && !currentUser.IsAdmin) {
return fiber.NewError(fiber.StatusForbidden, "no permissions to delete that comment")
}
// Delete the comment
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = rpc.Svcs.GetCommentSvc().DeleteComment(
ctx,
&commentv1.DeleteCommentRequest{Id: c.Params("id")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "msg": "comment deleted"})
}
func CreateComment(c *fiber.Ctx) error {
// Parse the body data
newComment := new(commentv1.CommentMutable)
if err := c.BodyParser(newComment); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
// check post is real
post, err := getPostById(c.Params("post_id"))
if err != nil {
return err
}
// access token claims
tokenClaims, err := handlers.GetTokenClaims(c)
if err != nil {
return err
}
// Create the comment
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
comment, err := rpc.Svcs.GetCommentSvc().CreateComment(
ctx,
&commentv1.CreateCommentRequest{
PostId: post.Id,
AuthorId: tokenClaims.Subject,
Data: newComment,
},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": comment})
}

View File

@@ -0,0 +1,186 @@
package v1
import (
"time"
"context"
"github.com/gofiber/fiber/v2"
"github.com/hexolan/panels/gateway-service/internal/rpc"
"github.com/hexolan/panels/gateway-service/internal/rpc/panelv1"
)
func getPanelById(id string) (*panelv1.Panel, error) {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
return rpc.Svcs.GetPanelSvc().GetPanel(
ctx,
&panelv1.GetPanelByIdRequest{Id: id},
)
}
func getPanelIDFromName(name string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
panel, err := rpc.Svcs.GetPanelSvc().GetPanelByName(
ctx,
&panelv1.GetPanelByNameRequest{Name: name},
)
if err != nil {
return "", err
}
return panel.GetId(), nil
}
func GetPanelById(c *fiber.Ctx) error {
panel, err := getPanelById(c.Params("id"))
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": panel})
}
func GetPanelByName(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
panel, err := rpc.Svcs.GetPanelSvc().GetPanelByName(
ctx,
&panelv1.GetPanelByNameRequest{Name: c.Params("name")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": panel})
}
func UpdatePanelById(c *fiber.Ctx) error {
// check user can update panels
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
if !currentUser.IsAdmin {
return fiber.NewError(fiber.StatusForbidden, "no permissions to update that panel")
}
// update the panel
patchData := new(panelv1.PanelMutable)
if err := c.BodyParser(patchData); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
panel, err := rpc.Svcs.GetPanelSvc().UpdatePanel(
ctx,
&panelv1.UpdatePanelByIdRequest{Id: c.Params("id"), Data: patchData},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": panel})
}
func UpdatePanelByName(c *fiber.Ctx) error {
// check user can update panels
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
if !currentUser.IsAdmin {
return fiber.NewError(fiber.StatusForbidden, "no permissions to update that panel")
}
// update the panel
patchData := new(panelv1.PanelMutable)
if err := c.BodyParser(patchData); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
panel, err := rpc.Svcs.GetPanelSvc().UpdatePanelByName(
ctx,
&panelv1.UpdatePanelByNameRequest{Name: c.Params("name"), Data: patchData},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": panel})
}
func DeletePanelById(c *fiber.Ctx) error {
// check user can delete panels
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
if !currentUser.IsAdmin {
return fiber.NewError(fiber.StatusForbidden, "no permissions to delete that panel")
}
// delete the panel
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
_, err = rpc.Svcs.GetPanelSvc().DeletePanel(
ctx,
&panelv1.DeletePanelByIdRequest{Id: c.Params("id")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "msg": "panel deleted"})
}
func DeletePanelByName(c *fiber.Ctx) error {
// check user can delete panels
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
if !currentUser.IsAdmin {
return fiber.NewError(fiber.StatusForbidden, "no permissions to update that panel")
}
// delete the panel
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
_, err = rpc.Svcs.GetPanelSvc().DeletePanelByName(
ctx,
&panelv1.DeletePanelByNameRequest{Name: c.Params("name")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "msg": "panel deleted"})
}
func CreatePanel(c *fiber.Ctx) error {
newPanel := new(panelv1.PanelMutable)
if err := c.BodyParser(newPanel); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
panel, err := rpc.Svcs.GetPanelSvc().CreatePanel(
ctx,
&panelv1.CreatePanelRequest{Data: newPanel},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": panel})
}

View File

@@ -0,0 +1,284 @@
package v1
import (
"time"
"context"
"github.com/gofiber/fiber/v2"
"github.com/hexolan/panels/gateway-service/internal/rpc"
"github.com/hexolan/panels/gateway-service/internal/rpc/postv1"
"github.com/hexolan/panels/gateway-service/internal/api/handlers"
)
func getPostById(postId string) (*postv1.Post, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
return rpc.Svcs.GetPostSvc().GetPost(
ctx,
&postv1.GetPostRequest{Id: postId},
)
}
func GetFeedPosts(c *fiber.Ctx) error {
// Make the request for feed posts
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
posts, err := rpc.Svcs.GetPostSvc().GetFeedPosts(
ctx,
&postv1.GetFeedPostsRequest{},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": posts})
}
func GetPanelPostFromId(c *fiber.Ctx) error {
// Make the request for the panel post
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
post, err := rpc.Svcs.GetPostSvc().GetPanelPost(
ctx,
&postv1.GetPanelPostRequest{Id: c.Params("id"), PanelId: c.Params("panel_id")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": post})
}
func GetPanelPostFromName(c *fiber.Ctx) error {
// Get the panel ID from name.
panelId, err := getPanelIDFromName(c.Params("panel_name"))
if err != nil {
return err
}
// Make the request for the panel post
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
post, err := rpc.Svcs.GetPostSvc().GetPanelPost(
ctx,
&postv1.GetPanelPostRequest{Id: c.Params("id"), PanelId: panelId},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": post})
}
func GetUserPostsFromId(c *fiber.Ctx) error {
// Make the request for user posts
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
posts, err := rpc.Svcs.GetPostSvc().GetUserPosts(
ctx,
&postv1.GetUserPostsRequest{UserId: c.Params("user_id")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": posts})
}
func GetUserPostsFromUsername(c *fiber.Ctx) error {
// Get the user ID from username.
user, err := getUserByUsername(c.Params("username"))
if err != nil {
return err
}
// Make the request for user posts
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
posts, err := rpc.Svcs.GetPostSvc().GetUserPosts(
ctx,
&postv1.GetUserPostsRequest{UserId: user.Id},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": posts})
}
func GetPanelPostsFromId(c *fiber.Ctx) error {
// Make the request for panel posts
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
posts, err := rpc.Svcs.GetPostSvc().GetPanelPosts(
ctx,
&postv1.GetPanelPostsRequest{PanelId: c.Params("panel_id")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": posts})
}
func GetPanelPostsFromName(c *fiber.Ctx) error {
// Get the panel ID from name.
panelId, err := getPanelIDFromName(c.Params("panel_name"))
if err != nil {
return err
}
// Make the request for panel posts
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
posts, err := rpc.Svcs.GetPostSvc().GetPanelPosts(
ctx,
&postv1.GetPanelPostsRequest{PanelId: panelId},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": posts})
}
func UpdatePost(c *fiber.Ctx) error {
// check if user has permissions to update the post
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
post, err := getPostById(c.Params("id"))
if err != nil {
return err
}
if (post.AuthorId != currentUser.Id) {
return fiber.NewError(fiber.StatusForbidden, "no permissions to update that post")
}
// form patch data for update request
patchData := new(postv1.PostMutable)
if err := c.BodyParser(patchData); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
// update the post
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
post, err = rpc.Svcs.GetPostSvc().UpdatePost(
ctx,
&postv1.UpdatePostRequest{Id: c.Params("id"), Data: patchData},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": post})
}
func DeletePost(c *fiber.Ctx) error {
// check if user has permissions to delete the post
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
post, err := getPostById(c.Params("id"))
if err != nil {
return err
}
if (post.AuthorId != currentUser.Id && !currentUser.IsAdmin) {
return fiber.NewError(fiber.StatusForbidden, "no permissions to delete that post")
}
// delete the post
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
_, err = rpc.Svcs.GetPostSvc().DeletePost(
ctx,
&postv1.DeletePostRequest{Id: c.Params("id")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "msg": "post deleted"})
}
func CreatePanelPostFromId(c *fiber.Ctx) error {
// Parse the body data
newPost := new(postv1.PostMutable)
if err := c.BodyParser(newPost); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
// Get the panel ID from provided panel name
panel, err := getPanelById(c.Params("panel_id"))
if err != nil {
return err
}
// access token claims
tokenClaims, err := handlers.GetTokenClaims(c)
if err != nil {
return err
}
// make rpc call
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
post, err := rpc.Svcs.GetPostSvc().CreatePost(
ctx,
&postv1.CreatePostRequest{
PanelId: panel.Id,
UserId: tokenClaims.Subject,
Data: newPost,
},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": post})
}
func CreatePanelPostFromName(c *fiber.Ctx) error {
// Parse the body data
newPost := new(postv1.PostMutable)
if err := c.BodyParser(newPost); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
// Get the panel ID from provided panel name
panelId, err := getPanelIDFromName(c.Params("panel_name"))
if err != nil {
return err
}
// access token claims
tokenClaims, err := handlers.GetTokenClaims(c)
if err != nil {
return err
}
// make rpc call
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
post, err := rpc.Svcs.GetPostSvc().CreatePost(
ctx,
&postv1.CreatePostRequest{
PanelId: panelId,
UserId: tokenClaims.Subject,
Data: newPost,
},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": post})
}

View File

@@ -0,0 +1,196 @@
package v1
import (
"time"
"context"
"github.com/gofiber/fiber/v2"
"github.com/hexolan/panels/gateway-service/internal/rpc"
"github.com/hexolan/panels/gateway-service/internal/rpc/userv1"
"github.com/hexolan/panels/gateway-service/internal/api/handlers"
)
type userSignupForm struct {
Username string
Password string
}
func getCurrentUser(c *fiber.Ctx) (*userv1.User, error) {
// access token claims
tokenClaims, err := handlers.GetTokenClaims(c)
if err != nil {
return nil, err
}
// fetch current user
return getUserById(tokenClaims.Subject)
}
func getUserById(id string) (*userv1.User, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
user, err := rpc.Svcs.GetUserSvc().GetUser(
ctx,
&userv1.GetUserByIdRequest{Id: id},
)
return user, err
}
func getUserByUsername(username string) (*userv1.User, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
user, err := rpc.Svcs.GetUserSvc().GetUserByName(
ctx,
&userv1.GetUserByNameRequest{Username: username},
)
return user, err
}
func GetUserById(c *fiber.Ctx) error {
user, err := getUserById(c.Params("id"))
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": user})
}
func GetUserByUsername(c *fiber.Ctx) error {
user, err := getUserByUsername(c.Params("username"))
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": user})
}
func GetCurrentUser(c *fiber.Ctx) error {
user, err := getCurrentUser(c)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "data": user})
}
func DeleteUserById(c *fiber.Ctx) error {
// get current user info
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
// check current user has permission to delete user
if currentUser.Id != c.Params("id") && !currentUser.IsAdmin {
return fiber.NewError(fiber.StatusForbidden, "no permissions to delete that user")
}
// delete the user
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
_, err = rpc.Svcs.GetUserSvc().DeleteUser(
ctx,
&userv1.DeleteUserByIdRequest{Id: c.Params("id")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "msg": "user deleted"})
}
func DeleteUserByUsername(c *fiber.Ctx) error {
// get current user info
currentUser, err := getCurrentUser(c)
if err != nil {
return err
}
// check current user has permission to delete user
if currentUser.Id != c.Params("id") && !currentUser.IsAdmin {
return fiber.NewError(fiber.StatusForbidden, "no permissions to delete that user")
}
// delete the user
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
_, err = rpc.Svcs.GetUserSvc().DeleteUserByName(
ctx,
&userv1.DeleteUserByNameRequest{Username: c.Params("username")},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "msg": "user deleted"})
}
func DeleteCurrentUser(c *fiber.Ctx) error {
// View access token claims
tokenClaims, err := handlers.GetTokenClaims(c)
if err != nil {
return err
}
// RPC call
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = rpc.Svcs.GetUserSvc().DeleteUser(
ctx,
&userv1.DeleteUserByIdRequest{Id: tokenClaims.Subject},
)
if err != nil {
return err
}
return c.JSON(fiber.Map{"status": "success", "msg": "user deleted"})
}
func UserSignup(c *fiber.Ctx) error {
// Parse the body data
form := new(userSignupForm)
if err := c.BodyParser(form); err != nil {
fiber.NewError(fiber.StatusBadRequest, "malformed request")
}
// Attempt to create the user
// todo: defer this logic away from gateway-service in future (potentially into seperate registration-service)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
user, err := rpc.Svcs.GetUserSvc().CreateUser(
ctx,
&userv1.CreateUserRequest{
Data: &userv1.UserMutable{
Username: &form.Username,
},
},
)
if err != nil {
return err
}
// Attempt to set password auth method for the user
err = setAuthMethod(user.Id, form.Password)
if err != nil {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
_, _ = rpc.Svcs.GetUserSvc().DeleteUser(
ctx,
&userv1.DeleteUserByIdRequest{Id: user.Id},
)
return err
}
// Signup success - attempt to get auth token
token, _ := authWithPassword(user.Id, form.Password)
return c.JSON(fiber.Map{
"status": "success",
"data": fiber.Map{
"user": user,
"token": token,
},
})
}