1
0
mirror of https://github.com/mattermost/focalboard.git synced 2025-01-11 18:13:52 +02:00

Add Swagger / OpenAPI docs

This commit is contained in:
Chen-I Lim 2021-02-17 11:29:20 -08:00
parent 13d48c5a8f
commit 1ad985232d
16 changed files with 11793 additions and 131 deletions

1
.gitignore vendored
View File

@ -59,3 +59,4 @@ win/temp
win/dist
webapp/cypress/screenshots
webapp/cypress/videos
server/swagger/clients

View File

@ -133,6 +133,18 @@ linux-app: server-linux webapp
cd linux/temp; tar -zcf ../dist/focalboard-linux.tar.gz focalboard-app
rm -rf linux/temp
swagger:
mkdir -p server/swagger/docs
mkdir -p server/swagger/clients
cd server && swagger generate spec -m -o ./swagger/swagger.yml
cd server/swagger && openapi-generator generate -i swagger.yml -g html2 -o docs/html
cd server/swagger && openapi-generator generate -i swagger.yml -g go -o clients/go
cd server/swagger && openapi-generator generate -i swagger.yml -g javascript -o clients/javascript
cd server/swagger && openapi-generator generate -i swagger.yml -g typescript-fetch -o clients/typescript
cd server/swagger && openapi-generator generate -i swagger.yml -g swift5 -o clients/swift
cd server/swagger && openapi-generator generate -i swagger.yml -g python -o clients/python
clean:
rm -rf bin
rm -rf dist

View File

@ -20,29 +20,29 @@ func (a *API) handleAdminSetPassword(w http.ResponseWriter, r *http.Request) {
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
var requestData AdminSetPasswordData
err = json.Unmarshal(requestBody, &requestData)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
if !strings.Contains(requestData.Password, "") {
errorResponse(w, http.StatusInternalServerError, map[string]string{"error": "password is required"}, err)
errorResponse(w, http.StatusBadRequest, "password is required", err)
return
}
err = a.app().UpdateUserPassword(username, requestData.Password)
if err != nil {
errorResponse(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
log.Printf("AdminSetPassword, username: %s", username)
jsonBytesResponse(w, http.StatusOK, nil)
jsonStringResponse(w, http.StatusOK, "{}")
}

View File

@ -83,7 +83,7 @@ func (a *API) requireCSRFToken(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !a.checkCSRFToken(r) {
log.Println("checkCSRFToken FAILED")
errorResponse(w, http.StatusBadRequest, nil, nil)
errorResponse(w, http.StatusBadRequest, "", nil)
return
}
@ -102,13 +102,45 @@ func (a *API) checkCSRFToken(r *http.Request) bool {
}
func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/blocks getBlocks
//
// Returns blocks
//
// ---
// produces:
// - application/json
// parameters:
// - name: parent_id
// in: query
// description: ID of parent block, omit to specify all blocks
// required: false
// type: string
// - name: type
// in: query
// description: Type of blocks to return, omit to specify all types
// required: false
// type: string
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// schema:
// type: array
// items:
// "$ref": "#/definitions/Block"
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
query := r.URL.Query()
parentID := query.Get("parent_id")
blockType := query.Get("type")
blocks, err := a.app().GetBlocks(parentID, blockType)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -116,7 +148,7 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
json, err := json.Marshal(blocks)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -137,9 +169,35 @@ func stampModifiedByUser(r *http.Request, blocks []model.Block) {
}
func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/blocks updateBlocks
//
// Insert or update blocks
//
// ---
// produces:
// - application/json
// parameters:
// - name: Body
// in: body
// description: array of blocks to insert or update
// required: true
// schema:
// type: array
// items:
// "$ref": "#/definitions/Block"
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -147,27 +205,27 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
err = json.Unmarshal(requestBody, &blocks)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
for _, block := range blocks {
// Error checking
if len(block.Type) < 1 {
errorData := map[string]string{"description": "missing type", "id": block.ID}
errorResponse(w, http.StatusBadRequest, errorData, nil)
message := fmt.Sprintf("missing type for block id %s", block.ID)
errorResponse(w, http.StatusBadRequest, message, nil)
return
}
if block.CreateAt < 1 {
errorData := map[string]string{"description": "invalid createAt", "id": block.ID}
errorResponse(w, http.StatusBadRequest, errorData, nil)
message := fmt.Sprintf("invalid createAt for block id %s", block.ID)
errorResponse(w, http.StatusBadRequest, message, nil)
return
}
if block.UpdateAt < 1 {
errorData := map[string]string{"description": "invalid UpdateAt", "id": block.ID}
errorResponse(w, http.StatusBadRequest, errorData, nil)
message := fmt.Sprintf("invalid UpdateAt for block id %s", block.ID)
errorResponse(w, http.StatusBadRequest, message, nil)
return
}
}
@ -176,7 +234,7 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
err = a.app().InsertBlocks(blocks)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -185,25 +243,69 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handleGetUser(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/users/{userID} getUser
//
// Returns a user
//
// ---
// produces:
// - application/json
// parameters:
// - name: userID
// in: path
// description: User ID
// required: true
// type: string
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// schema:
// "$ref": "#/definitions/User"
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
vars := mux.Vars(r)
userID := vars["userID"]
user, err := a.app().GetUser(userID)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
userData, err := json.Marshal(user)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
jsonStringResponse(w, http.StatusOK, string(userData))
jsonBytesResponse(w, http.StatusOK, userData)
}
func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/users/me getMe
//
// Returns the currently logged-in user
//
// ---
// produces:
// - application/json
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// schema:
// "$ref": "#/definitions/User"
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
ctx := r.Context()
session := ctx.Value("session").(*model.Session)
var user *model.User
@ -221,21 +323,44 @@ func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
} else {
user, err = a.app().GetUser(session.UserID)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
}
userData, err := json.Marshal(user)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
jsonStringResponse(w, http.StatusOK, string(userData))
jsonBytesResponse(w, http.StatusOK, userData)
}
func (a *API) handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
// swagger:operation DELETE /api/v1/blocks/{blockID} deleteBlock
//
// Deletes a block
//
// ---
// produces:
// - application/json
// parameters:
// - name: blockID
// in: path
// description: ID of block to delete
// required: true
// type: string
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
ctx := r.Context()
session := ctx.Value("session").(*model.Session)
userID := session.UserID
@ -245,7 +370,7 @@ func (a *API) handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
err := a.app().DeleteBlock(blockID, userID)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -255,6 +380,40 @@ func (a *API) handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/blocks/{blockID}/subtree getSubTree
//
// Returns the blocks of a subtree
//
// ---
// produces:
// - application/json
// parameters:
// - name: blockID
// in: path
// description: The ID of the root block of the subtree
// required: true
// type: string
// - name: l
// in: query
// description: The number of levels to return. 2 or 3. Defaults to 2.
// required: false
// type: integer
// minimum: 2
// maximum: 3
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// schema:
// type: array
// items:
// "$ref": "#/definitions/Block"
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
vars := mux.Vars(r)
blockID := vars["blockID"]
@ -267,18 +426,18 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
// Require read token
if len(readToken) < 1 {
errorResponse(w, http.StatusUnauthorized, map[string]string{"error": "No read_token"}, nil)
errorResponse(w, http.StatusBadRequest, "No read_token", nil)
return
}
isValid, err := a.app().IsValidReadToken(blockID, readToken)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
if !isValid {
errorResponse(w, http.StatusUnauthorized, nil, nil)
errorResponse(w, http.StatusUnauthorized, "", nil)
return
}
}
@ -291,21 +450,20 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
if levels != 2 && levels != 3 {
log.Printf(`ERROR Invalid levels: %d`, levels)
errorData := map[string]string{"description": "invalid levels"}
errorResponse(w, http.StatusInternalServerError, errorData, nil)
errorResponse(w, http.StatusBadRequest, "invalid levels", nil)
return
}
blocks, err := a.app().GetSubTree(blockID, int(levels))
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
log.Printf("GetSubTree (%v) blockID: %s, %d result(s)", levels, blockID, len(blocks))
json, err := json.Marshal(blocks)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -313,9 +471,30 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
}
func (a *API) handleExport(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/blocks/export exportBlocks
//
// Returns all blocks
//
// ---
// produces:
// - application/json
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// schema:
// type: array
// items:
// "$ref": "#/definitions/Block"
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
blocks, err := a.app().GetAllBlocks()
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -325,7 +504,7 @@ func (a *API) handleExport(w http.ResponseWriter, r *http.Request) {
json, err := json.Marshal(blocks)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -377,9 +556,35 @@ func arrayContainsBlockWithID(array []model.Block, blockID string) bool {
}
func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/blocks/import importBlocks
//
// Import blocks
//
// ---
// produces:
// - application/json
// parameters:
// - name: Body
// in: body
// description: array of blocks to import
// required: true
// schema:
// type: array
// items:
// "$ref": "#/definitions/Block"
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -387,8 +592,7 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
err = json.Unmarshal(requestBody, &blocks)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -396,8 +600,7 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
err = a.app().InsertBlocks(blocks)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -408,29 +611,83 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
// Sharing
func (a *API) handleGetSharing(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/sharing/{rootID} getSharing
//
// Returns sharing information for a root block
//
// ---
// produces:
// - application/json
// parameters:
// - name: rootID
// in: path
// description: ID of the root block
// required: true
// type: string
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// schema:
// "$ref": "#/definitions/Sharing"
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
vars := mux.Vars(r)
rootID := vars["rootID"]
sharing, err := a.app().GetSharing(rootID)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
sharingData, err := json.Marshal(sharing)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
log.Printf("GET sharing %s", rootID)
jsonStringResponse(w, http.StatusOK, string(sharingData))
jsonBytesResponse(w, http.StatusOK, sharingData)
}
func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/sharing/{rootID} postSharing
//
// Sets sharing information for a root block
//
// ---
// produces:
// - application/json
// parameters:
// - name: rootID
// in: path
// description: ID of the root block
// required: true
// type: string
// - name: Body
// in: body
// description: sharing information for a root block
// required: true
// schema:
// "$ref": "#/definitions/Sharing"
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -438,7 +695,7 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
err = json.Unmarshal(requestBody, &sharing)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -453,7 +710,7 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
err = a.app().UpsertSharing(sharing)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -464,25 +721,61 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
// Workspace
func (a *API) handleGetWorkspace(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /api/v1/workspace getWorkspace
//
// Returns information of the root workspace
//
// ---
// produces:
// - application/json
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// schema:
// "$ref": "#/definitions/Workspace"
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
workspace, err := a.app().GetRootWorkspace()
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
workspaceData, err := json.Marshal(workspace)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
jsonStringResponse(w, http.StatusOK, string(workspaceData))
jsonBytesResponse(w, http.StatusOK, workspaceData)
}
func (a *API) handlePostWorkspaceRegenerateSignupToken(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/workspace/regenerate_signup_token regenerateSignupToken
//
// Regenerates the signup token for the root workspace
//
// ---
// produces:
// - application/json
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
workspace, err := a.app().GetRootWorkspace()
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -490,7 +783,7 @@ func (a *API) handlePostWorkspaceRegenerateSignupToken(w http.ResponseWriter, r
err = a.app().UpsertWorkspaceSignupToken(*workspace)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -500,6 +793,31 @@ func (a *API) handlePostWorkspaceRegenerateSignupToken(w http.ResponseWriter, r
// File upload
func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) {
// swagger:operation GET /files/{fileID} getFile
//
// Returns the contents of an uploaded file
//
// ---
// produces:
// - application/json
// - image/jpg
// - image/png
// parameters:
// - name: fileID
// in: path
// description: ID of the file
// required: true
// type: string
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
vars := mux.Vars(r)
filename := vars["filename"]
@ -516,8 +834,40 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, filePath)
}
// FileUploadResponse is the response to a file upload
// swagger:model
type FileUploadResponse struct {
// The URL to retrieve the uploaded file
// required: true
URL string `json:"url"`
}
func (a *API) handleUploadFile(w http.ResponseWriter, r *http.Request) {
fmt.Println(`handleUploadFile`)
// swagger:operation POST /api/v1/files uploadFile
//
// Upload a binary file
//
// ---
// consumes:
// - multipart/form-data
// produces:
// - application/json
// parameters:
// - name: uploaded file
// in: formData
// type: file
// description: The file to upload
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// schema:
// "$ref": "#/definitions/FileUploadResponse"
// default:
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
file, handle, err := r.FormFile("file")
if err != nil {
@ -527,18 +877,20 @@ func (a *API) handleUploadFile(w http.ResponseWriter, r *http.Request) {
}
defer file.Close()
log.Printf(`handleUploadFile, filename: %s`, handle.Filename)
url, err := a.app().SaveFile(file, handle.Filename)
if err != nil {
jsonStringResponse(w, http.StatusInternalServerError, `{}`)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
log.Printf(`saveFile, url: %s`, url)
json := fmt.Sprintf(`{ "url": "%s" }`, url)
jsonStringResponse(w, http.StatusOK, json)
log.Printf("uploadFile, filename: %s, url: %s", handle.Filename, url)
data, err := json.Marshal(FileUploadResponse{URL: url})
if err != nil {
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
jsonBytesResponse(w, http.StatusOK, data)
}
// Response helpers
@ -555,10 +907,10 @@ func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) {
w.Write(json)
}
func errorResponse(w http.ResponseWriter, code int, message map[string]string, sourceError error) {
func errorResponse(w http.ResponseWriter, code int, message string, sourceError error) {
log.Printf("API ERROR %d, err: %v\n", code, sourceError)
w.Header().Set("Content-Type", "application/json")
data, err := json.Marshal(message)
data, err := json.Marshal(model.ErrorResponse{Error: message})
if err != nil {
data = []byte("{}")
}

View File

@ -17,22 +17,60 @@ import (
"github.com/mattermost/focalboard/server/services/auth"
)
type LoginData struct {
// LoginRequest is a login request
// swagger:model
type LoginRequest struct {
// Type of login, currently must be set to "normal"
// required: true
Type string `json:"type"`
// If specified, login using username
// required: false
Username string `json:"username"`
// If specified, login using email
// required: false
Email string `json:"email"`
// Password
// required: true
Password string `json:"password"`
// MFA token
// required: false
// swagger:ignore
MfaToken string `json:"mfa_token"`
}
type RegisterData struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
// LoginResponse is a login response
// swagger:model
type LoginResponse struct {
// Session token
// required: true
Token string `json:"token"`
}
func (rd *RegisterData) IsValid() error {
// RegisterRequest is a user registration request
// swagger:model
type RegisterRequest struct {
// User name
// required: true
Username string `json:"username"`
// User's email
// required: true
Email string `json:"email"`
// Password
// required: true
Password string `json:"password"`
// Registration authorization token
// required: true
Token string `json:"token"`
}
func (rd *RegisterRequest) IsValid() error {
if rd.Username == "" {
return errors.New("Username is required")
}
@ -51,12 +89,20 @@ func (rd *RegisterData) IsValid() error {
return nil
}
type ChangePasswordData struct {
// ChangePasswordRequest is a user password change request
// swagger:model
type ChangePasswordRequest struct {
// Old password
// required: true
OldPassword string `json:"oldPassword"`
// New password
// required: true
NewPassword string `json:"newPassword"`
}
func (rd *ChangePasswordData) IsValid() error {
// IsValid validates a password change request
func (rd *ChangePasswordRequest) IsValid() error {
if rd.OldPassword == "" {
return errors.New("Old password is required")
}
@ -78,34 +124,62 @@ func isValidPassword(password string) error {
}
func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/login login
//
// Login user
//
// ---
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// description: Login request
// required: true
// schema:
// "$ref": "#/definitions/LoginRequest"
// responses:
// '200':
// description: success
// schema:
// "$ref": "#/definitions/LoginResponse"
// '401':
// description: invalid login
// schema:
// "$ref": "#/definitions/ErrorResponse"
// '500':
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
if len(a.singleUserToken) > 0 {
// Not permitted in single-user mode
errorResponse(w, http.StatusUnauthorized, nil, nil)
errorResponse(w, http.StatusUnauthorized, "", nil)
return
}
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
var loginData LoginData
var loginData LoginRequest
err = json.Unmarshal(requestBody, &loginData)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
if loginData.Type == "normal" {
token, err := a.app().Login(loginData.Username, loginData.Email, loginData.Password, loginData.MfaToken)
if err != nil {
errorResponse(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}, err)
errorResponse(w, http.StatusUnauthorized, "incorrect login", err)
return
}
json, err := json.Marshal(map[string]string{"token": token})
json, err := json.Marshal(LoginResponse{Token: token})
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -113,26 +187,50 @@ func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) {
return
}
errorResponse(w, http.StatusInternalServerError, map[string]string{"error": "Unknown login type"}, nil)
errorResponse(w, http.StatusBadRequest, "invalid login type", nil)
}
func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/register register
//
// Register new user
//
// ---
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// description: Register request
// required: true
// schema:
// "$ref": "#/definitions/RegisterRequest"
// responses:
// '200':
// description: success
// '401':
// description: invalid registration token
// '500':
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
if len(a.singleUserToken) > 0 {
// Not permitted in single-user mode
errorResponse(w, http.StatusUnauthorized, nil, nil)
errorResponse(w, http.StatusUnauthorized, "", nil)
return
}
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
var registerData RegisterData
var registerData RegisterRequest
err = json.Unmarshal(requestBody, &registerData)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
@ -140,45 +238,78 @@ func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
if len(registerData.Token) > 0 {
workspace, err := a.app().GetRootWorkspace()
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
if registerData.Token != workspace.SignupToken {
errorResponse(w, http.StatusUnauthorized, nil, nil)
errorResponse(w, http.StatusUnauthorized, "", nil)
return
}
} else {
// No signup token, check if no active users
userCount, err := a.app().GetRegisteredUserCount()
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
if userCount > 0 {
errorResponse(w, http.StatusUnauthorized, nil, nil)
errorResponse(w, http.StatusUnauthorized, "", nil)
return
}
}
if err = registerData.IsValid(); err != nil {
errorResponse(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}, err)
errorResponse(w, http.StatusBadRequest, err.Error(), err)
return
}
err = a.app().RegisterUser(registerData.Username, registerData.Email, registerData.Password)
if err != nil {
errorResponse(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}, err)
errorResponse(w, http.StatusBadRequest, err.Error(), err)
return
}
jsonBytesResponse(w, http.StatusOK, nil)
jsonStringResponse(w, http.StatusOK, "{}")
}
func (a *API) handleChangePassword(w http.ResponseWriter, r *http.Request) {
// swagger:operation POST /api/v1/users/{userID}/changepassword changePassword
//
// Change a user's password
//
// ---
// produces:
// - application/json
// parameters:
// - name: userID
// in: path
// description: User ID
// required: true
// type: string
// - name: body
// in: body
// description: Change password request
// required: true
// schema:
// "$ref": "#/definitions/ChangePasswordRequest"
// security:
// - BearerAuth: []
// responses:
// '200':
// description: success
// '400':
// description: invalid request
// schema:
// "$ref": "#/definitions/ErrorResponse"
// '500':
// description: internal error
// schema:
// "$ref": "#/definitions/ErrorResponse"
if len(a.singleUserToken) > 0 {
// Not permitted in single-user mode
errorResponse(w, http.StatusUnauthorized, nil, nil)
errorResponse(w, http.StatusUnauthorized, "", nil)
return
}
@ -187,27 +318,27 @@ func (a *API) handleChangePassword(w http.ResponseWriter, r *http.Request) {
requestBody, err := ioutil.ReadAll(r.Body)
if err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
var requestData ChangePasswordData
var requestData ChangePasswordRequest
if err := json.Unmarshal(requestBody, &requestData); err != nil {
errorResponse(w, http.StatusInternalServerError, nil, err)
errorResponse(w, http.StatusInternalServerError, "", err)
return
}
if err = requestData.IsValid(); err != nil {
errorResponse(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}, err)
errorResponse(w, http.StatusBadRequest, err.Error(), err)
return
}
if err = a.app().ChangePassword(userID, requestData.OldPassword, requestData.NewPassword); err != nil {
errorResponse(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}, err)
errorResponse(w, http.StatusBadRequest, err.Error(), err)
return
}
jsonBytesResponse(w, http.StatusOK, nil)
jsonStringResponse(w, http.StatusOK, "{}")
}
func (a *API) sessionRequired(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
@ -221,7 +352,7 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
log.Printf(`Single User: %v`, len(a.singleUserToken) > 0)
if len(a.singleUserToken) > 0 {
if required && (token != a.singleUserToken) {
errorResponse(w, http.StatusUnauthorized, nil, nil)
errorResponse(w, http.StatusUnauthorized, "", nil)
return
}
@ -241,7 +372,7 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
session, err := a.app().GetSession(token)
if err != nil {
if required {
errorResponse(w, http.StatusUnauthorized, nil, err)
errorResponse(w, http.StatusUnauthorized, "", err)
return
}
@ -258,7 +389,7 @@ func (a *API) adminRequired(handler func(w http.ResponseWriter, r *http.Request)
// Currently, admin APIs require local unix connections
conn := serverContext.GetContextConn(r)
if _, isUnix := conn.(*net.UnixConn); !isUnix {
errorResponse(w, http.StatusUnauthorized, nil, nil)
errorResponse(w, http.StatusUnauthorized, "", nil)
return
}

View File

@ -1,3 +1,28 @@
// Package classification Focalboard Server
//
// Server for Focalboard
//
// Schemes: http, https
// Host: localhost
// BasePath: /api/v1
// Version: 0.5.0
// License: Custom https://github.com/mattermost/focalboard/blob/main/LICENSE.txt
// Contact: Focalboard<api@focalboard.com> https://www.focalboard.com
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// securityDefinitions:
// BearerAuth:
// type: apiKey
// name: Authorization
// in: header
// description: 'Pass session token using Bearer authentication, e.g. set header "Authorization: Bearer <session token>"'
//
// swagger:meta
package main
import (

View File

@ -5,18 +5,51 @@ import (
"io"
)
// Block is the basic data unit.
// Block is the basic data unit
// swagger:model
type Block struct {
// The id for this block
// required: true
ID string `json:"id"`
// The id for this block's parent block. Empty for root blocks
// required: false
ParentID string `json:"parentId"`
// The id for this block's root block
// required: true
RootID string `json:"rootId"`
// The id for user who last modified this block
// required: true
ModifiedBy string `json:"modifiedBy"`
// The schema version of this block
// required: true
Schema int64 `json:"schema"`
// The block type
// required: true
Type string `json:"type"`
// The display title
// required: false
Title string `json:"title"`
// The block fields
// required: false
Fields map[string]interface{} `json:"fields"`
// The creation time
// required: true
CreateAt int64 `json:"createAt"`
// The last modified time
// required: true
UpdateAt int64 `json:"updateAt"`
// The deleted time. Set to indicate this block is deleted
// required: false
DeleteAt int64 `json:"deleteAt"`
}

View File

@ -0,0 +1,9 @@
package model
// ErrorResponse is an error response
// swagger:model
type ErrorResponse struct {
// The error message
// required: false
Error string `json:"error"`
}

View File

@ -5,11 +5,27 @@ import (
"io"
)
// Sharing is sharing information for a root block
// swagger:model
type Sharing struct {
// ID of the root block
// required: true
ID string `json:"id"`
// Is sharing enabled
// required: true
Enabled bool `json:"enabled"`
// Access token
// required: true
Token string `json:"token"`
// ID of the user who last modified this
// required: true
ModifiedBy string `json:"modifiedBy"`
// Updated time
// required: true
UpdateAt int64 `json:"update_at,omitempty"`
}

View File

@ -1,16 +1,46 @@
package model
// User is a user
// swagger:model
type User struct {
// The user ID
// required: true
ID string `json:"id"`
// The user name
// required: true
Username string `json:"username"`
// The user's email
// required: true
Email string `json:"email"`
// swagger:ignore
Password string `json:"-"`
// swagger:ignore
MfaSecret string `json:"-"`
// swagger:ignore
AuthService string `json:"-"`
// swagger:ignore
AuthData string `json:"-"`
// User settings
// required: true
Props map[string]interface{} `json:"props"`
// Created time
// required: true
CreateAt int64 `json:"create_at,omitempty"`
// Updated time
// required: true
UpdateAt int64 `json:"update_at,omitempty"`
// Deleted time, set to indicate user is deleted
// required: true
DeleteAt int64 `json:"delete_at"`
}

View File

@ -1,9 +1,25 @@
package model
// Workspace is information global to a workspace
// swagger:model
type Workspace struct {
// ID of the workspace
// required: true
ID string `json:"id"`
// Token required to register new users
// required: true
SignupToken string `json:"signupToken"`
// Workspace settings
// required: false
Settings map[string]interface{} `json:"settings"`
// ID of user who last modified this
// required: true
ModifiedBy string `json:"modifiedBy"`
// Updated time
// required: true
UpdateAt int64 `json:"updateAt"`
}

14
server/swagger/README.md Normal file
View File

@ -0,0 +1,14 @@
# Swagger / OpenAPI 2.0 auto-generated files
This folder is generated by the `make swagger` command from comments in the server code.
Prerequisites:
1. [go-swagger](https://goswagger.io/install.html)
2. [openapi-generator](https://github.com/OpenAPITools/openapi-generator)
These can be installed via Homebrew:
```
brew tap go-swagger/go-swagger
brew install go-swagger
brew install openapi-generator
```

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1 @@
5.0.1

File diff suppressed because one or more lines are too long

693
server/swagger/swagger.yml Normal file
View File

@ -0,0 +1,693 @@
basePath: /api/v1
consumes:
- application/json
definitions:
Block:
description: Block is the basic data unit
properties:
createAt:
description: The creation time
format: int64
type: integer
x-go-name: CreateAt
deleteAt:
description: The deleted time. Set to indicate this block is deleted
format: int64
type: integer
x-go-name: DeleteAt
fields:
additionalProperties:
type: object
description: The block fields
type: object
x-go-name: Fields
id:
description: The id for this block
type: string
x-go-name: ID
modifiedBy:
description: The id for user who last modified this block
type: string
x-go-name: ModifiedBy
parentId:
description: The id for this block's parent block. Empty for root blocks
type: string
x-go-name: ParentID
rootId:
description: The id for this block's root block
type: string
x-go-name: RootID
schema:
description: The schema version of this block
format: int64
type: integer
x-go-name: Schema
title:
description: The display title
type: string
x-go-name: Title
type:
description: The block type
type: string
x-go-name: Type
updateAt:
description: The last modified time
format: int64
type: integer
x-go-name: UpdateAt
required:
- id
- rootId
- modifiedBy
- schema
- type
- createAt
- updateAt
type: object
x-go-package: github.com/mattermost/focalboard/server/model
ChangePasswordRequest:
description: ChangePasswordRequest is a user password change request
properties:
newPassword:
description: New password
type: string
x-go-name: NewPassword
oldPassword:
description: Old password
type: string
x-go-name: OldPassword
required:
- oldPassword
- newPassword
type: object
x-go-package: github.com/mattermost/focalboard/server/api
ErrorResponse:
description: ErrorResponse is an error response
properties:
error:
description: The error message
type: string
x-go-name: Error
type: object
x-go-package: github.com/mattermost/focalboard/server/model
FileUploadResponse:
description: FileUploadResponse is the response to a file upload
properties:
url:
description: The URL to retrieve the uploaded file
type: string
x-go-name: URL
required:
- url
type: object
x-go-package: github.com/mattermost/focalboard/server/api
LoginRequest:
description: LoginRequest is a login request
properties:
email:
description: If specified, login using email
type: string
x-go-name: Email
password:
description: Password
type: string
x-go-name: Password
type:
description: Type of login, currently must be set to "normal"
type: string
x-go-name: Type
username:
description: If specified, login using username
type: string
x-go-name: Username
required:
- type
- password
type: object
x-go-package: github.com/mattermost/focalboard/server/api
LoginResponse:
description: LoginResponse is a login response
properties:
token:
description: Session token
type: string
x-go-name: Token
required:
- token
type: object
x-go-package: github.com/mattermost/focalboard/server/api
RegisterRequest:
description: RegisterRequest is a user registration request
properties:
email:
description: User's email
type: string
x-go-name: Email
password:
description: Password
type: string
x-go-name: Password
token:
description: Registration authorization token
type: string
x-go-name: Token
username:
description: User name
type: string
x-go-name: Username
required:
- username
- email
- password
- token
type: object
x-go-package: github.com/mattermost/focalboard/server/api
Sharing:
description: Sharing is sharing information for a root block
properties:
enabled:
description: Is sharing enabled
type: boolean
x-go-name: Enabled
id:
description: ID of the root block
type: string
x-go-name: ID
modifiedBy:
description: ID of the user who last modified this
type: string
x-go-name: ModifiedBy
token:
description: Access token
type: string
x-go-name: Token
update_at:
description: Updated time
format: int64
type: integer
x-go-name: UpdateAt
required:
- id
- enabled
- token
- modifiedBy
- update_at
type: object
x-go-package: github.com/mattermost/focalboard/server/model
User:
description: User is a user
properties:
create_at:
description: Created time
format: int64
type: integer
x-go-name: CreateAt
delete_at:
description: Deleted time, set to indicate user is deleted
format: int64
type: integer
x-go-name: DeleteAt
email:
description: The user's email
type: string
x-go-name: Email
id:
description: The user ID
type: string
x-go-name: ID
props:
additionalProperties:
type: object
description: User settings
type: object
x-go-name: Props
update_at:
description: Updated time
format: int64
type: integer
x-go-name: UpdateAt
username:
description: The user name
type: string
x-go-name: Username
required:
- id
- username
- email
- props
- create_at
- update_at
- delete_at
type: object
x-go-package: github.com/mattermost/focalboard/server/model
Workspace:
description: Workspace is information global to a workspace
properties:
id:
description: ID of the workspace
type: string
x-go-name: ID
modifiedBy:
description: ID of user who last modified this
type: string
x-go-name: ModifiedBy
settings:
additionalProperties:
type: object
description: Workspace settings
type: object
x-go-name: Settings
signupToken:
description: Token required to register new users
type: string
x-go-name: SignupToken
updateAt:
description: Updated time
format: int64
type: integer
x-go-name: UpdateAt
required:
- id
- signupToken
- modifiedBy
- updateAt
type: object
x-go-package: github.com/mattermost/focalboard/server/model
host: localhost
info:
contact:
email: api@focalboard.com
name: Focalboard
url: https://www.focalboard.com
description: Server for Focalboard
license:
name: Custom
url: https://github.com/mattermost/focalboard/blob/main/LICENSE.txt
title: Focalboard Server
version: 0.5.0
paths:
/api/v1/blocks:
get:
description: Returns blocks
operationId: getBlocks
parameters:
- description: ID of parent block, omit to specify all blocks
in: query
name: parent_id
type: string
- description: Type of blocks to return, omit to specify all types
in: query
name: type
type: string
produces:
- application/json
responses:
"200":
description: success
schema:
items:
$ref: '#/definitions/Block'
type: array
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
post:
description: Insert or update blocks
operationId: updateBlocks
parameters:
- description: array of blocks to insert or update
in: body
name: Body
required: true
schema:
items:
$ref: '#/definitions/Block'
type: array
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/blocks/{blockID}:
delete:
description: Deletes a block
operationId: deleteBlock
parameters:
- description: ID of block to delete
in: path
name: blockID
required: true
type: string
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/blocks/{blockID}/subtree:
get:
description: Returns the blocks of a subtree
operationId: getSubTree
parameters:
- description: The ID of the root block of the subtree
in: path
name: blockID
required: true
type: string
- description: The number of levels to return. 2 or 3. Defaults to 2.
in: query
maximum: 3
minimum: 2
name: l
type: integer
produces:
- application/json
responses:
"200":
description: success
schema:
items:
$ref: '#/definitions/Block'
type: array
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/blocks/export:
get:
description: Returns all blocks
operationId: exportBlocks
produces:
- application/json
responses:
"200":
description: success
schema:
items:
$ref: '#/definitions/Block'
type: array
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/blocks/import:
post:
description: Import blocks
operationId: importBlocks
parameters:
- description: array of blocks to import
in: body
name: Body
required: true
schema:
items:
$ref: '#/definitions/Block'
type: array
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/files:
post:
consumes:
- multipart/form-data
description: Upload a binary file
operationId: uploadFile
parameters:
- description: The file to upload
in: formData
name: uploaded file
type: file
produces:
- application/json
responses:
"200":
description: success
schema:
$ref: '#/definitions/FileUploadResponse'
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/login:
post:
description: Login user
operationId: login
parameters:
- description: Login request
in: body
name: body
required: true
schema:
$ref: '#/definitions/LoginRequest'
produces:
- application/json
responses:
"200":
description: success
schema:
$ref: '#/definitions/LoginResponse'
"401":
description: invalid login
schema:
$ref: '#/definitions/ErrorResponse'
"500":
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
/api/v1/register:
post:
description: Register new user
operationId: register
parameters:
- description: Register request
in: body
name: body
required: true
schema:
$ref: '#/definitions/RegisterRequest'
produces:
- application/json
responses:
"200":
description: success
"401":
description: invalid registration token
"500":
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
/api/v1/sharing/{rootID}:
get:
description: Returns sharing information for a root block
operationId: getSharing
parameters:
- description: ID of the root block
in: path
name: rootID
required: true
type: string
produces:
- application/json
responses:
"200":
description: success
schema:
$ref: '#/definitions/Sharing'
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
post:
description: Sets sharing information for a root block
operationId: postSharing
parameters:
- description: ID of the root block
in: path
name: rootID
required: true
type: string
- description: sharing information for a root block
in: body
name: Body
required: true
schema:
$ref: '#/definitions/Sharing'
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/users/{userID}:
get:
description: Returns a user
operationId: getUser
parameters:
- description: User ID
in: path
name: userID
required: true
type: string
produces:
- application/json
responses:
"200":
description: success
schema:
$ref: '#/definitions/User'
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/users/{userID}/changepassword:
post:
description: Change a user's password
operationId: changePassword
parameters:
- description: User ID
in: path
name: userID
required: true
type: string
- description: Change password request
in: body
name: body
required: true
schema:
$ref: '#/definitions/ChangePasswordRequest'
produces:
- application/json
responses:
"200":
description: success
"400":
description: invalid request
schema:
$ref: '#/definitions/ErrorResponse'
"500":
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/users/me:
get:
description: Returns the currently logged-in user
operationId: getMe
produces:
- application/json
responses:
"200":
description: success
schema:
$ref: '#/definitions/User'
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspace:
get:
description: Returns information of the root workspace
operationId: getWorkspace
produces:
- application/json
responses:
"200":
description: success
schema:
$ref: '#/definitions/Workspace'
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/api/v1/workspace/regenerate_signup_token:
post:
description: Regenerates the signup token for the root workspace
operationId: regenerateSignupToken
produces:
- application/json
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
/files/{fileID}:
get:
description: Returns the contents of an uploaded file
operationId: getFile
parameters:
- description: ID of the file
in: path
name: fileID
required: true
type: string
produces:
- application/json
- image/jpg
- image/png
responses:
"200":
description: success
default:
description: internal error
schema:
$ref: '#/definitions/ErrorResponse'
security:
- BearerAuth: []
produces:
- application/json
schemes:
- http
- https
securityDefinitions:
BearerAuth:
description: 'Pass session token using Bearer authentication, e.g. set header "Authorization: Bearer <session token>"'
in: header
name: Authorization
type: apiKey
swagger: "2.0"