Compare commits
	
		
			9 commits
		
	
	
		
			0792c6b8a3
			...
			bc9b01d85a
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						bc9b01d85a | ||
| 
							 | 
						d2b4bf2a89 | ||
| 
							 | 
						8d5329146d | ||
| 
							 | 
						77f028fd39 | ||
| 
							 | 
						f1e15137d6 | ||
| 
							 | 
						87a19bfd4e | ||
| 
							 | 
						c2fa9aa0c1 | ||
| 
							 | 
						1385011769 | ||
| 
							 | 
						374e357820 | 
					 14 changed files with 548 additions and 102 deletions
				
			
		| 
						 | 
				
			
			@ -34,6 +34,7 @@ clean:
 | 
			
		|||
	rm -f plantuml.jar
 | 
			
		||||
	rm -f erd.png
 | 
			
		||||
	rm -f config.toml
 | 
			
		||||
	rm -f database.txt
 | 
			
		||||
 | 
			
		||||
# Test target
 | 
			
		||||
test: db.sqlite3
 | 
			
		||||
| 
						 | 
				
			
			@ -105,6 +106,7 @@ docs:
 | 
			
		|||
	swag init -outputTypes go
 | 
			
		||||
 | 
			
		||||
api: ./docs/swagger.json
 | 
			
		||||
	rm ../frontend/src/API/GenApi.ts
 | 
			
		||||
	npx swagger-typescript-api \
 | 
			
		||||
		--api-class-name GenApi \
 | 
			
		||||
		--path ./docs/swagger.json \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,21 +21,21 @@ const docTemplate = `{
 | 
			
		|||
    "paths": {
 | 
			
		||||
        "/login": {
 | 
			
		||||
            "post": {
 | 
			
		||||
                "description": "logs the user in and returns a jwt token",
 | 
			
		||||
                "description": "Logs in a user and returns a JWT token",
 | 
			
		||||
                "consumes": [
 | 
			
		||||
                    "application/json"
 | 
			
		||||
                ],
 | 
			
		||||
                "produces": [
 | 
			
		||||
                    "text/plain"
 | 
			
		||||
                    "application/json"
 | 
			
		||||
                ],
 | 
			
		||||
                "tags": [
 | 
			
		||||
                    "User"
 | 
			
		||||
                    "Auth"
 | 
			
		||||
                ],
 | 
			
		||||
                "summary": "login",
 | 
			
		||||
                "summary": "Login",
 | 
			
		||||
                "parameters": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "description": "login info",
 | 
			
		||||
                        "name": "NewUser",
 | 
			
		||||
                        "description": "User credentials",
 | 
			
		||||
                        "name": "body",
 | 
			
		||||
                        "in": "body",
 | 
			
		||||
                        "required": true,
 | 
			
		||||
                        "schema": {
 | 
			
		||||
| 
						 | 
				
			
			@ -45,9 +45,9 @@ const docTemplate = `{
 | 
			
		|||
                ],
 | 
			
		||||
                "responses": {
 | 
			
		||||
                    "200": {
 | 
			
		||||
                        "description": "Successfully signed token for user",
 | 
			
		||||
                        "description": "JWT token",
 | 
			
		||||
                        "schema": {
 | 
			
		||||
                            "type": "Token"
 | 
			
		||||
                            "$ref": "#/definitions/types.Token"
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    "400": {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,29 +71,26 @@ const docTemplate = `{
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "/loginerenew": {
 | 
			
		||||
        "/loginrenew": {
 | 
			
		||||
            "post": {
 | 
			
		||||
                "security": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "bererToken": []
 | 
			
		||||
                        "JWT": []
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "description": "renews the users token",
 | 
			
		||||
                "consumes": [
 | 
			
		||||
                "description": "Renews the users token.",
 | 
			
		||||
                "produces": [
 | 
			
		||||
                    "application/json"
 | 
			
		||||
                ],
 | 
			
		||||
                "produces": [
 | 
			
		||||
                    "text/plain"
 | 
			
		||||
                ],
 | 
			
		||||
                "tags": [
 | 
			
		||||
                    "User"
 | 
			
		||||
                    "Auth"
 | 
			
		||||
                ],
 | 
			
		||||
                "summary": "LoginRenews",
 | 
			
		||||
                "responses": {
 | 
			
		||||
                    "200": {
 | 
			
		||||
                        "description": "Successfully signed token for user",
 | 
			
		||||
                        "schema": {
 | 
			
		||||
                            "type": "Token"
 | 
			
		||||
                            "$ref": "#/definitions/types.Token"
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    "401": {
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +110,12 @@ const docTemplate = `{
 | 
			
		|||
        },
 | 
			
		||||
        "/promoteToAdmin": {
 | 
			
		||||
            "post": {
 | 
			
		||||
                "description": "promote chosen user to admin",
 | 
			
		||||
                "security": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "JWT": []
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "description": "Promote chosen user to site admin",
 | 
			
		||||
                "consumes": [
 | 
			
		||||
                    "application/json"
 | 
			
		||||
                ],
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +141,7 @@ const docTemplate = `{
 | 
			
		|||
                    "200": {
 | 
			
		||||
                        "description": "Successfully promoted user",
 | 
			
		||||
                        "schema": {
 | 
			
		||||
                            "type": "json"
 | 
			
		||||
                            "$ref": "#/definitions/types.Token"
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    "400": {
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +175,7 @@ const docTemplate = `{
 | 
			
		|||
                    "text/plain"
 | 
			
		||||
                ],
 | 
			
		||||
                "tags": [
 | 
			
		||||
                    "User"
 | 
			
		||||
                    "Auth"
 | 
			
		||||
                ],
 | 
			
		||||
                "summary": "Register",
 | 
			
		||||
                "parameters": [
 | 
			
		||||
| 
						 | 
				
			
			@ -211,6 +213,11 @@ const docTemplate = `{
 | 
			
		|||
        },
 | 
			
		||||
        "/userdelete/{username}": {
 | 
			
		||||
            "delete": {
 | 
			
		||||
                "security": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "JWT": []
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "description": "UserDelete deletes a user from the database",
 | 
			
		||||
                "consumes": [
 | 
			
		||||
                    "application/json"
 | 
			
		||||
| 
						 | 
				
			
			@ -252,12 +259,14 @@ const docTemplate = `{
 | 
			
		|||
        },
 | 
			
		||||
        "/users/all": {
 | 
			
		||||
            "get": {
 | 
			
		||||
                "description": "lists all users",
 | 
			
		||||
                "consumes": [
 | 
			
		||||
                    "application/json"
 | 
			
		||||
                "security": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "JWT": []
 | 
			
		||||
                    }
 | 
			
		||||
                ],
 | 
			
		||||
                "description": "lists all users",
 | 
			
		||||
                "produces": [
 | 
			
		||||
                    "text/plain"
 | 
			
		||||
                    "application/json"
 | 
			
		||||
                ],
 | 
			
		||||
                "tags": [
 | 
			
		||||
                    "User"
 | 
			
		||||
| 
						 | 
				
			
			@ -265,9 +274,12 @@ const docTemplate = `{
 | 
			
		|||
                "summary": "ListsAllUsers",
 | 
			
		||||
                "responses": {
 | 
			
		||||
                    "200": {
 | 
			
		||||
                        "description": "Successfully signed token for user",
 | 
			
		||||
                        "description": "Successfully returned all users",
 | 
			
		||||
                        "schema": {
 | 
			
		||||
                            "type": "json"
 | 
			
		||||
                            "type": "array",
 | 
			
		||||
                            "items": {
 | 
			
		||||
                                "type": "string"
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    "401": {
 | 
			
		||||
| 
						 | 
				
			
			@ -291,16 +303,27 @@ const docTemplate = `{
 | 
			
		|||
            "type": "object",
 | 
			
		||||
            "properties": {
 | 
			
		||||
                "password": {
 | 
			
		||||
                    "type": "string"
 | 
			
		||||
                    "type": "string",
 | 
			
		||||
                    "example": "password123"
 | 
			
		||||
                },
 | 
			
		||||
                "username": {
 | 
			
		||||
                    "type": "string",
 | 
			
		||||
                    "example": "username123"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "types.Token": {
 | 
			
		||||
            "type": "object",
 | 
			
		||||
            "properties": {
 | 
			
		||||
                "token": {
 | 
			
		||||
                    "type": "string"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "securityDefinitions": {
 | 
			
		||||
        "bererToken": {
 | 
			
		||||
        "JWT": {
 | 
			
		||||
            "description": "Use the JWT token provided by the login endpoint to authenticate requests.  **Prefix the token with \"Bearer \".**",
 | 
			
		||||
            "type": "apiKey",
 | 
			
		||||
            "name": "Authorization",
 | 
			
		||||
            "in": "header"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,16 +7,17 @@ import (
 | 
			
		|||
	"github.com/gofiber/fiber/v2/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ListAllUsers is a handler that returns a list of all users in the application database
 | 
			
		||||
// @Summary		ListsAllUsers
 | 
			
		||||
// @Description	lists all users
 | 
			
		||||
// @Tags			User
 | 
			
		||||
// @Accept			json
 | 
			
		||||
// @Produce		plain
 | 
			
		||||
// @Success		200	{json}		json	"Successfully signed token for user"
 | 
			
		||||
// @Failure		401	{string}	string	"Unauthorized"
 | 
			
		||||
// @Failure		500	{string}	string	"Internal server error"
 | 
			
		||||
// @Router			/users/all [get]
 | 
			
		||||
//	@Summary		ListsAllUsers
 | 
			
		||||
//	@Description	lists all users
 | 
			
		||||
//	@Tags			User
 | 
			
		||||
//	@Produce		json
 | 
			
		||||
//	@Security		JWT
 | 
			
		||||
//	@Success		200	{array}		string	"Successfully returned all users"
 | 
			
		||||
//	@Failure		401	{string}	string	"Unauthorized"
 | 
			
		||||
//	@Failure		500	{string}	string	"Internal server error"
 | 
			
		||||
//	@Router			/users/all [get]
 | 
			
		||||
//
 | 
			
		||||
// ListAllUsers returns a list of all users in the application database
 | 
			
		||||
func ListAllUsers(c *fiber.Ctx) error {
 | 
			
		||||
	// Get all users from the database
 | 
			
		||||
	users, err := db.GetDb(c).GetAllUsersApplication()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,18 +10,19 @@ import (
 | 
			
		|||
	"github.com/golang-jwt/jwt/v5"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Login is a simple login handler that returns a JWT token
 | 
			
		||||
// @Summary		login
 | 
			
		||||
// @Description	logs the user in and returns a jwt token
 | 
			
		||||
// @Tags			User
 | 
			
		||||
// @Accept			json
 | 
			
		||||
// @Param			NewUser	body	types.NewUser	true	"login info"
 | 
			
		||||
// @Produce		plain
 | 
			
		||||
// @Success		200	Token		types.Token	"Successfully signed token for user"
 | 
			
		||||
// @Failure		400	{string}	string		"Bad request"
 | 
			
		||||
// @Failure		401	{string}	string		"Unauthorized"
 | 
			
		||||
// @Failure		500	{string}	string		"Internal server error"
 | 
			
		||||
// @Router			/login [post]
 | 
			
		||||
//	@Summary		Login
 | 
			
		||||
//	@Description	Logs in a user and returns a JWT token
 | 
			
		||||
//	@Tags			Auth
 | 
			
		||||
//	@Accept			json
 | 
			
		||||
//	@Produce		json
 | 
			
		||||
//	@Param			body	body		types.NewUser	true	"User credentials"
 | 
			
		||||
//	@Success		200		{object}	types.Token		"JWT token"
 | 
			
		||||
//	@Failure		400		{string}	string			"Bad request"
 | 
			
		||||
//	@Failure		401		{string}	string			"Unauthorized"
 | 
			
		||||
//	@Failure		500		{string}	string			"Internal server error"
 | 
			
		||||
//	@Router			/login [post]
 | 
			
		||||
//
 | 
			
		||||
// Login logs in a user and returns a JWT token
 | 
			
		||||
func Login(c *fiber.Ctx) error {
 | 
			
		||||
	// The body type is identical to a NewUser
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,34 +9,40 @@ import (
 | 
			
		|||
	"github.com/golang-jwt/jwt/v5"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LoginRenew is a simple handler that renews the token
 | 
			
		||||
// @Summary		LoginRenews
 | 
			
		||||
// @Description	renews the users token
 | 
			
		||||
// @Security		bererToken
 | 
			
		||||
// @Tags			User
 | 
			
		||||
// @Accept			json
 | 
			
		||||
// @Produce		plain
 | 
			
		||||
// @Success		200	Token		types.Token	"Successfully signed token for user"
 | 
			
		||||
// @Failure		401	{string}	string		"Unauthorized"
 | 
			
		||||
// @Failure		500	{string}	string		"Internal server error"
 | 
			
		||||
// @Router			/loginerenew [post]
 | 
			
		||||
//	@Summary		LoginRenews
 | 
			
		||||
//	@Description	Renews the users token.
 | 
			
		||||
//	@Tags			Auth
 | 
			
		||||
//	@Produce		json
 | 
			
		||||
//	@Security		JWT
 | 
			
		||||
//	@Success		200	{object}	types.Token	"Successfully signed token for user"
 | 
			
		||||
//	@Failure		401	{string}	string		"Unauthorized"
 | 
			
		||||
//	@Failure		500	{string}	string		"Internal server error"
 | 
			
		||||
//	@Router			/loginrenew [post]
 | 
			
		||||
//
 | 
			
		||||
// LoginRenew renews the users token
 | 
			
		||||
func LoginRenew(c *fiber.Ctx) error {
 | 
			
		||||
	user := c.Locals("user").(*jwt.Token)
 | 
			
		||||
 | 
			
		||||
	log.Info("Renewing token for user:", user.Claims.(jwt.MapClaims)["name"])
 | 
			
		||||
 | 
			
		||||
	// Renewing the token means we trust whatever is already in the token
 | 
			
		||||
	claims := user.Claims.(jwt.MapClaims)
 | 
			
		||||
 | 
			
		||||
	// 72 hour expiration time
 | 
			
		||||
	claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
 | 
			
		||||
	renewed := jwt.MapClaims{
 | 
			
		||||
 | 
			
		||||
	// Create token with old claims, but new expiration time
 | 
			
		||||
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
 | 
			
		||||
		"name":  claims["name"],
 | 
			
		||||
		"admin": claims["admin"],
 | 
			
		||||
		"exp":   claims["exp"],
 | 
			
		||||
	}
 | 
			
		||||
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, renewed)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Sign it with top secret key
 | 
			
		||||
	t, err := token.SignedString([]byte("secret"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Warn("Error signing token")
 | 
			
		||||
		return c.SendStatus(fiber.StatusInternalServerError)
 | 
			
		||||
		return c.SendStatus(fiber.StatusInternalServerError) // 500
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Info("Successfully renewed token for user:", user.Claims.(jwt.MapClaims)["name"])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,17 +8,20 @@ import (
 | 
			
		|||
	"github.com/gofiber/fiber/v2/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// @Summary		PromoteToAdmin
 | 
			
		||||
// @Description	promote chosen user to admin
 | 
			
		||||
// @Tags			User
 | 
			
		||||
// @Accept			json
 | 
			
		||||
// @Produce		plain
 | 
			
		||||
// @Param			NewUser	body		types.NewUser	true	"user info"
 | 
			
		||||
// @Success		200		{json}		json			"Successfully promoted user"
 | 
			
		||||
// @Failure		400		{string}	string			"Bad request"
 | 
			
		||||
// @Failure		401		{string}	string			"Unauthorized"
 | 
			
		||||
// @Failure		500		{string}	string			"Internal server error"
 | 
			
		||||
// @Router			/promoteToAdmin [post]
 | 
			
		||||
//	@Summary		PromoteToAdmin
 | 
			
		||||
//	@Description	Promote chosen user to site admin
 | 
			
		||||
//	@Tags			User
 | 
			
		||||
//	@Accept			json
 | 
			
		||||
//	@Produce		plain
 | 
			
		||||
//	@Security		JWT
 | 
			
		||||
//	@Param			NewUser	body		types.NewUser	true	"user info"
 | 
			
		||||
//	@Success		200		{object}	types.Token		"Successfully promoted user"
 | 
			
		||||
//	@Failure		400		{string}	string			"Bad request"
 | 
			
		||||
//	@Failure		401		{string}	string			"Unauthorized"
 | 
			
		||||
//	@Failure		500		{string}	string			"Internal server error"
 | 
			
		||||
//	@Router			/promoteToAdmin [post]
 | 
			
		||||
//
 | 
			
		||||
// PromoteToAdmin promotes a user to a site admin
 | 
			
		||||
func PromoteToAdmin(c *fiber.Ctx) error {
 | 
			
		||||
	// Extract the username from the request body
 | 
			
		||||
	var newUser types.NewUser
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,11 +8,9 @@ import (
 | 
			
		|||
	"github.com/gofiber/fiber/v2/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Register is a simple handler that registers a new user
 | 
			
		||||
//
 | 
			
		||||
//	@Summary		Register
 | 
			
		||||
//	@Description	Register a new user
 | 
			
		||||
//	@Tags			User
 | 
			
		||||
//	@Tags			Auth
 | 
			
		||||
//	@Accept			json
 | 
			
		||||
//	@Produce		plain
 | 
			
		||||
//	@Param			NewUser	body		types.NewUser	true	"User to register"
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +18,8 @@ import (
 | 
			
		|||
//	@Failure		400		{string}	string			"Bad request"
 | 
			
		||||
//	@Failure		500		{string}	string			"Internal server error"
 | 
			
		||||
//	@Router			/register [post]
 | 
			
		||||
//
 | 
			
		||||
// Register is a simple handler that registers a new user
 | 
			
		||||
func Register(c *fiber.Ctx) error {
 | 
			
		||||
	u := new(types.NewUser)
 | 
			
		||||
	if err := c.BodyParser(u); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,19 +8,19 @@ import (
 | 
			
		|||
	"github.com/golang-jwt/jwt/v5"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This path should obviously be protected in the future
 | 
			
		||||
// UserDelete deletes a user from the database
 | 
			
		||||
//
 | 
			
		||||
//	@Summary		UserDelete
 | 
			
		||||
//	@Description	UserDelete deletes a user from the database
 | 
			
		||||
//	@Tags			User
 | 
			
		||||
//	@Accept			json
 | 
			
		||||
//	@Produce		plain
 | 
			
		||||
//	@Security		JWT
 | 
			
		||||
//	@Success		200	{string}	string	"User deleted"
 | 
			
		||||
//	@Failure		403	{string}	string	"You can only delete yourself"
 | 
			
		||||
//	@Failure		500	{string}	string	"Internal server error"
 | 
			
		||||
//	@Failure		401	{string}	string	"Unauthorized"
 | 
			
		||||
//	@Router			/userdelete/{username} [delete]
 | 
			
		||||
//
 | 
			
		||||
// UserDelete deletes a user from the database
 | 
			
		||||
func UserDelete(c *fiber.Ctx) error {
 | 
			
		||||
	// Read from path parameters
 | 
			
		||||
	username := c.Params("username")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,8 +18,8 @@ func (u *User) ToPublicUser() (*PublicUser, error) {
 | 
			
		|||
 | 
			
		||||
// Should be used when registering, for example
 | 
			
		||||
type NewUser struct {
 | 
			
		||||
	Username string `json:"username"`
 | 
			
		||||
	Password string `json:"password"`
 | 
			
		||||
	Username string `json:"username" example:"username123"`
 | 
			
		||||
	Password string `json:"password" example:"password123"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PublicUser represents a user that is safe to send over the API (no password)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,15 +25,16 @@ import (
 | 
			
		|||
//	@license.name	AGPL
 | 
			
		||||
//	@license.url	https://www.gnu.org/licenses/agpl-3.0.html
 | 
			
		||||
 | 
			
		||||
//@securityDefinitions.apikey bererToken
 | 
			
		||||
//@in header
 | 
			
		||||
//@name Authorization
 | 
			
		||||
//	@securityDefinitions.apikey	JWT
 | 
			
		||||
//	@in							header
 | 
			
		||||
//	@name						Authorization
 | 
			
		||||
//	@description				Use the JWT token provided by the login endpoint to authenticate requests.  **Prefix the token with "Bearer ".**
 | 
			
		||||
 | 
			
		||||
//	@host		localhost:8080
 | 
			
		||||
//	@BasePath	/api
 | 
			
		||||
 | 
			
		||||
// @externalDocs.description	OpenAPI
 | 
			
		||||
// @externalDocs.url			https://swagger.io/resources/open-api/
 | 
			
		||||
//	@externalDocs.description	OpenAPI
 | 
			
		||||
//	@externalDocs.url			https://swagger.io/resources/open-api/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
Main function for starting the server and initializing configurations.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								frontend/.prettierignore
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								frontend/.prettierignore
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
goTypes.ts
 | 
			
		||||
GenApi.ts
 | 
			
		||||
							
								
								
									
										358
									
								
								frontend/src/API/GenApi.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								frontend/src/API/GenApi.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,358 @@
 | 
			
		|||
/* eslint-disable */
 | 
			
		||||
/* tslint:disable */
 | 
			
		||||
/*
 | 
			
		||||
 * ---------------------------------------------------------------
 | 
			
		||||
 * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
 | 
			
		||||
 * ##                                                           ##
 | 
			
		||||
 * ## AUTHOR: acacode                                           ##
 | 
			
		||||
 * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
 | 
			
		||||
 * ---------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export interface TypesNewUser {
 | 
			
		||||
  /** @example "password123" */
 | 
			
		||||
  password?: string;
 | 
			
		||||
  /** @example "username123" */
 | 
			
		||||
  username?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface TypesToken {
 | 
			
		||||
  token?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type QueryParamsType = Record<string | number, any>;
 | 
			
		||||
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
 | 
			
		||||
 | 
			
		||||
export interface FullRequestParams extends Omit<RequestInit, "body"> {
 | 
			
		||||
  /** set parameter to `true` for call `securityWorker` for this request */
 | 
			
		||||
  secure?: boolean;
 | 
			
		||||
  /** request path */
 | 
			
		||||
  path: string;
 | 
			
		||||
  /** content type of request body */
 | 
			
		||||
  type?: ContentType;
 | 
			
		||||
  /** query params */
 | 
			
		||||
  query?: QueryParamsType;
 | 
			
		||||
  /** format of response (i.e. response.json() -> format: "json") */
 | 
			
		||||
  format?: ResponseFormat;
 | 
			
		||||
  /** request body */
 | 
			
		||||
  body?: unknown;
 | 
			
		||||
  /** base url */
 | 
			
		||||
  baseUrl?: string;
 | 
			
		||||
  /** request cancellation token */
 | 
			
		||||
  cancelToken?: CancelToken;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;
 | 
			
		||||
 | 
			
		||||
export interface ApiConfig<SecurityDataType = unknown> {
 | 
			
		||||
  baseUrl?: string;
 | 
			
		||||
  baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
 | 
			
		||||
  securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
 | 
			
		||||
  customFetch?: typeof fetch;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
 | 
			
		||||
  data: D;
 | 
			
		||||
  error: E;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type CancelToken = Symbol | string | number;
 | 
			
		||||
 | 
			
		||||
export enum ContentType {
 | 
			
		||||
  Json = "application/json",
 | 
			
		||||
  FormData = "multipart/form-data",
 | 
			
		||||
  UrlEncoded = "application/x-www-form-urlencoded",
 | 
			
		||||
  Text = "text/plain",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class HttpClient<SecurityDataType = unknown> {
 | 
			
		||||
  public baseUrl: string = "//localhost:8080/api";
 | 
			
		||||
  private securityData: SecurityDataType | null = null;
 | 
			
		||||
  private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
 | 
			
		||||
  private abortControllers = new Map<CancelToken, AbortController>();
 | 
			
		||||
  private customFetch = (...fetchParams: Parameters<typeof fetch>) => fetch(...fetchParams);
 | 
			
		||||
 | 
			
		||||
  private baseApiParams: RequestParams = {
 | 
			
		||||
    credentials: "same-origin",
 | 
			
		||||
    headers: {},
 | 
			
		||||
    redirect: "follow",
 | 
			
		||||
    referrerPolicy: "no-referrer",
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  constructor(apiConfig: ApiConfig<SecurityDataType> = {}) {
 | 
			
		||||
    Object.assign(this, apiConfig);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public setSecurityData = (data: SecurityDataType | null) => {
 | 
			
		||||
    this.securityData = data;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  protected encodeQueryParam(key: string, value: any) {
 | 
			
		||||
    const encodedKey = encodeURIComponent(key);
 | 
			
		||||
    return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected addQueryParam(query: QueryParamsType, key: string) {
 | 
			
		||||
    return this.encodeQueryParam(key, query[key]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected addArrayQueryParam(query: QueryParamsType, key: string) {
 | 
			
		||||
    const value = query[key];
 | 
			
		||||
    return value.map((v: any) => this.encodeQueryParam(key, v)).join("&");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected toQueryString(rawQuery?: QueryParamsType): string {
 | 
			
		||||
    const query = rawQuery || {};
 | 
			
		||||
    const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
 | 
			
		||||
    return keys
 | 
			
		||||
      .map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)))
 | 
			
		||||
      .join("&");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected addQueryParams(rawQuery?: QueryParamsType): string {
 | 
			
		||||
    const queryString = this.toQueryString(rawQuery);
 | 
			
		||||
    return queryString ? `?${queryString}` : "";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private contentFormatters: Record<ContentType, (input: any) => any> = {
 | 
			
		||||
    [ContentType.Json]: (input: any) =>
 | 
			
		||||
      input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
 | 
			
		||||
    [ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input),
 | 
			
		||||
    [ContentType.FormData]: (input: any) =>
 | 
			
		||||
      Object.keys(input || {}).reduce((formData, key) => {
 | 
			
		||||
        const property = input[key];
 | 
			
		||||
        formData.append(
 | 
			
		||||
          key,
 | 
			
		||||
          property instanceof Blob
 | 
			
		||||
            ? property
 | 
			
		||||
            : typeof property === "object" && property !== null
 | 
			
		||||
            ? JSON.stringify(property)
 | 
			
		||||
            : `${property}`,
 | 
			
		||||
        );
 | 
			
		||||
        return formData;
 | 
			
		||||
      }, new FormData()),
 | 
			
		||||
    [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
 | 
			
		||||
    return {
 | 
			
		||||
      ...this.baseApiParams,
 | 
			
		||||
      ...params1,
 | 
			
		||||
      ...(params2 || {}),
 | 
			
		||||
      headers: {
 | 
			
		||||
        ...(this.baseApiParams.headers || {}),
 | 
			
		||||
        ...(params1.headers || {}),
 | 
			
		||||
        ...((params2 && params2.headers) || {}),
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => {
 | 
			
		||||
    if (this.abortControllers.has(cancelToken)) {
 | 
			
		||||
      const abortController = this.abortControllers.get(cancelToken);
 | 
			
		||||
      if (abortController) {
 | 
			
		||||
        return abortController.signal;
 | 
			
		||||
      }
 | 
			
		||||
      return void 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const abortController = new AbortController();
 | 
			
		||||
    this.abortControllers.set(cancelToken, abortController);
 | 
			
		||||
    return abortController.signal;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  public abortRequest = (cancelToken: CancelToken) => {
 | 
			
		||||
    const abortController = this.abortControllers.get(cancelToken);
 | 
			
		||||
 | 
			
		||||
    if (abortController) {
 | 
			
		||||
      abortController.abort();
 | 
			
		||||
      this.abortControllers.delete(cancelToken);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  public request = async <T = any, E = any>({
 | 
			
		||||
    body,
 | 
			
		||||
    secure,
 | 
			
		||||
    path,
 | 
			
		||||
    type,
 | 
			
		||||
    query,
 | 
			
		||||
    format,
 | 
			
		||||
    baseUrl,
 | 
			
		||||
    cancelToken,
 | 
			
		||||
    ...params
 | 
			
		||||
  }: FullRequestParams): Promise<HttpResponse<T, E>> => {
 | 
			
		||||
    const secureParams =
 | 
			
		||||
      ((typeof secure === "boolean" ? secure : this.baseApiParams.secure) &&
 | 
			
		||||
        this.securityWorker &&
 | 
			
		||||
        (await this.securityWorker(this.securityData))) ||
 | 
			
		||||
      {};
 | 
			
		||||
    const requestParams = this.mergeRequestParams(params, secureParams);
 | 
			
		||||
    const queryString = query && this.toQueryString(query);
 | 
			
		||||
    const payloadFormatter = this.contentFormatters[type || ContentType.Json];
 | 
			
		||||
    const responseFormat = format || requestParams.format;
 | 
			
		||||
 | 
			
		||||
    return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, {
 | 
			
		||||
      ...requestParams,
 | 
			
		||||
      headers: {
 | 
			
		||||
        ...(requestParams.headers || {}),
 | 
			
		||||
        ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
 | 
			
		||||
      },
 | 
			
		||||
      signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
 | 
			
		||||
      body: typeof body === "undefined" || body === null ? null : payloadFormatter(body),
 | 
			
		||||
    }).then(async (response) => {
 | 
			
		||||
      const r = response as HttpResponse<T, E>;
 | 
			
		||||
      r.data = null as unknown as T;
 | 
			
		||||
      r.error = null as unknown as E;
 | 
			
		||||
 | 
			
		||||
      const data = !responseFormat
 | 
			
		||||
        ? r
 | 
			
		||||
        : await response[responseFormat]()
 | 
			
		||||
            .then((data) => {
 | 
			
		||||
              if (r.ok) {
 | 
			
		||||
                r.data = data;
 | 
			
		||||
              } else {
 | 
			
		||||
                r.error = data;
 | 
			
		||||
              }
 | 
			
		||||
              return r;
 | 
			
		||||
            })
 | 
			
		||||
            .catch((e) => {
 | 
			
		||||
              r.error = e;
 | 
			
		||||
              return r;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
      if (cancelToken) {
 | 
			
		||||
        this.abortControllers.delete(cancelToken);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!response.ok) throw data;
 | 
			
		||||
      return data;
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @title TTime API
 | 
			
		||||
 * @version 0.0.1
 | 
			
		||||
 * @license AGPL (https://www.gnu.org/licenses/agpl-3.0.html)
 | 
			
		||||
 * @baseUrl //localhost:8080/api
 | 
			
		||||
 * @externalDocs https://swagger.io/resources/open-api/
 | 
			
		||||
 * @contact
 | 
			
		||||
 *
 | 
			
		||||
 * This is the API for TTime, a time tracking application.
 | 
			
		||||
 */
 | 
			
		||||
export class GenApi<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
 | 
			
		||||
  login = {
 | 
			
		||||
    /**
 | 
			
		||||
     * @description Logs in a user and returns a JWT token
 | 
			
		||||
     *
 | 
			
		||||
     * @tags Auth
 | 
			
		||||
     * @name LoginCreate
 | 
			
		||||
     * @summary Login
 | 
			
		||||
     * @request POST:/login
 | 
			
		||||
     */
 | 
			
		||||
    loginCreate: (body: TypesNewUser, params: RequestParams = {}) =>
 | 
			
		||||
      this.request<TypesToken, string>({
 | 
			
		||||
        path: `/login`,
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        body: body,
 | 
			
		||||
        type: ContentType.Json,
 | 
			
		||||
        format: "json",
 | 
			
		||||
        ...params,
 | 
			
		||||
      }),
 | 
			
		||||
  };
 | 
			
		||||
  loginrenew = {
 | 
			
		||||
    /**
 | 
			
		||||
     * @description Renews the users token.
 | 
			
		||||
     *
 | 
			
		||||
     * @tags Auth
 | 
			
		||||
     * @name LoginrenewCreate
 | 
			
		||||
     * @summary LoginRenews
 | 
			
		||||
     * @request POST:/loginrenew
 | 
			
		||||
     * @secure
 | 
			
		||||
     */
 | 
			
		||||
    loginrenewCreate: (params: RequestParams = {}) =>
 | 
			
		||||
      this.request<TypesToken, string>({
 | 
			
		||||
        path: `/loginrenew`,
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        secure: true,
 | 
			
		||||
        format: "json",
 | 
			
		||||
        ...params,
 | 
			
		||||
      }),
 | 
			
		||||
  };
 | 
			
		||||
  promoteToAdmin = {
 | 
			
		||||
    /**
 | 
			
		||||
     * @description Promote chosen user to site admin
 | 
			
		||||
     *
 | 
			
		||||
     * @tags User
 | 
			
		||||
     * @name PromoteToAdminCreate
 | 
			
		||||
     * @summary PromoteToAdmin
 | 
			
		||||
     * @request POST:/promoteToAdmin
 | 
			
		||||
     * @secure
 | 
			
		||||
     */
 | 
			
		||||
    promoteToAdminCreate: (NewUser: TypesNewUser, params: RequestParams = {}) =>
 | 
			
		||||
      this.request<TypesToken, string>({
 | 
			
		||||
        path: `/promoteToAdmin`,
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        body: NewUser,
 | 
			
		||||
        secure: true,
 | 
			
		||||
        type: ContentType.Json,
 | 
			
		||||
        ...params,
 | 
			
		||||
      }),
 | 
			
		||||
  };
 | 
			
		||||
  register = {
 | 
			
		||||
    /**
 | 
			
		||||
     * @description Register a new user
 | 
			
		||||
     *
 | 
			
		||||
     * @tags Auth
 | 
			
		||||
     * @name RegisterCreate
 | 
			
		||||
     * @summary Register
 | 
			
		||||
     * @request POST:/register
 | 
			
		||||
     */
 | 
			
		||||
    registerCreate: (NewUser: TypesNewUser, params: RequestParams = {}) =>
 | 
			
		||||
      this.request<string, string>({
 | 
			
		||||
        path: `/register`,
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        body: NewUser,
 | 
			
		||||
        type: ContentType.Json,
 | 
			
		||||
        ...params,
 | 
			
		||||
      }),
 | 
			
		||||
  };
 | 
			
		||||
  userdelete = {
 | 
			
		||||
    /**
 | 
			
		||||
     * @description UserDelete deletes a user from the database
 | 
			
		||||
     *
 | 
			
		||||
     * @tags User
 | 
			
		||||
     * @name UserdeleteDelete
 | 
			
		||||
     * @summary UserDelete
 | 
			
		||||
     * @request DELETE:/userdelete/{username}
 | 
			
		||||
     * @secure
 | 
			
		||||
     */
 | 
			
		||||
    userdeleteDelete: (username: string, params: RequestParams = {}) =>
 | 
			
		||||
      this.request<string, string>({
 | 
			
		||||
        path: `/userdelete/${username}`,
 | 
			
		||||
        method: "DELETE",
 | 
			
		||||
        secure: true,
 | 
			
		||||
        type: ContentType.Json,
 | 
			
		||||
        ...params,
 | 
			
		||||
      }),
 | 
			
		||||
  };
 | 
			
		||||
  users = {
 | 
			
		||||
    /**
 | 
			
		||||
     * @description lists all users
 | 
			
		||||
     *
 | 
			
		||||
     * @tags User
 | 
			
		||||
     * @name GetUsers
 | 
			
		||||
     * @summary ListsAllUsers
 | 
			
		||||
     * @request GET:/users/all
 | 
			
		||||
     * @secure
 | 
			
		||||
     */
 | 
			
		||||
    getUsers: (params: RequestParams = {}) =>
 | 
			
		||||
      this.request<string[], string>({
 | 
			
		||||
        path: `/users/all`,
 | 
			
		||||
        method: "GET",
 | 
			
		||||
        secure: true,
 | 
			
		||||
        format: "json",
 | 
			
		||||
        ...params,
 | 
			
		||||
      }),
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								frontend/src/Containers/GenApiDemo.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								frontend/src/Containers/GenApiDemo.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
import { useEffect } from "react";
 | 
			
		||||
import { GenApi } from "../API/GenApi";
 | 
			
		||||
 | 
			
		||||
// Instantiation of the API
 | 
			
		||||
const api = new GenApi();
 | 
			
		||||
 | 
			
		||||
export function GenApiDemo(): JSX.Element {
 | 
			
		||||
  // Sync wrapper around the loginCreate method
 | 
			
		||||
  const register = async (): Promise<void> => {
 | 
			
		||||
    const response = await api.login.loginCreate({
 | 
			
		||||
      username: "admin",
 | 
			
		||||
      password: "admin",
 | 
			
		||||
    });
 | 
			
		||||
    console.log(response.data); // This should be the inner type of the response
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Call the wrapper
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    void register();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return <></>;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +124,44 @@ export interface WeeklyReport {
 | 
			
		|||
   */
 | 
			
		||||
  signedBy?: number /* int */;
 | 
			
		||||
}
 | 
			
		||||
export interface UpdateWeeklyReport {
 | 
			
		||||
  /**
 | 
			
		||||
   * The name of the project, as it appears in the database
 | 
			
		||||
   */
 | 
			
		||||
  projectName: string;
 | 
			
		||||
  /**
 | 
			
		||||
   * The name of the user
 | 
			
		||||
   */
 | 
			
		||||
  userName: string;
 | 
			
		||||
  /**
 | 
			
		||||
   * The week number
 | 
			
		||||
   */
 | 
			
		||||
  week: number /* int */;
 | 
			
		||||
  /**
 | 
			
		||||
   * Total time spent on development
 | 
			
		||||
   */
 | 
			
		||||
  developmentTime: number /* int */;
 | 
			
		||||
  /**
 | 
			
		||||
   * Total time spent in meetings
 | 
			
		||||
   */
 | 
			
		||||
  meetingTime: number /* int */;
 | 
			
		||||
  /**
 | 
			
		||||
   * Total time spent on administrative tasks
 | 
			
		||||
   */
 | 
			
		||||
  adminTime: number /* int */;
 | 
			
		||||
  /**
 | 
			
		||||
   * Total time spent on personal projects
 | 
			
		||||
   */
 | 
			
		||||
  ownWorkTime: number /* int */;
 | 
			
		||||
  /**
 | 
			
		||||
   * Total time spent on studying
 | 
			
		||||
   */
 | 
			
		||||
  studyTime: number /* int */;
 | 
			
		||||
  /**
 | 
			
		||||
   * Total time spent on testing
 | 
			
		||||
   */
 | 
			
		||||
  testingTime: number /* int */;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//////////
 | 
			
		||||
// source: project.go
 | 
			
		||||
| 
						 | 
				
			
			@ -151,16 +189,9 @@ export interface NewProject {
 | 
			
		|||
 */
 | 
			
		||||
export interface RoleChange {
 | 
			
		||||
  username: string;
 | 
			
		||||
  role: "project_manager" | "user";
 | 
			
		||||
  role: 'project_manager' | 'user';
 | 
			
		||||
  projectname: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface NewProjMember {
 | 
			
		||||
  username: string;
 | 
			
		||||
  projectname: string;
 | 
			
		||||
  role: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface NameChange {
 | 
			
		||||
  id: number /* int */;
 | 
			
		||||
  name: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -191,11 +222,6 @@ export interface PublicUser {
 | 
			
		|||
  userId: string;
 | 
			
		||||
  username: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface UserProjectMember {
 | 
			
		||||
  Username: string;
 | 
			
		||||
  UserRole: string;
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * wrapper type for token
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue