From 2873b269b9f24a34e9084762914f6002fedb1eda Mon Sep 17 00:00:00 2001 From: Denis Istratov Date: Fri, 25 Oct 2024 12:00:00 +0300 Subject: [PATCH] update api.go, docs.go, swagger.json, swagger.yaml --- docs/docs.go | 530 +++++++++++++++++++++++++++++++++++++++++++++ docs/swagger.json | 506 +++++++++++++++++++++++++++++++++++++++++++ docs/swagger.yaml | 330 ++++++++++++++++++++++++++++ pkg/handler/api.go | 390 +++++++++++++++++++++++++++++++++ 4 files changed, 1756 insertions(+) create mode 100644 docs/docs.go create mode 100644 docs/swagger.json create mode 100644 docs/swagger.yaml create mode 100644 pkg/handler/api.go diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..762114f --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,530 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "name": "API Support", + "email": "red.tea.dev@gmail.com" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/member/invite/": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "invite member (only for owner of project)", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "invite data in json format", + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.memberRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/esproject.ProjectInvite" + } + } + } + } + }, + "/api/member/invite/accept/{id}": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "accept invite", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "id of invite", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/api/member/invite/my": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "invites of current user", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + }, + "/api/member/invite/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "invite by id", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + }, + "/api/member/kick/": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "kick member (only for owner of project)", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "invite data in json format", + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.memberRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/api/project": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Create new project", + "parameters": [ + { + "description": "Project data in JSON", + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.projectRequest" + } + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "created project", + "schema": { + "$ref": "#/definitions/esproject.Project" + } + } + } + } + }, + "/api/project/": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Edit project (only for owner of project)", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "edited project", + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/esproject.Project" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/esproject.Project" + } + } + } + } + }, + "/api/project/all": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Get ids of projects", + "parameters": [ + { + "type": "integer", + "description": "limit of projects size", + "name": "limit", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "select offset", + "name": "start", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + }, + "/api/project/count": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Get count of projects", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.countResponse" + } + } + } + } + }, + "/api/project/my": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Get ids of projects when user is member", + "parameters": [ + { + "type": "integer", + "description": "limit of projects size", + "name": "limit", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + }, + "/api/project/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Get project by id", + "parameters": [ + { + "type": "integer", + "description": "id of project", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Delete project by id (only for owner of project)", + "parameters": [ + { + "type": "integer", + "description": "id of project", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "definitions": { + "esproject.Project": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "members": { + "type": "array", + "items": { + "type": "integer" + } + }, + "name": { + "type": "string" + }, + "owner_id": { + "type": "integer" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "esproject.ProjectInvite": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "project": { + "type": "integer" + }, + "user": { + "type": "integer" + } + } + }, + "handler.countResponse": { + "type": "object", + "properties": { + "count": { + "type": "integer" + } + } + }, + "handler.memberRequest": { + "type": "object", + "properties": { + "project": { + "type": "integer" + }, + "user": { + "type": "integer" + } + } + }, + "handler.projectRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "owner_id": { + "type": "integer" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "project.easystartup.su", + BasePath: "/api", + Schemes: []string{}, + Title: "EasyStartup Project", + Description: "Project service for EasyStartup.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 0000000..54f83c5 --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,506 @@ +{ + "swagger": "2.0", + "info": { + "description": "Project service for EasyStartup.", + "title": "EasyStartup Project", + "contact": { + "name": "API Support", + "email": "red.tea.dev@gmail.com" + }, + "version": "1.0" + }, + "host": "project.easystartup.su", + "basePath": "/api", + "paths": { + "/api/member/invite/": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "invite member (only for owner of project)", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "invite data in json format", + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.memberRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/esproject.ProjectInvite" + } + } + } + } + }, + "/api/member/invite/accept/{id}": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "accept invite", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "id of invite", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/api/member/invite/my": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "invites of current user", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + }, + "/api/member/invite/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "invite by id", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + }, + "/api/member/kick/": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "member" + ], + "summary": "kick member (only for owner of project)", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "invite data in json format", + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.memberRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/api/project": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Create new project", + "parameters": [ + { + "description": "Project data in JSON", + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/handler.projectRequest" + } + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "created project", + "schema": { + "$ref": "#/definitions/esproject.Project" + } + } + } + } + }, + "/api/project/": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Edit project (only for owner of project)", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "edited project", + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/esproject.Project" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/esproject.Project" + } + } + } + } + }, + "/api/project/all": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Get ids of projects", + "parameters": [ + { + "type": "integer", + "description": "limit of projects size", + "name": "limit", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "select offset", + "name": "start", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + }, + "/api/project/count": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Get count of projects", + "parameters": [ + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.countResponse" + } + } + } + } + }, + "/api/project/my": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Get ids of projects when user is member", + "parameters": [ + { + "type": "integer", + "description": "limit of projects size", + "name": "limit", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + } + }, + "/api/project/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Get project by id", + "parameters": [ + { + "type": "integer", + "description": "id of project", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Delete project by id (only for owner of project)", + "parameters": [ + { + "type": "integer", + "description": "id of project", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "JWT Bearer token (authorization)", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "definitions": { + "esproject.Project": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "members": { + "type": "array", + "items": { + "type": "integer" + } + }, + "name": { + "type": "string" + }, + "owner_id": { + "type": "integer" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "esproject.ProjectInvite": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "project": { + "type": "integer" + }, + "user": { + "type": "integer" + } + } + }, + "handler.countResponse": { + "type": "object", + "properties": { + "count": { + "type": "integer" + } + } + }, + "handler.memberRequest": { + "type": "object", + "properties": { + "project": { + "type": "integer" + }, + "user": { + "type": "integer" + } + } + }, + "handler.projectRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "owner_id": { + "type": "integer" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..c7221a6 --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,330 @@ +basePath: /api +definitions: + esproject.Project: + properties: + id: + type: integer + members: + items: + type: integer + type: array + name: + type: string + owner_id: + type: integer + tags: + items: + type: string + type: array + type: object + esproject.ProjectInvite: + properties: + id: + type: integer + project: + type: integer + user: + type: integer + type: object + handler.countResponse: + properties: + count: + type: integer + type: object + handler.memberRequest: + properties: + project: + type: integer + user: + type: integer + type: object + handler.projectRequest: + properties: + name: + type: string + owner_id: + type: integer + tags: + items: + type: string + type: array + type: object +host: project.easystartup.su +info: + contact: + email: red.tea.dev@gmail.com + name: API Support + description: Project service for EasyStartup. + title: EasyStartup Project + version: "1.0" +paths: + /api/member/invite/: + post: + parameters: + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + - description: invite data in json format + in: body + name: project + required: true + schema: + $ref: '#/definitions/handler.memberRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/esproject.ProjectInvite' + summary: invite member (only for owner of project) + tags: + - member + /api/member/invite/{id}: + get: + parameters: + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: integer + type: array + summary: invite by id + tags: + - member + /api/member/invite/accept/{id}: + post: + parameters: + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + - description: id of invite + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + summary: accept invite + tags: + - member + /api/member/invite/my: + get: + parameters: + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: integer + type: array + summary: invites of current user + tags: + - member + /api/member/kick/: + post: + parameters: + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + - description: invite data in json format + in: body + name: project + required: true + schema: + $ref: '#/definitions/handler.memberRequest' + produces: + - application/json + responses: + "200": + description: OK + summary: kick member (only for owner of project) + tags: + - member + /api/project: + post: + parameters: + - description: Project data in JSON + in: body + name: project + required: true + schema: + $ref: '#/definitions/handler.projectRequest' + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: created project + schema: + $ref: '#/definitions/esproject.Project' + summary: Create new project + tags: + - project + /api/project/: + post: + parameters: + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + - description: edited project + in: body + name: project + required: true + schema: + $ref: '#/definitions/esproject.Project' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/esproject.Project' + summary: Edit project (only for owner of project) + tags: + - project + /api/project/{id}: + delete: + parameters: + - description: id of project + in: path + name: id + required: true + type: integer + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + summary: Delete project by id (only for owner of project) + tags: + - project + get: + parameters: + - description: id of project + in: path + name: id + required: true + type: integer + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + summary: Get project by id + tags: + - project + /api/project/all: + get: + parameters: + - description: limit of projects size + in: query + name: limit + required: true + type: integer + - description: select offset + in: query + name: start + required: true + type: integer + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: integer + type: array + summary: Get ids of projects + tags: + - project + /api/project/count: + get: + parameters: + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.countResponse' + summary: Get count of projects + tags: + - project + /api/project/my: + get: + parameters: + - description: limit of projects size + in: query + name: limit + required: true + type: integer + - description: JWT Bearer token (authorization) + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: integer + type: array + summary: Get ids of projects when user is member + tags: + - project +swagger: "2.0" diff --git a/pkg/handler/api.go b/pkg/handler/api.go new file mode 100644 index 0000000..11636ec --- /dev/null +++ b/pkg/handler/api.go @@ -0,0 +1,390 @@ +package handler + +import ( + "esproject" + "esproject/pkg/util" + "fmt" + "net/http" + "slices" + "strconv" + + "github.com/gin-gonic/gin" +) + +type projectRequest struct { + Name string `json:"name"` + OwnerId int64 `json:"owner_id"` + Tags []string `json:"tags"` +} + +// project godoc +// @Summary Create new project +// @Tags project +// @Produce json +// @Param project body handler.projectRequest true "Project data in JSON" +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Success 200 {object} esproject.Project "created project" +// @Router /api/project [post] +func (h *Handler) create(c *gin.Context) { + current, _ := c.Get(userCtx) + var input projectRequest + + if (input.OwnerId != current) && (!util.MatchRole(c, "admin")) { + util.NewErrorResponse(c, http.StatusForbidden, "no perms") + return + } + + if err := c.BindJSON(&input); err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + project, err := h.repo.CreateProject(input.OwnerId, input.Name, input.Tags) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + c.JSON(http.StatusOK, project) +} + +// project/all godoc +// @Summary Get ids of projects +// @Tags project +// @Produce json +// @Param limit query int true "limit of projects size" +// @Param start query int true "select offset" +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Success 200 {array} int +// @Router /api/project/all [get] +func (h *Handler) all(c *gin.Context) { + limit, err := strconv.Atoi(c.Query("limit")) + if err != nil { + limit = 10 + } + start, err := strconv.Atoi(c.Query("start")) + if err != nil { + start = 0 + } + + c.JSON(http.StatusOK, h.repo.GetProjects(start, limit)) +} + +type countResponse struct { + Count int64 `json:"count"` +} + +// user/count godoc +// @Summary Get count of projects +// @Tags project +// @Produce json +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Success 200 {object} handler.countResponse +// @Router /api/project/count [get] +func (h *Handler) count(c *gin.Context) { + c.JSON(http.StatusOK, countResponse{Count: h.repo.GetProjectsCount()}) +} + +// project/delete godoc +// @Summary Delete project by id (only for owner of project) +// @Tags project +// @Produce json +// @Param id path int true "id of project" +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Success 200 +// @Router /api/project/{id} [delete] +func (h *Handler) delete(c *gin.Context) { + id, _ := strconv.ParseInt(c.Param("id"), 10, 64) + project, err := h.repo.GetProject(id) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + current, _ := c.Get(userCtx) + currentUser, err := h.services.AuthorizeById(current.(int64)) + if (err != nil) || ((project.Id != currentUser.Id) && (currentUser.Access != "admin")) { + util.NewErrorResponse(c, http.StatusForbidden, err.Error()) + return + } + + err = h.repo.DeleteProject(id) + if err != nil { + util.NewErrorResponse(c, http.StatusInternalServerError, err.Error()) + return + } + + c.JSON(http.StatusOK, "{ \"status\": \"ok\" }") +} + +// project godoc +// @Summary Get project by id +// @Tags project +// @Produce json +// @Param id path int true "id of project" +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Success 200 +// @Router /api/project/{id} [get] +func (h *Handler) getProject(c *gin.Context) { + id, _ := strconv.ParseInt(c.Param("id"), 10, 64) + project, err := h.repo.GetProject(id) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + c.JSON(http.StatusOK, project) +} + +// project godoc +// @Summary Edit project (only for owner of project) +// @Tags project +// @Produce json +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Param project body esproject.Project true "edited project" +// @Success 200 {object} esproject.Project +// @Router /api/project/ [post] +func (h *Handler) editProject(c *gin.Context) { + id, _ := strconv.ParseInt(c.Param("id"), 10, 64) + project, err := h.repo.GetProject(id) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + current, _ := c.Get(userCtx) + currentUser, err := h.services.AuthorizeById(current.(int64)) + if (err != nil) || ((project.Id != currentUser.Id) && (currentUser.Access != "admin")) { + util.NewErrorResponse(c, http.StatusForbidden, err.Error()) + return + } + + var input esproject.Project + if err := c.BindJSON(&input); err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + input.Id = project.Id + + response, err := h.repo.SaveProject(&input) + if err != nil { + return + } + + c.JSON(http.StatusOK, response) +} + +type memberRequest struct { + Project int64 `json:"project"` + User int64 `json:"user"` +} + +// member godoc +// @Summary invite member (only for owner of project) +// @Tags member +// @Produce json +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Param project body handler.memberRequest true "invite data in json format" +// @Success 200 {object} esproject.ProjectInvite +// @Router /api/member/invite/ [post] +func (h *Handler) invite(c *gin.Context) { + var input memberRequest + if err := c.BindJSON(&input); err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + project, err := h.repo.GetProject(input.Project) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + current, _ := c.Get(userCtx) + currentUser, err := h.services.AuthorizeById(current.(int64)) + if (err != nil) || ((input.User != project.OwnerId) && (currentUser.Access != "admin")) { + util.NewErrorResponse(c, http.StatusForbidden, err.Error()) + return + } + + invite, err := h.repo.CreateInvite(input.Project, input.User) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + c.JSON(http.StatusOK, invite) +} + +// member godoc +// @Summary invites of current user +// @Tags member +// @Produce json +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Success 200 {array} int +// @Router /api/member/invite/my [get] +func (h *Handler) myInvites(c *gin.Context) { + + current, _ := c.Get(userCtx) + currentUser, err := h.services.AuthorizeById(current.(int64)) + if err != nil { + util.NewErrorResponse(c, http.StatusForbidden, err.Error()) + return + } + + invites, err := h.repo.FindInvites("user = ?", fmt.Sprintf("%d", currentUser.Id)) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + var ans []int64 + for _, inv := range invites { + ans = append(ans, inv.Id) + } + c.JSON(http.StatusOK, ans) +} + +// member godoc +// @Summary accept invite +// @Tags member +// @Produce json +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Param id path int true "id of invite" +// @Success 200 +// @Router /api/member/invite/accept/{id} [post] +func (h *Handler) accept(c *gin.Context) { + current, _ := c.Get(userCtx) + + id, _ := strconv.ParseInt(c.Param("id"), 10, 64) + invite, err := h.repo.GetInvite(id) + if (err != nil) || (invite.User != current) { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + project, err := h.repo.GetProject(invite.Project) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + project.Members = append(project.Members, invite.User) + + err = h.repo.DeleteInvite(invite.Id) + if err != nil { + util.NewErrorResponse(c, http.StatusInternalServerError, err.Error()) + return + } + _, err = h.repo.SaveProject(project) + if err != nil { + util.NewErrorResponse(c, http.StatusInternalServerError, err.Error()) + return + } + + c.JSON(http.StatusOK, "{ \"status\": \"ok\" }") +} + +// member godoc +// @Summary kick member (only for owner of project) +// @Tags member +// @Produce json +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Param project body handler.memberRequest true "invite data in json format" +// @Success 200 +// @Router /api/member/kick/ [post] +func (h *Handler) kick(c *gin.Context) { + var input memberRequest + if err := c.BindJSON(&input); err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + project, err := h.repo.GetProject(input.Project) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + current, _ := c.Get(userCtx) + currentUser, err := h.services.AuthorizeById(current.(int64)) + if (err != nil) || ((input.User != project.OwnerId) && (currentUser.Access != "admin")) { + util.NewErrorResponse(c, http.StatusForbidden, err.Error()) + return + } + + if !slices.Contains(project.Members, input.User) { + util.NewErrorResponse(c, http.StatusBadRequest, "user is not in project") + return + } + + project.Members = removeElement(project.Members, input.User) + c.JSON(http.StatusOK, "{ \"status\": \"ok\" }") +} + +// project/my godoc +// @Summary Get ids of projects when user is member +// @Tags project +// @Produce json +// @Param limit query int true "limit of projects size" +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Success 200 {array} int +// @Router /api/project/my [get] +func (h *Handler) my(c *gin.Context) { + limit, err := strconv.Atoi(c.Query("limit")) + current, _ := c.Get(userCtx) + if err != nil { + limit = 10 + } + var result []int64 + for _, p := range h.repo.GetProjects(0, int(h.repo.GetProjectsCount())) { + if (len(result) == limit) && (limit > 0) { + break + } + project, err := h.repo.GetProject(p) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + if (project.OwnerId == current.(int64)) || containsElement(project.Members, current.(int64)) { + result = append(result, project.Id) + } + } + + c.JSON(http.StatusOK, result) +} + +// member godoc +// @Summary invite by id +// @Tags member +// @Produce json +// @Param Authorization header string true "JWT Bearer token (authorization)" +// @Success 200 {array} int +// @Router /api/member/invite/{id} [get] +func (h *Handler) getInvite(c *gin.Context) { + id, _ := strconv.ParseInt(c.Param("id"), 10, 64) + invite, err := h.repo.GetInvite(id) + if err != nil { + util.NewErrorResponse(c, http.StatusBadRequest, err.Error()) + return + } + + c.JSON(http.StatusOK, invite) +} + +func removeElement(slice []int64, value int64) []int64 { + // Find the index of the value + for i, v := range slice { + if v == value { + return append(slice[:i], slice[i+1:]...) + } + } + return slice +} + +func containsElement(slice []int64, value int64) bool { + // Find the index of the value + for _, v := range slice { + if v == value { + return true + } + } + return false +}