all
This commit is contained in:
44
Downloads/Voltaserve/api/.air.toml
Normal file
44
Downloads/Voltaserve/api/.air.toml
Normal file
@@ -0,0 +1,44 @@
|
||||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = ".air"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./.air/main"
|
||||
cmd = "go build -o ./.air/main ."
|
||||
delay = 0
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
6
Downloads/Voltaserve/api/.dockerignore
Normal file
6
Downloads/Voltaserve/api/.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
||||
README.md
|
||||
DEVELOPMENT.md
|
||||
Dockerfile
|
||||
.gitignore
|
||||
.gitattributes
|
||||
.vscode
|
39
Downloads/Voltaserve/api/.env
Normal file
39
Downloads/Voltaserve/api/.env
Normal file
@@ -0,0 +1,39 @@
|
||||
PORT=5000
|
||||
|
||||
# URLs
|
||||
PUBLIC_UI_URL="http://localhost:3000"
|
||||
CONVERSION_URL="http://127.0.0.1:5001"
|
||||
POSTGRES_URL="postgresql://voltaserve:voltaserve@127.0.0.1:5432/voltaserve"
|
||||
|
||||
# Security
|
||||
SECURITY_JWT_SIGNING_KEY="586cozl1x9m6zmu4fg8iwi6ajazguehcm9qdfgd5ndo2pc3pcn"
|
||||
SECURITY_CORS_ORIGINS="http://localhost:3000"
|
||||
SECURITY_API_KEY="7znl9Zd8F!4lRZA43lEQb757mCy"
|
||||
|
||||
# S3
|
||||
S3_URL="127.0.0.1:9000"
|
||||
S3_ACCESS_KEY="voltaserve"
|
||||
S3_SECRET_KEY="voltaserve"
|
||||
S3_REGION="us-east-1"
|
||||
S3_SECURE=false
|
||||
|
||||
# Search
|
||||
SEARCH_URL="http://127.0.0.1:7700"
|
||||
|
||||
# Redis
|
||||
REDIS_ADDRESS="127.0.0.1:6379"
|
||||
REDIS_DB=0
|
||||
|
||||
# SMTP
|
||||
SMTP_HOST="localhost"
|
||||
SMTP_PORT=1025
|
||||
SMTP_SECURE=false
|
||||
SMTP_SENDER_ADDRESS="no-reply@localhost"
|
||||
SMTP_SENDER_NAME="Voltaserve"
|
||||
|
||||
# Limits
|
||||
LIMITS_EXTERNAL_COMMAND_TIMEOUT_SECONDS=900
|
||||
LIMITS_MULTIPART_BODY_LENGTH_LIMIT_MB=1000
|
||||
|
||||
# Defaults
|
||||
DEFAULT_WORKSPACE_STORAGE_CAPACITY_BYTES=100000000000
|
1
Downloads/Voltaserve/api/.gitattributes
vendored
Normal file
1
Downloads/Voltaserve/api/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
docs/** linguist-generated
|
4
Downloads/Voltaserve/api/.gitignore
vendored
Normal file
4
Downloads/Voltaserve/api/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/.env.local
|
||||
/voltaserve
|
||||
/__debug_bin*
|
||||
/.air
|
5
Downloads/Voltaserve/api/.vscode/extensions.json
vendored
Normal file
5
Downloads/Voltaserve/api/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"golang.go"
|
||||
]
|
||||
}
|
15
Downloads/Voltaserve/api/.vscode/launch.json
vendored
Normal file
15
Downloads/Voltaserve/api/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/main.go"
|
||||
}
|
||||
]
|
||||
}
|
6
Downloads/Voltaserve/api/.vscode/settings.json
vendored
Normal file
6
Downloads/Voltaserve/api/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"[go]": {
|
||||
"editor.defaultFormatter": "golang.go",
|
||||
"editor.tabSize": 4
|
||||
}
|
||||
}
|
20
Downloads/Voltaserve/api/Dockerfile
Normal file
20
Downloads/Voltaserve/api/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
||||
FROM registry.suse.com/bci/golang:1.21 AS builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN go mod download
|
||||
RUN go build -o voltaserve-api
|
||||
|
||||
FROM registry.suse.com/bci/bci-micro:15.5 AS runner
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /build/voltaserve-api ./voltaserve-api
|
||||
COPY --from=builder /build/.env ./.env
|
||||
COPY --from=builder /build/templates ./templates
|
||||
|
||||
ENTRYPOINT ["./voltaserve-api"]
|
||||
|
||||
EXPOSE 5000
|
55
Downloads/Voltaserve/api/README.md
Normal file
55
Downloads/Voltaserve/api/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Voltaserve API
|
||||
|
||||
## Getting Started
|
||||
|
||||
Install [swag](https://github.com/swaggo/swag) and [golangci-lint](https://golangci-lint.run/usage/install).
|
||||
|
||||
Run for development:
|
||||
|
||||
```shell
|
||||
go run .
|
||||
```
|
||||
|
||||
Build binary:
|
||||
|
||||
```shell
|
||||
go build .
|
||||
```
|
||||
|
||||
Build Docker image:
|
||||
|
||||
```shell
|
||||
docker build -t voltaserve/api .
|
||||
```
|
||||
|
||||
Run code linter:
|
||||
|
||||
```shell
|
||||
golangci-lint run
|
||||
```
|
||||
|
||||
## Generate and Publish Documentation
|
||||
|
||||
Format swag comments:
|
||||
|
||||
```shell
|
||||
swag fmt
|
||||
```
|
||||
|
||||
Generate `swagger.yml`:
|
||||
|
||||
```shell
|
||||
swag init --output ./docs --outputTypes yaml
|
||||
```
|
||||
|
||||
Preview (will be served at [http://127.0.0.1:5555](http://127.0.0.1:5555)):
|
||||
|
||||
```shell
|
||||
npx @redocly/cli preview-docs --port 5555 ./docs/swagger.yaml
|
||||
```
|
||||
|
||||
Generate the final static HTML documentation:
|
||||
|
||||
```shell
|
||||
npx @redocly/cli build-docs ./docs/swagger.yaml --output ./docs/index.html
|
||||
```
|
64
Downloads/Voltaserve/api/cache/file_cache.go
vendored
Normal file
64
Downloads/Voltaserve/api/cache/file_cache.go
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type FileCache struct {
|
||||
redis *infra.RedisManager
|
||||
fileRepo repo.FileRepo
|
||||
keyPrefix string
|
||||
}
|
||||
|
||||
func NewFileCache() *FileCache {
|
||||
return &FileCache{
|
||||
fileRepo: repo.NewFileRepo(),
|
||||
redis: infra.NewRedisManager(),
|
||||
keyPrefix: "file:",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *FileCache) Set(file model.File) error {
|
||||
b, err := json.Marshal(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.redis.Set(c.keyPrefix+file.GetID(), string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FileCache) Get(id string) (model.File, error) {
|
||||
value, err := c.redis.Get(c.keyPrefix + id)
|
||||
if err != nil {
|
||||
return c.Refresh(id)
|
||||
}
|
||||
file := repo.NewFile()
|
||||
if err = json.Unmarshal([]byte(value), &file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (c *FileCache) Refresh(id string) (model.File, error) {
|
||||
res, err := c.fileRepo.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = c.Set(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *FileCache) Delete(id string) error {
|
||||
if err := c.redis.Delete(c.keyPrefix + id); err != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
57
Downloads/Voltaserve/api/cache/group_cache.go
vendored
Normal file
57
Downloads/Voltaserve/api/cache/group_cache.go
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type GroupCache struct {
|
||||
redis *infra.RedisManager
|
||||
groupRepo repo.GroupRepo
|
||||
keyPrefix string
|
||||
}
|
||||
|
||||
func NewGroupCache() *GroupCache {
|
||||
return &GroupCache{
|
||||
redis: infra.NewRedisManager(),
|
||||
groupRepo: repo.NewGroupRepo(),
|
||||
keyPrefix: "group:",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GroupCache) Set(workspace model.Group) error {
|
||||
b, err := json.Marshal(workspace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.redis.Set(c.keyPrefix+workspace.GetID(), string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *GroupCache) Get(id string) (model.Group, error) {
|
||||
value, err := c.redis.Get(c.keyPrefix + id)
|
||||
if err != nil {
|
||||
return c.Refresh(id)
|
||||
}
|
||||
group := repo.NewGroup()
|
||||
if err = json.Unmarshal([]byte(value), &group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func (c *GroupCache) Refresh(id string) (model.Group, error) {
|
||||
res, err := c.groupRepo.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = c.Set(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
64
Downloads/Voltaserve/api/cache/organization_cache.go
vendored
Normal file
64
Downloads/Voltaserve/api/cache/organization_cache.go
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type OrganizationCache struct {
|
||||
redis *infra.RedisManager
|
||||
orgRepo repo.OrganizationRepo
|
||||
keyPrefix string
|
||||
}
|
||||
|
||||
func NewOrganizationCache() *OrganizationCache {
|
||||
return &OrganizationCache{
|
||||
redis: infra.NewRedisManager(),
|
||||
orgRepo: repo.NewOrganizationRepo(),
|
||||
keyPrefix: "organization:",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *OrganizationCache) Set(organization model.Organization) error {
|
||||
b, err := json.Marshal(organization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.redis.Set(c.keyPrefix+organization.GetID(), string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *OrganizationCache) Get(id string) (model.Organization, error) {
|
||||
value, err := c.redis.Get(c.keyPrefix + id)
|
||||
if err != nil {
|
||||
return c.Refresh(id)
|
||||
}
|
||||
var org = repo.NewOrganization()
|
||||
if err = json.Unmarshal([]byte(value), &org); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return org, nil
|
||||
}
|
||||
|
||||
func (c *OrganizationCache) Refresh(id string) (model.Organization, error) {
|
||||
res, err := c.orgRepo.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = c.Set(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *OrganizationCache) Delete(id string) error {
|
||||
if err := c.redis.Delete(c.keyPrefix + id); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
64
Downloads/Voltaserve/api/cache/workspace_cache.go
vendored
Normal file
64
Downloads/Voltaserve/api/cache/workspace_cache.go
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type WorkspaceCache struct {
|
||||
redis *infra.RedisManager
|
||||
workspaceRepo repo.WorkspaceRepo
|
||||
keyPrefix string
|
||||
}
|
||||
|
||||
func NewWorkspaceCache() *WorkspaceCache {
|
||||
return &WorkspaceCache{
|
||||
redis: infra.NewRedisManager(),
|
||||
workspaceRepo: repo.NewWorkspaceRepo(),
|
||||
keyPrefix: "workspace:",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *WorkspaceCache) Set(workspace model.Workspace) error {
|
||||
b, err := json.Marshal(workspace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.redis.Set(c.keyPrefix+workspace.GetID(), string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *WorkspaceCache) Get(id string) (model.Workspace, error) {
|
||||
value, err := c.redis.Get(c.keyPrefix + id)
|
||||
if err != nil {
|
||||
return c.Refresh(id)
|
||||
}
|
||||
workspace := repo.NewWorkspace()
|
||||
if err = json.Unmarshal([]byte(value), &workspace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return workspace, nil
|
||||
}
|
||||
|
||||
func (c *WorkspaceCache) Refresh(id string) (model.Workspace, error) {
|
||||
res, err := c.workspaceRepo.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = c.Set(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *WorkspaceCache) Delete(id string) error {
|
||||
if err := c.redis.Delete(c.keyPrefix + id); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
47
Downloads/Voltaserve/api/client/conversion_client.go
Normal file
47
Downloads/Voltaserve/api/client/conversion_client.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"voltaserve/config"
|
||||
)
|
||||
|
||||
type ConversionClient struct {
|
||||
config config.Config
|
||||
}
|
||||
|
||||
type PipelineRunOptions struct {
|
||||
FileID string `json:"fileId"`
|
||||
SnapshotID string `json:"snapshotId"`
|
||||
Bucket string `json:"bucket"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
func NewConversionClient() *ConversionClient {
|
||||
return &ConversionClient{
|
||||
config: config.GetConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConversionClient) RunPipeline(opts *PipelineRunOptions) error {
|
||||
body, err := json.Marshal(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/pipelines/run?api_key=%s", c.config.ConversionURL, c.config.Security.APIKey), bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
|
||||
client := &http.Client{}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := res.Body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
121
Downloads/Voltaserve/api/config/config.go
Normal file
121
Downloads/Voltaserve/api/config/config.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var config *Config
|
||||
|
||||
func GetConfig() Config {
|
||||
if config == nil {
|
||||
port, err := strconv.Atoi(os.Getenv("PORT"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config = &Config{
|
||||
Port: port,
|
||||
}
|
||||
readURLs(config)
|
||||
readSecurity(config)
|
||||
readS3(config)
|
||||
readSearch(config)
|
||||
readRedis(config)
|
||||
readSMTP(config)
|
||||
readLimits(config)
|
||||
readDefaults(config)
|
||||
}
|
||||
return *config
|
||||
}
|
||||
|
||||
func readURLs(config *Config) {
|
||||
config.PublicUIURL = os.Getenv("PUBLIC_UI_URL")
|
||||
config.ConversionURL = os.Getenv("CONVERSION_URL")
|
||||
config.DatabaseURL = os.Getenv("POSTGRES_URL")
|
||||
}
|
||||
|
||||
func readSecurity(config *Config) {
|
||||
config.Security.JWTSigningKey = os.Getenv("SECURITY_JWT_SIGNING_KEY")
|
||||
config.Security.CORSOrigins = strings.Split(os.Getenv("SECURITY_CORS_ORIGINS"), ",")
|
||||
config.Security.APIKey = os.Getenv("SECURITY_API_KEY")
|
||||
}
|
||||
|
||||
func readS3(config *Config) {
|
||||
config.S3.URL = os.Getenv("S3_URL")
|
||||
config.S3.AccessKey = os.Getenv("S3_ACCESS_KEY")
|
||||
config.S3.SecretKey = os.Getenv("S3_SECRET_KEY")
|
||||
config.S3.Region = os.Getenv("S3_REGION")
|
||||
if len(os.Getenv("S3_SECURE")) > 0 {
|
||||
v, err := strconv.ParseBool(os.Getenv("S3_SECURE"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.S3.Secure = v
|
||||
}
|
||||
}
|
||||
|
||||
func readSearch(config *Config) {
|
||||
config.Search.URL = os.Getenv("SEARCH_URL")
|
||||
}
|
||||
|
||||
func readRedis(config *Config) {
|
||||
config.Redis.Address = os.Getenv("REDIS_ADDRESS")
|
||||
config.Redis.Password = os.Getenv("REDIS_PASSWORD")
|
||||
if len(os.Getenv("REDIS_DB")) > 0 {
|
||||
v, err := strconv.ParseInt(os.Getenv("REDIS_DB"), 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.Redis.DB = int(v)
|
||||
}
|
||||
}
|
||||
|
||||
func readSMTP(config *Config) {
|
||||
config.SMTP.Host = os.Getenv("SMTP_HOST")
|
||||
if len(os.Getenv("SMTP_PORT")) > 0 {
|
||||
v, err := strconv.ParseInt(os.Getenv("SMTP_PORT"), 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.SMTP.Port = int(v)
|
||||
}
|
||||
if len(os.Getenv("SMTP_SECURE")) > 0 {
|
||||
v, err := strconv.ParseBool(os.Getenv("SMTP_SECURE"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.SMTP.Secure = v
|
||||
}
|
||||
config.SMTP.Username = os.Getenv("SMTP_USERNAME")
|
||||
config.SMTP.Password = os.Getenv("SMTP_PASSWORD")
|
||||
config.SMTP.SenderAddress = os.Getenv("SMTP_SENDER_ADDRESS")
|
||||
config.SMTP.SenderName = os.Getenv("SMTP_SENDER_NAME")
|
||||
}
|
||||
|
||||
func readLimits(config *Config) {
|
||||
if len(os.Getenv("LIMITS_EXTERNAL_COMMAND_TIMEOUT_SECONDS")) > 0 {
|
||||
v, err := strconv.ParseInt(os.Getenv("LIMITS_EXTERNAL_COMMAND_TIMEOUT_SECONDS"), 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.Limits.ExternalCommandTimeoutSeconds = int(v)
|
||||
}
|
||||
if len(os.Getenv("LIMITS_MULTIPART_BODY_LENGTH_LIMIT_MB")) > 0 {
|
||||
v, err := strconv.ParseInt(os.Getenv("LIMITS_MULTIPART_BODY_LENGTH_LIMIT_MB"), 10, 32)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.Limits.MultipartBodyLengthLimitMB = int(v)
|
||||
}
|
||||
}
|
||||
|
||||
func readDefaults(config *Config) {
|
||||
if len(os.Getenv("DEFAULT_WORKSPACE_STORAGE_CAPACITY_BYTES")) > 0 {
|
||||
v, err := strconv.ParseInt(os.Getenv("DEFAULT_WORKSPACE_STORAGE_CAPACITY_BYTES"), 10, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
config.Defaults.WorkspaceStorageCapacityBytes = v
|
||||
}
|
||||
}
|
65
Downloads/Voltaserve/api/config/types.go
Normal file
65
Downloads/Voltaserve/api/config/types.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
Port int
|
||||
PublicUIURL string
|
||||
ConversionURL string
|
||||
DatabaseURL string
|
||||
Search SearchConfig
|
||||
Redis RedisConfig
|
||||
S3 S3Config
|
||||
Limits LimitsConfig
|
||||
Security SecurityConfig
|
||||
SMTP SMTPConfig
|
||||
Defaults DefaultsConfig
|
||||
}
|
||||
|
||||
type LimitsConfig struct {
|
||||
ExternalCommandTimeoutSeconds int
|
||||
MultipartBodyLengthLimitMB int
|
||||
}
|
||||
|
||||
type DefaultsConfig struct {
|
||||
WorkspaceStorageCapacityBytes int64
|
||||
}
|
||||
|
||||
type TokenConfig struct {
|
||||
AccessTokenLifetime int
|
||||
RefreshTokenLifetime int
|
||||
TokenAudience string
|
||||
TokenIssuer string
|
||||
}
|
||||
|
||||
type SearchConfig struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
type RedisConfig struct {
|
||||
Address string
|
||||
Password string
|
||||
DB int
|
||||
}
|
||||
|
||||
type S3Config struct {
|
||||
URL string
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
Region string
|
||||
Secure bool
|
||||
}
|
||||
|
||||
type SecurityConfig struct {
|
||||
JWTSigningKey string
|
||||
CORSOrigins []string
|
||||
APIKey string
|
||||
}
|
||||
|
||||
type SMTPConfig struct {
|
||||
Host string
|
||||
Port int
|
||||
Secure bool
|
||||
Username string
|
||||
Password string
|
||||
SenderAddress string
|
||||
SenderName string
|
||||
}
|
763
Downloads/Voltaserve/api/docs/index.html
generated
Normal file
763
Downloads/Voltaserve/api/docs/index.html
generated
Normal file
File diff suppressed because one or more lines are too long
2679
Downloads/Voltaserve/api/docs/swagger.yaml
generated
Normal file
2679
Downloads/Voltaserve/api/docs/swagger.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
7
Downloads/Voltaserve/api/errorpkg/error_constants.go
Normal file
7
Downloads/Voltaserve/api/errorpkg/error_constants.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package errorpkg
|
||||
|
||||
const (
|
||||
MsgResourceNotFound = "The resource you are looking for might have been removed or temporatily unavailable."
|
||||
MsgSomethingWentWrong = "Oops! something went wrong."
|
||||
MsgInvalidRequest = "An invalid request was sent to the server."
|
||||
)
|
415
Downloads/Voltaserve/api/errorpkg/error_creators.go
Normal file
415
Downloads/Voltaserve/api/errorpkg/error_creators.go
Normal file
@@ -0,0 +1,415 @@
|
||||
package errorpkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"voltaserve/model"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
func NewGroupNotFoundError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"group_not_found",
|
||||
http.StatusNotFound,
|
||||
"Group not found.",
|
||||
MsgResourceNotFound,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewFileNotFoundError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"file_not_found",
|
||||
http.StatusNotFound,
|
||||
"File not found.",
|
||||
MsgResourceNotFound,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewInvalidPathError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"invalid_path",
|
||||
http.StatusBadRequest,
|
||||
"Invalid path.",
|
||||
MsgInvalidRequest,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewWorkspaceNotFoundError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"workspace_not_found",
|
||||
http.StatusNotFound,
|
||||
"Workspace not found.",
|
||||
MsgResourceNotFound,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewOrganizationNotFoundError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"organization_not_found",
|
||||
http.StatusNotFound,
|
||||
"Organization not found.",
|
||||
MsgResourceNotFound,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewSnapshotNotFoundError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"snapshot_not_found",
|
||||
http.StatusNotFound,
|
||||
"Snapshot not found.",
|
||||
"The file has no snapshots.",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewS3ObjectNotFoundError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"s3_object_not_found",
|
||||
http.StatusNotFound,
|
||||
"S3 object not found.",
|
||||
"The snapshot does not contain the S3 object requested.",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewInvitationNotFoundError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"invitation_not_found",
|
||||
http.StatusNotFound,
|
||||
"Invitation not found.",
|
||||
MsgResourceNotFound,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewUserNotFoundError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"user_not_found",
|
||||
http.StatusNotFound,
|
||||
"User not found.",
|
||||
MsgResourceNotFound,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewInternalServerError(err error) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"internal_server_error",
|
||||
http.StatusInternalServerError,
|
||||
"Internal server error.",
|
||||
MsgSomethingWentWrong,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewOrganizationPermissionError(user model.User, org model.Organization, permission string) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"missing_organization_permission",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf(
|
||||
"User '%s' (%s) is missing the permission '%s' for organization '%s' (%s).",
|
||||
user.GetUsername(), user.GetID(), permission, org.GetName(), org.GetID(),
|
||||
),
|
||||
fmt.Sprintf("Sorry, you don't have enough permissions for organization '%s'.", org.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewCannotRemoveLastRemainingOwnerOfOrganizationError(id string) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"cannot_remove_last_owner_of_organization",
|
||||
http.StatusBadRequest,
|
||||
fmt.Sprintf("Cannot remove the last remaining owner of organization '%s'.", id), MsgInvalidRequest,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewGroupPermissionError(user model.User, org model.Organization, permission string) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"missing_group_permission",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf(
|
||||
"User '%s' (%s) is missing the permission '%s' for the group '%s' (%s).",
|
||||
user.GetUsername(), user.GetID(), permission, org.GetName(), org.GetID(),
|
||||
),
|
||||
fmt.Sprintf("Sorry, you don't have enough permissions for the group '%s'.", org.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewWorkspacePermissionError(user model.User, workspace model.Workspace, permission string) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"missing_workspace_permission",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf(
|
||||
"User '%s' (%s) is missing the permission '%s' for the workspace '%s' (%s).",
|
||||
user.GetUsername(), user.GetID(), permission, workspace.GetName(), workspace.GetID(),
|
||||
),
|
||||
fmt.Sprintf("Sorry, you don't have enough permissions for the workspace '%s'.", workspace.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewFilePermissionError(user model.User, file model.File, permission string) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"missing_file_permission",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf(
|
||||
"User '%s' (%s) is missing the permission '%s' for the file '%s' (%s).",
|
||||
user.GetUsername(), user.GetID(), permission, file.GetName(), file.GetID(),
|
||||
),
|
||||
fmt.Sprintf("Sorry, you don't have enough permissions for the item '%s'.", file.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewS3Error(message string) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"s3_error",
|
||||
http.StatusInternalServerError,
|
||||
message,
|
||||
MsgSomethingWentWrong,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewMissingQueryParamError(param string) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"missing_query_param",
|
||||
http.StatusBadRequest,
|
||||
fmt.Sprintf("Query param '%s' is required.", param),
|
||||
MsgInvalidRequest,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewInvalidQueryParamError(param string) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"invalid_query_param",
|
||||
http.StatusBadRequest,
|
||||
fmt.Sprintf("Query param '%s' is invalid.", param),
|
||||
MsgInvalidRequest,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewStorageLimitExceededError() *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"storage_limit_exceeded",
|
||||
http.StatusForbidden,
|
||||
"Storage limit exceeded.",
|
||||
"The storage limit of your workspace has been reached, please increase it and try again.",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewInsufficientStorageCapacityError() *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"insufficient_storage_capacity",
|
||||
http.StatusForbidden,
|
||||
"Insufficient storage capacity.",
|
||||
"The requested storage capacity is insufficient.",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewRequestBodyValidationError(err error) *ErrorResponse {
|
||||
var fields []string
|
||||
for _, e := range err.(validator.ValidationErrors) {
|
||||
fields = append(fields, e.Field())
|
||||
}
|
||||
return NewErrorResponse(
|
||||
"request_validation_error",
|
||||
http.StatusBadRequest,
|
||||
fmt.Sprintf("Failed validation for the following fields: %s.", strings.Join(fields, ",")),
|
||||
MsgInvalidRequest,
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
func NewFileAlreadyChildOfDestinationError(source model.File, target model.File) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"file_already_child_of_destination",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("File '%s' (%s) is already a child of '%s' (%s).", source.GetName(), source.GetID(), target.GetName(), target.GetID()),
|
||||
fmt.Sprintf("Item '%s' is already within '%s'.", source.GetName(), target.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewFileCannotBeMovedIntoItselfError(source model.File) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"file_cannot_be_moved_into_itself",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("File '%s' (%s) cannot be moved into itself.", source.GetName(), source.GetID()),
|
||||
fmt.Sprintf("Item '%s' cannot be moved into itself.", source.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewFileIsNotAFolderError(file model.File) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"file_is_not_a_folder",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("File '%s' (%s) is not a folder.", file.GetName(), file.GetID()),
|
||||
fmt.Sprintf("Item '%s' is not a folder.", file.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewTargetIsGrandChildOfSourceError(file model.File) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"target_is_grant_child_of_source",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("File '%s' (%s) cannot be moved in another file within its own tree.", file.GetName(), file.GetID()),
|
||||
fmt.Sprintf("Item '%s' cannot be moved in another item within its own tree.", file.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewCannotDeleteWorkspaceRootError(file model.File, workspace model.Workspace) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"cannot_delete_workspace_root",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("Cannot delete the root file (%s) of the workspace '%s' (%s).", file.GetID(), workspace.GetName(), workspace.GetID()),
|
||||
fmt.Sprintf("Cannot delete the root item of the workspace '%s'.", workspace.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewFileCannotBeCopiedIntoOwnSubtreeError(file model.File) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"file_cannot_be_coped_into_own_subtree",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("File '%s' (%s) cannot be copied in another file within its own subtree.", file.GetName(), file.GetID()),
|
||||
fmt.Sprintf("Item '%s' cannot be copied in another item within its own subtree.", file.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewFileCannotBeCopiedIntoIselfError(file model.File) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"file_cannot_be_copied_into_itself",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("File '%s' (%s) cannot be copied into itself.", file.GetName(), file.GetID()),
|
||||
fmt.Sprintf("Item '%s' cannot be copied into itself.", file.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewFileWithSimilarNameExistsError() *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"file_with_similar_name_exists",
|
||||
http.StatusForbidden,
|
||||
"File with similar name exists.",
|
||||
"Item with similar name exists.",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewInvalidPageParameterError() *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"invalid_page_parameter",
|
||||
http.StatusBadRequest,
|
||||
"Invalid page parameter, must be >= 1.",
|
||||
MsgInvalidRequest,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewInvalidSizeParameterError() *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"invalid_size_parameter",
|
||||
http.StatusBadRequest,
|
||||
"Invalid size parameter, must be >= 1.",
|
||||
MsgInvalidRequest,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewCannotAcceptNonPendingInvitationError(invitation model.Invitation) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"cannot_accept_non_pending_invitation",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("Cannot accept an invitation which is not pending, the status of the invitation (%s) is (%s).", invitation.GetID(), invitation.GetStatus()),
|
||||
"Cannot accept an invitation which is not pending.",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewCannotDeclineNonPendingInvitationError(invitation model.Invitation) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"cannot_decline_non_pending_invitation",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("Cannot decline an invitation which is not pending, the status of the invitation (%s) is (%s).", invitation.GetID(), invitation.GetStatus()),
|
||||
"Cannot decline an invitation which is not pending.",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewCannotResendNonPendingInvitationError(invitation model.Invitation) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"cannot_resend_non_pending_invitation",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("Cannot resend an invitation which is not pending, the status of the invitation (%s) is (%s).", invitation.GetID(), invitation.GetStatus()),
|
||||
"Cannot resend an invitation which is not pending.",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewUserNotAllowedToAcceptInvitationError(user model.User, invitation model.Invitation) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"user_not_allowed_to_accept_invitation",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("User '%s' (%s) is not allowed to accept the invitation (%s).", user.GetUsername(), user.GetID(), invitation.GetID()),
|
||||
"Not allowed to accept this invitation.",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewUserNotAllowedToDeclineInvitationError(user model.User, invitation model.Invitation) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"user_not_allowed_to_decline_invitation",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("User '%s' (%s) is not allowed to decline the invitation (%s).", user.GetUsername(), user.GetID(), invitation.GetID()),
|
||||
"Not allowed to decline this invitation.",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewUserNotAllowedToDeleteInvitationError(user model.User, invitation model.Invitation) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"user_not_allowed_to_delete_invitation",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("User '%s' (%s) not allowed to delete the invitation (%s).", user.GetUsername(), user.GetID(), invitation.GetID()),
|
||||
"Not allowed to delete this invitation.",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewUserAlreadyMemberOfOrganizationError(user model.User, org model.Organization) *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"user_already_member_of_organization",
|
||||
http.StatusForbidden,
|
||||
fmt.Sprintf("User '%s' (%s) is already a member of the organization '%s' (%s).", user.GetUsername(), user.GetID(), org.GetName(), org.GetID()),
|
||||
fmt.Sprintf("You are already a member of the organization '%s'.", org.GetName()),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func NewInvalidAPIKeyError() *ErrorResponse {
|
||||
return NewErrorResponse(
|
||||
"invalid_api_key",
|
||||
http.StatusUnauthorized,
|
||||
"Invalid API key.",
|
||||
"The API key is either missing or invalid.",
|
||||
nil,
|
||||
)
|
||||
}
|
20
Downloads/Voltaserve/api/errorpkg/error_handler.go
Normal file
20
Downloads/Voltaserve/api/errorpkg/error_handler.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package errorpkg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
)
|
||||
|
||||
func ErrorHandler(c *fiber.Ctx, err error) error {
|
||||
var e *ErrorResponse
|
||||
if errors.As(err, &e) {
|
||||
v := err.(*ErrorResponse)
|
||||
return c.Status(v.Status).JSON(v)
|
||||
} else {
|
||||
log.Error(err)
|
||||
return c.Status(http.StatusInternalServerError).JSON(NewInternalServerError(err))
|
||||
}
|
||||
}
|
31
Downloads/Voltaserve/api/errorpkg/error_response.go
Normal file
31
Downloads/Voltaserve/api/errorpkg/error_response.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package errorpkg
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ErrorResponse struct {
|
||||
Code string `json:"code"`
|
||||
Status int `json:"status"`
|
||||
Message string `json:"message"`
|
||||
UserMessage string `json:"userMessage"`
|
||||
MoreInfo string `json:"moreInfo"`
|
||||
Err error `json:"-"`
|
||||
}
|
||||
|
||||
func NewErrorResponse(code string, status int, message string, userMessage string, err error) *ErrorResponse {
|
||||
return &ErrorResponse{
|
||||
Code: code,
|
||||
Status: status,
|
||||
Message: message,
|
||||
UserMessage: userMessage,
|
||||
MoreInfo: fmt.Sprintf("https://voltaserve.com/docs/api/errors/%s", code),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func (err ErrorResponse) Error() string {
|
||||
return fmt.Sprintf("%s %s", err.Code, err.Message)
|
||||
}
|
||||
|
||||
func (err ErrorResponse) Unwrap() error {
|
||||
return err.Err
|
||||
}
|
82
Downloads/Voltaserve/api/go.mod
Normal file
82
Downloads/Voltaserve/api/go.mod
Normal file
@@ -0,0 +1,82 @@
|
||||
module voltaserve
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.22.2
|
||||
|
||||
require (
|
||||
github.com/gabriel-vasile/mimetype v1.4.3
|
||||
github.com/go-playground/validator/v10 v10.19.0
|
||||
github.com/gofiber/contrib/jwt v1.0.8
|
||||
github.com/gofiber/fiber/v2 v2.52.4
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gosimple/slug v1.14.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/meilisearch/meilisearch-go v0.26.2
|
||||
github.com/minio/minio-go/v7 v7.0.69
|
||||
github.com/reactivex/rxgo/v2 v2.5.0
|
||||
github.com/redis/go-redis/v9 v9.5.1
|
||||
github.com/speps/go-hashids/v2 v2.0.1
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gorm.io/datatypes v1.2.0
|
||||
gorm.io/driver/postgres v1.5.7
|
||||
gorm.io/gorm v1.25.9
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/gosimple/unidecode v1.0.1 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/teivah/onecontext v1.3.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.52.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.14.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.5.6 // indirect
|
||||
)
|
228
Downloads/Voltaserve/api/go.sum
Normal file
228
Downloads/Voltaserve/api/go.sum
Normal file
@@ -0,0 +1,228 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k=
|
||||
github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
|
||||
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/gofiber/contrib/jwt v1.0.8 h1:/GeOsm/Mr1OGr0GTy+RIVSz5VgNNyP3ZgK4wdqxF/WY=
|
||||
github.com/gofiber/contrib/jwt v1.0.8/go.mod h1:gWWBtBiLmKXRN7xy6a96QO0KGvPEyxdh8x496Ujtg84=
|
||||
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
||||
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gosimple/slug v1.14.0 h1:RtTL/71mJNDfpUbCOmnf/XFkzKRtD6wL6Uy+3akm4Es=
|
||||
github.com/gosimple/slug v1.14.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
|
||||
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
|
||||
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/meilisearch/meilisearch-go v0.26.2 h1:3gTlmiV1dHHumVUhYdJbvh3camiNiyqQ1hNveVsU2OE=
|
||||
github.com/meilisearch/meilisearch-go v0.26.2/go.mod h1:SxuSqDcPBIykjWz1PX+KzsYzArNLSCadQodWs8extS0=
|
||||
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
||||
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0=
|
||||
github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ=
|
||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/reactivex/rxgo/v2 v2.5.0 h1:FhPgHwX9vKdNQB2gq9EPt+EKk9QrrzoeztGbEEnZam4=
|
||||
github.com/reactivex/rxgo/v2 v2.5.0/go.mod h1:bs4fVZxcb5ZckLIOeIeVH942yunJLWDABWGbrHAW+qU=
|
||||
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/speps/go-hashids/v2 v2.0.1 h1:ViWOEqWES/pdOSq+C1SLVa8/Tnsd52XC34RY7lt7m4g=
|
||||
github.com/speps/go-hashids/v2 v2.0.1/go.mod h1:47LKunwvDZki/uRVD6NImtyk712yFzIs3UF3KlHohGw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/teivah/onecontext v0.0.0-20200513185103-40f981bfd775/go.mod h1:XUZ4x3oGhWfiOnUvTslnKKs39AWUct3g3yJvXTQSJOQ=
|
||||
github.com/teivah/onecontext v1.3.0 h1:tbikMhAlo6VhAuEGCvhc8HlTnpX4xTNPTOseWuhO1J0=
|
||||
github.com/teivah/onecontext v1.3.0/go.mod h1:hoW1nmdPVK/0jrvGtcx8sCKYs2PiS4z0zzfdeuEVyb0=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
|
||||
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
|
||||
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
|
||||
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco=
|
||||
gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04=
|
||||
gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
|
||||
gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM=
|
||||
gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
|
||||
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
|
||||
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
|
||||
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
|
||||
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
52
Downloads/Voltaserve/api/guard/file_guard.go
Normal file
52
Downloads/Voltaserve/api/guard/file_guard.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package guard
|
||||
|
||||
import (
|
||||
"voltaserve/cache"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/model"
|
||||
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
)
|
||||
|
||||
type FileGuard struct {
|
||||
groupCache *cache.GroupCache
|
||||
}
|
||||
|
||||
func NewFileGuard() *FileGuard {
|
||||
return &FileGuard{
|
||||
groupCache: cache.NewGroupCache(),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *FileGuard) IsAuthorized(user model.User, file model.File, permission string) bool {
|
||||
for _, p := range file.GetUserPermissions() {
|
||||
if p.GetUserID() == user.GetID() && model.IsEquivalentPermission(p.GetValue(), permission) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, p := range file.GetGroupPermissions() {
|
||||
g, err := g.groupCache.Get(p.GetGroupID())
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return false
|
||||
}
|
||||
for _, u := range g.GetUsers() {
|
||||
if u == user.GetID() && model.IsEquivalentPermission(p.GetValue(), permission) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *FileGuard) Authorize(user model.User, file model.File, permission string) error {
|
||||
if !g.IsAuthorized(user, file, permission) {
|
||||
err := errorpkg.NewFilePermissionError(user, file, permission)
|
||||
if g.IsAuthorized(user, file, model.PermissionViewer) {
|
||||
return err
|
||||
} else {
|
||||
return errorpkg.NewOrganizationNotFoundError(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
52
Downloads/Voltaserve/api/guard/group_guard.go
Normal file
52
Downloads/Voltaserve/api/guard/group_guard.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package guard
|
||||
|
||||
import (
|
||||
"voltaserve/cache"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/model"
|
||||
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
)
|
||||
|
||||
type GroupGuard struct {
|
||||
groupCache *cache.GroupCache
|
||||
}
|
||||
|
||||
func NewGroupGuard() *GroupGuard {
|
||||
return &GroupGuard{
|
||||
groupCache: cache.NewGroupCache(),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupGuard) IsAuthorized(user model.User, group model.Group, permission string) bool {
|
||||
for _, p := range group.GetUserPermissions() {
|
||||
if p.GetUserID() == user.GetID() && model.IsEquivalentPermission(p.GetValue(), permission) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, p := range group.GetGroupPermissions() {
|
||||
g, err := g.groupCache.Get(p.GetGroupID())
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return false
|
||||
}
|
||||
for _, u := range g.GetUsers() {
|
||||
if u == user.GetID() && model.IsEquivalentPermission(p.GetValue(), permission) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *GroupGuard) Authorize(user model.User, group model.Group, permission string) error {
|
||||
if !g.IsAuthorized(user, group, permission) {
|
||||
err := errorpkg.NewGroupPermissionError(user, group, permission)
|
||||
if g.IsAuthorized(user, group, model.PermissionViewer) {
|
||||
return err
|
||||
} else {
|
||||
return errorpkg.NewGroupNotFoundError(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
52
Downloads/Voltaserve/api/guard/organization_guard.go
Normal file
52
Downloads/Voltaserve/api/guard/organization_guard.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package guard
|
||||
|
||||
import (
|
||||
"voltaserve/cache"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/model"
|
||||
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
)
|
||||
|
||||
type OrganizationGuard struct {
|
||||
groupCache *cache.GroupCache
|
||||
}
|
||||
|
||||
func NewOrganizationGuard() *OrganizationGuard {
|
||||
return &OrganizationGuard{
|
||||
groupCache: cache.NewGroupCache(),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *OrganizationGuard) IsAuthorized(user model.User, org model.Organization, permission string) bool {
|
||||
for _, p := range org.GetUserPermissions() {
|
||||
if p.GetUserID() == user.GetID() && model.IsEquivalentPermission(p.GetValue(), permission) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, p := range org.GetGroupPermissions() {
|
||||
g, err := g.groupCache.Get(p.GetGroupID())
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return false
|
||||
}
|
||||
for _, u := range g.GetUsers() {
|
||||
if u == user.GetID() && model.IsEquivalentPermission(p.GetValue(), permission) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *OrganizationGuard) Authorize(user model.User, org model.Organization, permission string) error {
|
||||
if !g.IsAuthorized(user, org, permission) {
|
||||
err := errorpkg.NewOrganizationPermissionError(user, org, permission)
|
||||
if g.IsAuthorized(user, org, model.PermissionViewer) {
|
||||
return err
|
||||
} else {
|
||||
return errorpkg.NewOrganizationNotFoundError(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
52
Downloads/Voltaserve/api/guard/workspace_guard.go
Normal file
52
Downloads/Voltaserve/api/guard/workspace_guard.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package guard
|
||||
|
||||
import (
|
||||
"voltaserve/cache"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/model"
|
||||
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
)
|
||||
|
||||
type WorkspaceGuard struct {
|
||||
groupCache *cache.GroupCache
|
||||
}
|
||||
|
||||
func NewWorkspaceGuard() *WorkspaceGuard {
|
||||
return &WorkspaceGuard{
|
||||
groupCache: cache.NewGroupCache(),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *WorkspaceGuard) IsAuthorized(user model.User, workspace model.Workspace, permission string) bool {
|
||||
for _, p := range workspace.GetUserPermissions() {
|
||||
if p.GetUserID() == user.GetID() && model.IsEquivalentPermission(p.GetValue(), permission) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, p := range workspace.GetGroupPermissions() {
|
||||
g, err := g.groupCache.Get(p.GetGroupID())
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return false
|
||||
}
|
||||
for _, u := range g.GetUsers() {
|
||||
if u == user.GetID() && model.IsEquivalentPermission(p.GetValue(), permission) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *WorkspaceGuard) Authorize(user model.User, workspace model.Workspace, permission string) error {
|
||||
if !g.IsAuthorized(user, workspace, permission) {
|
||||
err := errorpkg.NewWorkspacePermissionError(user, workspace, permission)
|
||||
if g.IsAuthorized(user, workspace, model.PermissionViewer) {
|
||||
return err
|
||||
} else {
|
||||
return errorpkg.NewWorkspaceNotFoundError(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
22
Downloads/Voltaserve/api/helper/id.go
Normal file
22
Downloads/Voltaserve/api/helper/id.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/speps/go-hashids/v2"
|
||||
)
|
||||
|
||||
func NewID() string {
|
||||
hd := hashids.NewData()
|
||||
hd.Salt = uuid.NewString()
|
||||
h, err := hashids.NewWithData(hd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
id, err := h.EncodeInt64([]int64{time.Now().UTC().UnixNano()})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
5
Downloads/Voltaserve/api/helper/unit.go
Normal file
5
Downloads/Voltaserve/api/helper/unit.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package helper
|
||||
|
||||
func MegabyteToByte(mb int) int64 {
|
||||
return int64(mb) * 1000000
|
||||
}
|
17
Downloads/Voltaserve/api/helper/workspace.go
Normal file
17
Downloads/Voltaserve/api/helper/workspace.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gosimple/slug"
|
||||
)
|
||||
|
||||
func SlugFromWorkspace(id string, name string) string {
|
||||
return fmt.Sprintf("%s-%s", slug.Make(name), id)
|
||||
}
|
||||
|
||||
func WorkspaceIDFromSlug(slug string) string {
|
||||
parts := strings.Split(slug, "-")
|
||||
return parts[len(parts)-1]
|
||||
}
|
22
Downloads/Voltaserve/api/infra/db.go
Normal file
22
Downloads/Voltaserve/api/infra/db.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"voltaserve/config"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var db *gorm.DB
|
||||
|
||||
func GetDb() *gorm.DB {
|
||||
if db != nil {
|
||||
return db
|
||||
}
|
||||
var err error
|
||||
db, err = gorm.Open(postgres.Open(config.GetConfig().DatabaseURL), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return db
|
||||
}
|
123
Downloads/Voltaserve/api/infra/file_identifier.go
Normal file
123
Downloads/Voltaserve/api/infra/file_identifier.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package infra
|
||||
|
||||
import "strings"
|
||||
|
||||
type FileIdentifier struct {
|
||||
}
|
||||
|
||||
func NewFileIdentifier() *FileIdentifier {
|
||||
return &FileIdentifier{}
|
||||
}
|
||||
|
||||
func (fi *FileIdentifier) IsPDF(extension string) bool {
|
||||
return strings.ToLower(extension) == ".pdf"
|
||||
}
|
||||
|
||||
func (fi *FileIdentifier) IsOffice(extension string) bool {
|
||||
extensions := []string{
|
||||
".xls",
|
||||
".doc",
|
||||
".ppt",
|
||||
".xlsx",
|
||||
".docx",
|
||||
".pptx",
|
||||
".odt",
|
||||
".ott",
|
||||
".ods",
|
||||
".ots",
|
||||
".odp",
|
||||
".otp",
|
||||
".odg",
|
||||
".otg",
|
||||
".odf",
|
||||
".odc",
|
||||
".rtf",
|
||||
}
|
||||
for _, v := range extensions {
|
||||
if strings.ToLower(extension) == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (fi *FileIdentifier) IsPlainText(extension string) bool {
|
||||
extensions := []string{
|
||||
".txt",
|
||||
".html",
|
||||
".js",
|
||||
"jsx",
|
||||
".ts",
|
||||
".tsx",
|
||||
".css",
|
||||
".sass",
|
||||
".scss",
|
||||
".go",
|
||||
".py",
|
||||
".rb",
|
||||
".java",
|
||||
".c",
|
||||
".h",
|
||||
".cpp",
|
||||
".hpp",
|
||||
".json",
|
||||
".yml",
|
||||
".yaml",
|
||||
".toml",
|
||||
".md",
|
||||
}
|
||||
for _, v := range extensions {
|
||||
if strings.ToLower(extension) == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (fi *FileIdentifier) IsImage(extension string) bool {
|
||||
extensions := []string{
|
||||
".xpm",
|
||||
".png",
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".jp2",
|
||||
".gif",
|
||||
".webp",
|
||||
".tiff",
|
||||
".bmp",
|
||||
".ico",
|
||||
".heif",
|
||||
".xcf",
|
||||
".svg",
|
||||
}
|
||||
for _, v := range extensions {
|
||||
if strings.ToLower(extension) == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (fi *FileIdentifier) IsVideo(extension string) bool {
|
||||
extensions := []string{
|
||||
".ogv",
|
||||
".mpeg",
|
||||
".mov",
|
||||
".mqv",
|
||||
".mp4",
|
||||
".webm",
|
||||
".3gp",
|
||||
".3g2",
|
||||
".avi",
|
||||
".flv",
|
||||
".mkv",
|
||||
".asf",
|
||||
".m4v",
|
||||
}
|
||||
for _, v := range extensions {
|
||||
if strings.ToLower(extension) == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
99
Downloads/Voltaserve/api/infra/mail.go
Normal file
99
Downloads/Voltaserve/api/infra/mail.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"voltaserve/config"
|
||||
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"gopkg.in/gomail.v2"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type MessageParams struct {
|
||||
Subject string
|
||||
}
|
||||
|
||||
type MailTemplate struct {
|
||||
dialer *gomail.Dialer
|
||||
config config.SMTPConfig
|
||||
}
|
||||
|
||||
func NewMailTemplate() *MailTemplate {
|
||||
mt := new(MailTemplate)
|
||||
mt.config = config.GetConfig().SMTP
|
||||
mt.dialer = gomail.NewDialer(mt.config.Host, mt.config.Port, mt.config.Username, mt.config.Password)
|
||||
return mt
|
||||
}
|
||||
|
||||
func (mt *MailTemplate) Send(templateName string, address string, variables map[string]string) error {
|
||||
html, err := mt.GetText(filepath.FromSlash("templates/"+templateName+"/template.html"), variables)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
text, err := mt.GetText(filepath.FromSlash("templates/"+templateName+"/template.txt"), variables)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params, err := mt.GetMessageParams(templateName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", fmt.Sprintf(`"%s" <%s>`, mt.config.SenderName, mt.config.SenderAddress))
|
||||
m.SetHeader("To", address)
|
||||
m.SetHeader("Subject", params.Subject)
|
||||
m.SetBody("text/plain ", text)
|
||||
m.AddAlternative("text/html", html)
|
||||
if err := mt.dialer.DialAndSend(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mt *MailTemplate) GetText(path string, variables map[string]string) (string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func(f *os.File) {
|
||||
if err := f.Close(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}(f)
|
||||
b, _ := io.ReadAll(f)
|
||||
html := string(b)
|
||||
tmpl, err := template.New("").Parse(html)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
err = tmpl.Execute(&buf, variables)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (mt *MailTemplate) GetMessageParams(templateName string) (*MessageParams, error) {
|
||||
f, err := os.Open(filepath.FromSlash("templates/" + templateName + "/params.yml"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func(f *os.File) {
|
||||
if err := f.Close(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}(f)
|
||||
b, _ := io.ReadAll(f)
|
||||
res := &MessageParams{}
|
||||
if err := yaml.Unmarshal(b, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
15
Downloads/Voltaserve/api/infra/mime.go
Normal file
15
Downloads/Voltaserve/api/infra/mime.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package infra
|
||||
|
||||
import "github.com/gabriel-vasile/mimetype"
|
||||
|
||||
func DetectMimeFromFile(path string) string {
|
||||
mime, err := mimetype.DetectFile(path)
|
||||
if err != nil {
|
||||
return "application/octet-stream"
|
||||
}
|
||||
return mime.String()
|
||||
}
|
||||
|
||||
func DetectMimeFromBytes(b []byte) string {
|
||||
return mimetype.Detect(b).String()
|
||||
}
|
113
Downloads/Voltaserve/api/infra/redis.go
Normal file
113
Downloads/Voltaserve/api/infra/redis.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"voltaserve/config"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type RedisManager struct {
|
||||
config config.RedisConfig
|
||||
client *redis.Client
|
||||
clusterClient *redis.ClusterClient
|
||||
}
|
||||
|
||||
func NewRedisManager() *RedisManager {
|
||||
mgr := new(RedisManager)
|
||||
mgr.config = config.GetConfig().Redis
|
||||
return mgr
|
||||
}
|
||||
|
||||
func (mgr *RedisManager) Set(key string, value interface{}) error {
|
||||
if mgr.client == nil && mgr.clusterClient == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if mgr.clusterClient != nil {
|
||||
if _, err := mgr.clusterClient.Set(context.Background(), key, value, 0).Result(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := mgr.client.Set(context.Background(), key, value, 0).Result(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *RedisManager) Get(key string) (string, error) {
|
||||
if mgr.client == nil && mgr.clusterClient == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if mgr.clusterClient != nil {
|
||||
value, err := mgr.clusterClient.Get(context.Background(), key).Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return value, nil
|
||||
} else {
|
||||
value, err := mgr.client.Get(context.Background(), key).Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (mgr *RedisManager) Delete(key string) error {
|
||||
if mgr.client == nil && mgr.clusterClient == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if mgr.clusterClient != nil {
|
||||
if _, err := mgr.clusterClient.Del(context.Background(), key).Result(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if _, err := mgr.client.Del(context.Background(), key).Result(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *RedisManager) Close() error {
|
||||
if mgr.client != nil {
|
||||
if err := mgr.client.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *RedisManager) connect() error {
|
||||
if mgr.client != nil || mgr.clusterClient != nil {
|
||||
return nil
|
||||
}
|
||||
addresses := strings.Split(mgr.config.Address, ";")
|
||||
if len(addresses) > 1 {
|
||||
mgr.clusterClient = redis.NewClusterClient(&redis.ClusterOptions{
|
||||
Addrs: addresses,
|
||||
Password: mgr.config.Password,
|
||||
})
|
||||
if err := mgr.clusterClient.Ping(context.Background()).Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
mgr.client = redis.NewClient(&redis.Options{
|
||||
Addr: mgr.config.Address,
|
||||
Password: mgr.config.Password,
|
||||
DB: mgr.config.DB,
|
||||
})
|
||||
if err := mgr.client.Ping(context.Background()).Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
205
Downloads/Voltaserve/api/infra/s3.go
Normal file
205
Downloads/Voltaserve/api/infra/s3.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
"voltaserve/config"
|
||||
"voltaserve/errorpkg"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/minio/minio-go/v7/pkg/sse"
|
||||
)
|
||||
|
||||
type S3Manager struct {
|
||||
config config.S3Config
|
||||
client *minio.Client
|
||||
}
|
||||
|
||||
func NewS3Manager() *S3Manager {
|
||||
mgr := new(S3Manager)
|
||||
mgr.config = config.GetConfig().S3
|
||||
return mgr
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) GetFile(objectName string, filePath string, bucketName string) error {
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := mgr.client.FGetObject(context.Background(), bucketName, objectName, filePath, minio.GetObjectOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) PutFile(objectName string, filePath string, contentType string, bucketName string) error {
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if contentType == "" {
|
||||
contentType = "application/octet-stream"
|
||||
}
|
||||
if _, err := mgr.client.FPutObject(context.Background(), bucketName, objectName, filePath, minio.PutObjectOptions{
|
||||
ContentType: contentType,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) PutText(objectName string, text string, contentType string, bucketName string) error {
|
||||
if contentType != "" && contentType != "text/plain" && contentType != "application/json" {
|
||||
return errorpkg.NewS3Error("Invalid content type '" + contentType + "'")
|
||||
}
|
||||
if contentType == "" {
|
||||
contentType = "text/plain"
|
||||
}
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := mgr.client.PutObject(context.Background(), bucketName, objectName, strings.NewReader(text), int64(len(text)), minio.PutObjectOptions{
|
||||
ContentType: contentType,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) GetObject(objectName string, bucketName string) (*bytes.Buffer, error) {
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
reader, err := mgr.client.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(io.Writer(&buf), reader)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return &buf, nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) GetText(objectName string, bucketName string) (string, error) {
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
reader, err := mgr.client.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf := new(strings.Builder)
|
||||
_, err = io.Copy(buf, reader)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) RemoveObject(objectName string, bucketName string) error {
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := mgr.client.RemoveObject(context.Background(), bucketName, objectName, minio.RemoveObjectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) CreateBucket(bucketName string) error {
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
found, err := mgr.client.BucketExists(context.Background(), bucketName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
if err = mgr.client.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{
|
||||
Region: mgr.config.Region,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) RemoveBucket(bucketName string) error {
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
found, err := mgr.client.BucketExists(context.Background(), bucketName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
objectCh := mgr.client.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{
|
||||
Prefix: "",
|
||||
Recursive: true,
|
||||
})
|
||||
mgr.client.RemoveObjects(context.Background(), bucketName, objectCh, minio.RemoveObjectsOptions{})
|
||||
if err = mgr.client.RemoveBucket(context.Background(), bucketName); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) EnableBucketEncryption(bucketName string) error {
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := mgr.client.SetBucketEncryption(context.Background(), bucketName, sse.NewConfigurationSSES3())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) DisableBucketEncryption(bucketName string) error {
|
||||
if mgr.client == nil {
|
||||
if err := mgr.connect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := mgr.client.RemoveBucketEncryption(context.Background(), bucketName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *S3Manager) connect() error {
|
||||
client, err := minio.New(mgr.config.URL, &minio.Options{
|
||||
Creds: credentials.NewStaticV4(mgr.config.AccessKey, mgr.config.SecretKey, ""),
|
||||
Secure: mgr.config.Secure,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mgr.client = client
|
||||
return nil
|
||||
}
|
123
Downloads/Voltaserve/api/infra/search.go
Normal file
123
Downloads/Voltaserve/api/infra/search.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"voltaserve/config"
|
||||
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
)
|
||||
|
||||
var searchClient *meilisearch.Client
|
||||
|
||||
const (
|
||||
FileSearchIndex = "file"
|
||||
GroupSearchIndex = "group"
|
||||
WorkspaceSearchIndex = "workspace"
|
||||
OrganizationSearchIndex = "organization"
|
||||
UserSearchIndex = "user"
|
||||
)
|
||||
|
||||
type SearchModel interface {
|
||||
GetID() string
|
||||
}
|
||||
|
||||
type SearchManager struct {
|
||||
config config.SearchConfig
|
||||
}
|
||||
|
||||
func NewSearchManager() *SearchManager {
|
||||
if searchClient == nil {
|
||||
searchClient = meilisearch.NewClient(meilisearch.ClientConfig{
|
||||
Host: config.GetConfig().Search.URL,
|
||||
})
|
||||
if _, err := searchClient.CreateIndex(&meilisearch.IndexConfig{
|
||||
Uid: FileSearchIndex,
|
||||
PrimaryKey: "id",
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := searchClient.Index(FileSearchIndex).UpdateSettings(&meilisearch.Settings{
|
||||
SearchableAttributes: []string{"name", "text"},
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := searchClient.CreateIndex(&meilisearch.IndexConfig{
|
||||
Uid: GroupSearchIndex,
|
||||
PrimaryKey: "id",
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := searchClient.Index(GroupSearchIndex).UpdateSettings(&meilisearch.Settings{
|
||||
SearchableAttributes: []string{"name"},
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := searchClient.CreateIndex(&meilisearch.IndexConfig{
|
||||
Uid: WorkspaceSearchIndex,
|
||||
PrimaryKey: "id",
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := searchClient.Index(WorkspaceSearchIndex).UpdateSettings(&meilisearch.Settings{
|
||||
SearchableAttributes: []string{"name"},
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := searchClient.CreateIndex(&meilisearch.IndexConfig{
|
||||
Uid: OrganizationSearchIndex,
|
||||
PrimaryKey: "id",
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := searchClient.Index(OrganizationSearchIndex).UpdateSettings(&meilisearch.Settings{
|
||||
SearchableAttributes: []string{"name"},
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := searchClient.CreateIndex(&meilisearch.IndexConfig{
|
||||
Uid: UserSearchIndex,
|
||||
PrimaryKey: "id",
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := searchClient.Index(UserSearchIndex).UpdateSettings(&meilisearch.Settings{
|
||||
SearchableAttributes: []string{"fullName", "email"},
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return &SearchManager{
|
||||
config: config.GetConfig().Search,
|
||||
}
|
||||
}
|
||||
|
||||
func (mgr *SearchManager) Query(index string, query string) ([]interface{}, error) {
|
||||
res, err := searchClient.Index(index).Search(query, &meilisearch.SearchRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.Hits, nil
|
||||
}
|
||||
|
||||
func (mgr *SearchManager) Index(index string, models []SearchModel) error {
|
||||
_, err := searchClient.Index(index).AddDocuments(models)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *SearchManager) Update(index string, m []SearchModel) error {
|
||||
_, err := searchClient.Index(index).UpdateDocuments(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgr *SearchManager) Delete(index string, ids []string) error {
|
||||
_, err := searchClient.Index(index).DeleteDocuments(ids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
92
Downloads/Voltaserve/api/main.go
Normal file
92
Downloads/Voltaserve/api/main.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
|
||||
"voltaserve/config"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/router"
|
||||
|
||||
jwtware "github.com/gofiber/contrib/jwt"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
// @title Voltaserve API
|
||||
// @version 1.0.0
|
||||
// @BasePath /v1
|
||||
func main() {
|
||||
if _, err := os.Stat(".env.local"); err == nil {
|
||||
err := godotenv.Load(".env.local")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
cfg := config.GetConfig()
|
||||
|
||||
app := fiber.New(fiber.Config{
|
||||
ErrorHandler: errorpkg.ErrorHandler,
|
||||
BodyLimit: int(helper.MegabyteToByte(cfg.Limits.MultipartBodyLengthLimitMB)),
|
||||
})
|
||||
|
||||
v1 := app.Group("/v1")
|
||||
|
||||
f := v1.Group("files")
|
||||
|
||||
app.Get("v1/health", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(200)
|
||||
})
|
||||
|
||||
app.Use(cors.New(cors.Config{
|
||||
AllowOrigins: strings.Join(cfg.Security.CORSOrigins, ","),
|
||||
}))
|
||||
|
||||
fileDownloads := router.NewFileDownloadRouter()
|
||||
fileDownloads.AppendNonJWTRoutes(f)
|
||||
|
||||
conversionWebhook := router.NewConversionWebhookRouter()
|
||||
conversionWebhook.AppendInternalRoutes(f)
|
||||
|
||||
app.Use(jwtware.New(jwtware.Config{
|
||||
SigningKey: jwtware.SigningKey{Key: []byte(cfg.Security.JWTSigningKey)},
|
||||
}))
|
||||
|
||||
files := router.NewFileRouter()
|
||||
files.AppendRoutes(f)
|
||||
|
||||
invitations := router.NewInvitationRouter()
|
||||
invitations.AppendRoutes(v1.Group("invitations"))
|
||||
|
||||
notifications := router.NewNotificationRouter()
|
||||
notifications.AppendRoutes(v1.Group("notifications"))
|
||||
|
||||
organizations := router.NewOrganizationRouter()
|
||||
organizations.AppendRoutes(v1.Group("organizations"))
|
||||
|
||||
storage := router.NewStorageRouter()
|
||||
storage.AppendRoutes(v1.Group("storage"))
|
||||
|
||||
workspaces := router.NewWorkspaceRouter()
|
||||
workspaces.AppendRoutes(v1.Group("workspaces"))
|
||||
|
||||
groups := router.NewGroupRouter()
|
||||
groups.AppendRoutes(v1.Group("groups"))
|
||||
|
||||
users := router.NewUserRouter()
|
||||
users.AppendRoutes(v1.Group("users"))
|
||||
|
||||
if err := app.Listen(fmt.Sprintf(":%d", cfg.Port)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
28
Downloads/Voltaserve/api/model/file_model.go
Normal file
28
Downloads/Voltaserve/api/model/file_model.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package model
|
||||
|
||||
const (
|
||||
FileTypeFile = "file"
|
||||
FileTypeFolder = "folder"
|
||||
)
|
||||
|
||||
type File interface {
|
||||
GetID() string
|
||||
GetWorkspaceID() string
|
||||
GetName() string
|
||||
GetType() string
|
||||
GetParentID() *string
|
||||
GetCreateTime() string
|
||||
GetUpdateTime() *string
|
||||
GetSnapshots() []Snapshot
|
||||
GetUserPermissions() []CoreUserPermission
|
||||
GetGroupPermissions() []CoreGroupPermission
|
||||
GetText() *string
|
||||
SetID(string)
|
||||
SetParentID(*string)
|
||||
SetWorkspaceID(string)
|
||||
SetType(string)
|
||||
SetName(string)
|
||||
SetText(*string)
|
||||
SetCreateTime(string)
|
||||
SetUpdateTime(*string)
|
||||
}
|
14
Downloads/Voltaserve/api/model/group_model.go
Normal file
14
Downloads/Voltaserve/api/model/group_model.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package model
|
||||
|
||||
type Group interface {
|
||||
GetID() string
|
||||
GetName() string
|
||||
GetOrganizationID() string
|
||||
GetUserPermissions() []CoreUserPermission
|
||||
GetGroupPermissions() []CoreGroupPermission
|
||||
GetUsers() []string
|
||||
GetCreateTime() string
|
||||
GetUpdateTime() *string
|
||||
SetName(string)
|
||||
SetUpdateTime(*string)
|
||||
}
|
19
Downloads/Voltaserve/api/model/invitation_model.go
Normal file
19
Downloads/Voltaserve/api/model/invitation_model.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package model
|
||||
|
||||
const (
|
||||
InvitationStatusPending = "pending"
|
||||
InvitationStatusAccepted = "accepted"
|
||||
InvitationStatusDeclined = "declined"
|
||||
)
|
||||
|
||||
type Invitation interface {
|
||||
GetID() string
|
||||
GetOrganizationID() string
|
||||
GetOwnerID() string
|
||||
GetEmail() string
|
||||
GetStatus() string
|
||||
GetCreateTime() string
|
||||
GetUpdateTime() *string
|
||||
SetStatus(string)
|
||||
SetUpdateTime(*string)
|
||||
}
|
13
Downloads/Voltaserve/api/model/organization_model.go
Normal file
13
Downloads/Voltaserve/api/model/organization_model.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package model
|
||||
|
||||
type Organization interface {
|
||||
GetID() string
|
||||
GetName() string
|
||||
GetUserPermissions() []CoreUserPermission
|
||||
GetGroupPermissions() []CoreGroupPermission
|
||||
GetUsers() []string
|
||||
GetCreateTime() string
|
||||
GetUpdateTime() *string
|
||||
SetName(string)
|
||||
SetUpdateTime(*string)
|
||||
}
|
58
Downloads/Voltaserve/api/model/permission_model.go
Normal file
58
Downloads/Voltaserve/api/model/permission_model.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package model
|
||||
|
||||
const (
|
||||
PermissionViewer = "viewer"
|
||||
PermissionEditor = "editor"
|
||||
PermissionOwner = "owner"
|
||||
)
|
||||
|
||||
type CoreUserPermission interface {
|
||||
GetUserID() string
|
||||
GetValue() string
|
||||
}
|
||||
|
||||
type CoreGroupPermission interface {
|
||||
GetGroupID() string
|
||||
GetValue() string
|
||||
}
|
||||
|
||||
func GteViewerPermission(permission string) bool {
|
||||
return permission == PermissionViewer || permission == PermissionEditor || permission == PermissionOwner
|
||||
}
|
||||
|
||||
func GteEditorPermission(permission string) bool {
|
||||
return permission == PermissionEditor || permission == PermissionOwner
|
||||
}
|
||||
|
||||
func GteOwnerPermission(permission string) bool {
|
||||
return permission == PermissionOwner
|
||||
}
|
||||
|
||||
func IsEquivalentPermission(permission string, otherPermission string) bool {
|
||||
if permission == otherPermission {
|
||||
return true
|
||||
}
|
||||
if otherPermission == PermissionViewer && GteViewerPermission(permission) {
|
||||
return true
|
||||
}
|
||||
if otherPermission == PermissionEditor && GteEditorPermission(permission) {
|
||||
return true
|
||||
}
|
||||
if otherPermission == PermissionOwner && GteOwnerPermission(permission) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetPermissionWeight(permission string) int {
|
||||
if permission == PermissionViewer {
|
||||
return 1
|
||||
}
|
||||
if permission == PermissionEditor {
|
||||
return 2
|
||||
}
|
||||
if permission == PermissionOwner {
|
||||
return 3
|
||||
}
|
||||
return 0
|
||||
}
|
49
Downloads/Voltaserve/api/model/snapshot_model.go
Normal file
49
Downloads/Voltaserve/api/model/snapshot_model.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package model
|
||||
|
||||
const (
|
||||
SnapshotStatusNew = "new"
|
||||
SnapshotStatusProcessing = "processing"
|
||||
SnapshotStatusReady = "ready"
|
||||
SnapshotStatusError = "error"
|
||||
)
|
||||
|
||||
type Snapshot interface {
|
||||
GetID() string
|
||||
GetVersion() int64
|
||||
GetOriginal() *S3Object
|
||||
GetPreview() *S3Object
|
||||
GetText() *S3Object
|
||||
GetThumbnail() *Thumbnail
|
||||
HasOriginal() bool
|
||||
HasPreview() bool
|
||||
HasText() bool
|
||||
HasThumbnail() bool
|
||||
GetStatus() string
|
||||
GetCreateTime() string
|
||||
GetUpdateTime() *string
|
||||
SetID(string)
|
||||
SetVersion(int64)
|
||||
SetOriginal(*S3Object)
|
||||
SetPreview(*S3Object)
|
||||
SetText(*S3Object)
|
||||
SetThumbnail(*Thumbnail)
|
||||
SetStatus(string)
|
||||
}
|
||||
|
||||
type S3Object struct {
|
||||
Bucket string `json:"bucket"`
|
||||
Key string `json:"key"`
|
||||
Size int64 `json:"size"`
|
||||
Image *ImageProps `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
type ImageProps struct {
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
}
|
||||
|
||||
type Thumbnail struct {
|
||||
Base64 string `json:"base64"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
}
|
12
Downloads/Voltaserve/api/model/user_model.go
Normal file
12
Downloads/Voltaserve/api/model/user_model.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package model
|
||||
|
||||
type User interface {
|
||||
GetID() string
|
||||
GetFullName() string
|
||||
GetUsername() string
|
||||
GetEmail() string
|
||||
GetPicture() *string
|
||||
GetIsEmailConfirmed() bool
|
||||
GetCreateTime() string
|
||||
GetUpdateTime() *string
|
||||
}
|
16
Downloads/Voltaserve/api/model/workspace_model.go
Normal file
16
Downloads/Voltaserve/api/model/workspace_model.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package model
|
||||
|
||||
type Workspace interface {
|
||||
GetID() string
|
||||
GetName() string
|
||||
GetStorageCapacity() int64
|
||||
GetRootID() string
|
||||
GetOrganizationID() string
|
||||
GetUserPermissions() []CoreUserPermission
|
||||
GetGroupPermissions() []CoreGroupPermission
|
||||
GetBucket() string
|
||||
GetCreateTime() string
|
||||
GetUpdateTime() *string
|
||||
SetName(string)
|
||||
SetUpdateTime(*string)
|
||||
}
|
572
Downloads/Voltaserve/api/repo/file_repo.go
Normal file
572
Downloads/Voltaserve/api/repo/file_repo.go
Normal file
@@ -0,0 +1,572 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type FileInsertOptions struct {
|
||||
Name string
|
||||
WorkspaceID string
|
||||
ParentID *string
|
||||
Type string
|
||||
}
|
||||
|
||||
type FileRepo interface {
|
||||
Insert(opts FileInsertOptions) (model.File, error)
|
||||
Find(id string) (model.File, error)
|
||||
FindChildren(id string) ([]model.File, error)
|
||||
FindPath(id string) ([]model.File, error)
|
||||
FindTree(id string) ([]model.File, error)
|
||||
GetIDsByWorkspace(workspaceID string) ([]string, error)
|
||||
AssignSnapshots(cloneID string, originalID string) error
|
||||
MoveSourceIntoTarget(targetID string, sourceID string) error
|
||||
Save(file model.File) error
|
||||
BulkInsert(values []model.File, chunkSize int) error
|
||||
BulkInsertPermissions(values []*UserPermission, chunkSize int) error
|
||||
Delete(id string) error
|
||||
GetChildrenIDs(id string) ([]string, error)
|
||||
GetItemCount(id string) (int64, error)
|
||||
IsGrandChildOf(id string, ancestorID string) (bool, error)
|
||||
GetSize(id string) (int64, error)
|
||||
GrantUserPermission(id string, userID string, permission string) error
|
||||
RevokeUserPermission(id string, userID string) error
|
||||
GrantGroupPermission(id string, groupID string, permission string) error
|
||||
RevokeGroupPermission(id string, groupID string) error
|
||||
}
|
||||
|
||||
func NewFileRepo() FileRepo {
|
||||
return newFileRepo()
|
||||
}
|
||||
|
||||
func NewFile() model.File {
|
||||
return &fileEntity{}
|
||||
}
|
||||
|
||||
type fileEntity struct {
|
||||
ID string `json:"id" gorm:"column:id"`
|
||||
WorkspaceID string `json:"workspaceId" gorm:"column:workspace_id"`
|
||||
Name string `json:"name" gorm:"column:name"`
|
||||
Type string `json:"type" gorm:"column:type"`
|
||||
ParentID *string `json:"parentId,omitempty" gorm:"column:parent_id"`
|
||||
Snapshots []*snapshotEntity `json:"snapshots,omitempty" gorm:"-"`
|
||||
UserPermissions []*userPermissionValue `json:"userPermissions" gorm:"-"`
|
||||
GroupPermissions []*groupPermissionValue `json:"groupPermissions" gorm:"-"`
|
||||
Text *string `json:"text,omitempty" gorm:"-"`
|
||||
CreateTime string `json:"createTime" gorm:"column:create_time"`
|
||||
UpdateTime *string `json:"updateTime,omitempty" gorm:"column:update_time"`
|
||||
}
|
||||
|
||||
func (*fileEntity) TableName() string {
|
||||
return "file"
|
||||
}
|
||||
|
||||
func (f *fileEntity) BeforeCreate(*gorm.DB) (err error) {
|
||||
f.CreateTime = time.Now().UTC().Format(time.RFC3339)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fileEntity) BeforeSave(*gorm.DB) (err error) {
|
||||
timeNow := time.Now().UTC().Format(time.RFC3339)
|
||||
f.UpdateTime = &timeNow
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetID() string {
|
||||
return f.ID
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetWorkspaceID() string {
|
||||
return f.WorkspaceID
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetType() string {
|
||||
return f.Type
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetParentID() *string {
|
||||
return f.ParentID
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetSnapshots() []model.Snapshot {
|
||||
var res []model.Snapshot
|
||||
for _, s := range f.Snapshots {
|
||||
res = append(res, s)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetUserPermissions() []model.CoreUserPermission {
|
||||
var res []model.CoreUserPermission
|
||||
for _, p := range f.UserPermissions {
|
||||
res = append(res, p)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetGroupPermissions() []model.CoreGroupPermission {
|
||||
var res []model.CoreGroupPermission
|
||||
for _, p := range f.GroupPermissions {
|
||||
res = append(res, p)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetText() *string {
|
||||
return f.Text
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetCreateTime() string {
|
||||
return f.CreateTime
|
||||
}
|
||||
|
||||
func (f *fileEntity) GetUpdateTime() *string {
|
||||
return f.UpdateTime
|
||||
}
|
||||
|
||||
func (f *fileEntity) SetID(id string) {
|
||||
f.ID = id
|
||||
}
|
||||
|
||||
func (f *fileEntity) SetParentID(parentID *string) {
|
||||
f.ParentID = parentID
|
||||
}
|
||||
|
||||
func (f *fileEntity) SetWorkspaceID(workspaceID string) {
|
||||
f.WorkspaceID = workspaceID
|
||||
}
|
||||
|
||||
func (f *fileEntity) SetType(fileType string) {
|
||||
f.Type = fileType
|
||||
}
|
||||
|
||||
func (f *fileEntity) SetName(name string) {
|
||||
f.Name = name
|
||||
}
|
||||
|
||||
func (f *fileEntity) SetText(text *string) {
|
||||
f.Text = text
|
||||
}
|
||||
|
||||
func (f *fileEntity) SetCreateTime(createTime string) {
|
||||
f.CreateTime = createTime
|
||||
}
|
||||
|
||||
func (f *fileEntity) SetUpdateTime(updateTime *string) {
|
||||
f.UpdateTime = updateTime
|
||||
}
|
||||
|
||||
type fileRepo struct {
|
||||
db *gorm.DB
|
||||
snapshotRepo *snapshotRepo
|
||||
permissionRepo *permissionRepo
|
||||
}
|
||||
|
||||
func newFileRepo() *fileRepo {
|
||||
return &fileRepo{
|
||||
db: infra.GetDb(),
|
||||
snapshotRepo: newSnapshotRepo(),
|
||||
permissionRepo: newPermissionRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *fileRepo) Insert(opts FileInsertOptions) (model.File, error) {
|
||||
id := helper.NewID()
|
||||
file := fileEntity{
|
||||
ID: id,
|
||||
WorkspaceID: opts.WorkspaceID,
|
||||
Name: opts.Name,
|
||||
Type: opts.Type,
|
||||
ParentID: opts.ParentID,
|
||||
}
|
||||
if db := repo.db.Save(&file); db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
res, err := repo.find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := repo.populateModelFields([]*fileEntity{res}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) Find(id string) (model.File, error) {
|
||||
file, err := repo.find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := repo.populateModelFields([]*fileEntity{file}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) find(id string) (*fileEntity, error) {
|
||||
var res = fileEntity{}
|
||||
db := repo.db.Raw("SELECT * FROM file WHERE id = ?", id).Scan(&res)
|
||||
if db.Error != nil {
|
||||
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, errorpkg.NewFileNotFoundError(db.Error)
|
||||
} else {
|
||||
return nil, errorpkg.NewInternalServerError(db.Error)
|
||||
}
|
||||
}
|
||||
if len(res.ID) == 0 {
|
||||
return nil, errorpkg.NewFileNotFoundError(db.Error)
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) FindChildren(id string) ([]model.File, error) {
|
||||
var entities []*fileEntity
|
||||
db := repo.db.Raw("SELECT * FROM file WHERE parent_id = ? ORDER BY create_time ASC", id).Scan(&entities)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
if err := repo.populateModelFields(entities); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []model.File
|
||||
for _, f := range entities {
|
||||
res = append(res, f)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) FindPath(id string) ([]model.File, error) {
|
||||
var entities []*fileEntity
|
||||
if db := repo.db.
|
||||
Raw("WITH RECURSIVE rec (id, name, type, parent_id, workspace_id, create_time, update_time) AS "+
|
||||
"(SELECT f.id, f.name, f.type, f.parent_id, f.workspace_id, f.create_time, f.update_time FROM file f WHERE f.id = ? "+
|
||||
"UNION SELECT f.id, f.name, f.type, f.parent_id, f.workspace_id, f.create_time, f.update_time FROM rec, file f WHERE f.id = rec.parent_id) "+
|
||||
"SELECT * FROM rec", id).
|
||||
Scan(&entities); db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
if err := repo.populateModelFields(entities); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []model.File
|
||||
for _, f := range entities {
|
||||
res = append(res, f)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) FindTree(id string) ([]model.File, error) {
|
||||
var entities []*fileEntity
|
||||
db := repo.db.
|
||||
Raw("WITH RECURSIVE rec (id, name, type, parent_id, workspace_id, create_time, update_time) AS "+
|
||||
"(SELECT f.id, f.name, f.type, f.parent_id, f.workspace_id, f.create_time, f.update_time FROM file f WHERE f.id = ? "+
|
||||
"UNION SELECT f.id, f.name, f.type, f.parent_id, f.workspace_id, f.create_time, f.update_time FROM rec, file f WHERE f.parent_id = rec.id) "+
|
||||
"SELECT rec.* FROM rec ORDER BY create_time ASC", id).
|
||||
Scan(&entities)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
if err := repo.populateModelFields(entities); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []model.File
|
||||
for _, f := range entities {
|
||||
res = append(res, f)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) GetIDsByWorkspace(workspaceID string) ([]string, error) {
|
||||
type IDResult struct {
|
||||
Result string
|
||||
}
|
||||
var ids []IDResult
|
||||
db := repo.db.Raw("SELECT id result FROM file WHERE workspace_id = ? ORDER BY create_time ASC", workspaceID).Scan(&ids)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
res := []string{}
|
||||
for _, id := range ids {
|
||||
res = append(res, id.Result)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) AssignSnapshots(cloneID string, originalID string) error {
|
||||
if db := repo.db.Exec("INSERT INTO snapshot_file (snapshot_id, file_id) SELECT s.id, ? "+
|
||||
"FROM snapshot s LEFT JOIN snapshot_file map ON s.id = map.snapshot_id "+
|
||||
"WHERE map.file_id = ? ORDER BY s.version DESC LIMIT 1", cloneID, originalID); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) MoveSourceIntoTarget(targetID string, sourceID string) error {
|
||||
if db := repo.db.Exec("UPDATE file SET parent_id = ? WHERE id = ?", targetID, sourceID); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) Save(file model.File) error {
|
||||
if db := repo.db.Save(file); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) BulkInsert(values []model.File, chunkSize int) error {
|
||||
var entities []*fileEntity
|
||||
for _, f := range values {
|
||||
entities = append(entities, f.(*fileEntity))
|
||||
}
|
||||
if db := repo.db.CreateInBatches(entities, chunkSize); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) BulkInsertPermissions(values []*UserPermission, chunkSize int) error {
|
||||
if db := repo.db.CreateInBatches(values, chunkSize); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) Delete(id string) error {
|
||||
db := repo.db.Exec("DELETE FROM file WHERE id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
db = repo.db.Exec("DELETE FROM userpermission WHERE resource_id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
db = repo.db.Exec("DELETE FROM grouppermission WHERE resource_id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) GetChildrenIDs(id string) ([]string, error) {
|
||||
type Result struct {
|
||||
Result string
|
||||
}
|
||||
var results []Result
|
||||
db := repo.db.Raw("SELECT id result FROM file WHERE parent_id = ? ORDER BY create_time ASC", id).Scan(&results)
|
||||
if db.Error != nil {
|
||||
return []string{}, db.Error
|
||||
}
|
||||
res := []string{}
|
||||
for _, v := range results {
|
||||
res = append(res, v.Result)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) GetItemCount(id string) (int64, error) {
|
||||
type Result struct {
|
||||
Result int64
|
||||
}
|
||||
var res Result
|
||||
db := repo.db.
|
||||
Raw("WITH RECURSIVE rec (id, parent_id) AS "+
|
||||
"(SELECT f.id, f.parent_id FROM file f WHERE f.id = ? "+
|
||||
"UNION SELECT f.id, f.parent_id FROM rec, file f WHERE f.parent_id = rec.id) "+
|
||||
"SELECT count(rec.id) as result FROM rec", id).
|
||||
Scan(&res)
|
||||
if db.Error != nil {
|
||||
return 0, db.Error
|
||||
}
|
||||
return res.Result - 1, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) IsGrandChildOf(id string, ancestorID string) (bool, error) {
|
||||
type Result struct {
|
||||
Result bool
|
||||
}
|
||||
var res Result
|
||||
if db := repo.db.
|
||||
Raw("WITH RECURSIVE rec (id, parent_id) AS "+
|
||||
"(SELECT f.id, f.parent_id FROM file f WHERE f.id = ? "+
|
||||
"UNION SELECT f.id, f.parent_id FROM rec, file f WHERE f.parent_id = rec.id) "+
|
||||
"SELECT count(rec.id) > 0 as result FROM rec WHERE rec.id = ?", ancestorID, id).
|
||||
Scan(&res); db.Error != nil {
|
||||
return false, db.Error
|
||||
}
|
||||
return res.Result, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) GetSize(id string) (int64, error) {
|
||||
type Result struct {
|
||||
Result int64
|
||||
}
|
||||
var res Result
|
||||
db := repo.db.
|
||||
Raw("WITH RECURSIVE rec (id, parent_id) AS "+
|
||||
"(SELECT f.id, f.parent_id FROM file f WHERE f.id = ? "+
|
||||
"UNION SELECT f.id, f.parent_id FROM rec, file f WHERE f.parent_id = rec.id) "+
|
||||
"SELECT coalesce(sum((s.original->>'size')::int), 0) as result FROM snapshot s, rec "+
|
||||
"LEFT JOIN snapshot_file map ON rec.id = map.file_id WHERE map.snapshot_id = s.id", id).
|
||||
Scan(&res)
|
||||
if db.Error != nil {
|
||||
return res.Result, db.Error
|
||||
}
|
||||
return res.Result, nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) GrantUserPermission(id string, userID string, permission string) error {
|
||||
/* Grant permission to workspace */
|
||||
db := repo.db.Exec("INSERT INTO userpermission (id, user_id, resource_id, permission) "+
|
||||
"(SELECT ?, ?, w.id, 'viewer' FROM file f "+
|
||||
"INNER JOIN workspace w ON w.id = f.workspace_id AND f.id = ?) "+
|
||||
"ON CONFLICT DO NOTHING",
|
||||
helper.NewID(), userID, id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
|
||||
/* Grant 'viewer' permission to path files */
|
||||
path, err := repo.FindPath(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range path {
|
||||
db := repo.db.Exec("INSERT INTO userpermission (id, user_id, resource_id, permission) "+
|
||||
"VALUES (?, ?, ?, 'viewer') ON CONFLICT DO NOTHING",
|
||||
helper.NewID(), userID, f.GetID())
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
}
|
||||
|
||||
/* Grant the requested permission to tree files */
|
||||
tree, err := repo.FindTree(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range tree {
|
||||
db := repo.db.Exec("INSERT INTO userpermission (id, user_id, resource_id, permission) "+
|
||||
"VALUES (?, ?, ?, ?) ON CONFLICT (user_id, resource_id) DO UPDATE SET permission = ?",
|
||||
helper.NewID(), userID, f.GetID(), permission, permission)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) RevokeUserPermission(id string, userID string) error {
|
||||
tree, err := repo.FindTree(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range tree {
|
||||
db := repo.db.Exec("DELETE FROM userpermission WHERE user_id = ? AND resource_id = ?", userID, f.GetID())
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) GrantGroupPermission(id string, groupID string, permission string) error {
|
||||
/* Grant permission to workspace */
|
||||
db := repo.db.Exec("INSERT INTO grouppermission (id, group_id, resource_id, permission) "+
|
||||
"(SELECT ?, ?, w.id, 'viewer' FROM file f "+
|
||||
"INNER JOIN workspace w ON w.id = f.workspace_id AND f.id = ?) "+
|
||||
"ON CONFLICT DO NOTHING",
|
||||
helper.NewID(), groupID, id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
|
||||
/* Grant 'viewer' permission to path files */
|
||||
path, err := repo.FindPath(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range path {
|
||||
db := repo.db.Exec("INSERT INTO grouppermission (id, group_id, resource_id, permission) "+
|
||||
"VALUES (?, ?, ?, 'viewer') ON CONFLICT DO NOTHING",
|
||||
helper.NewID(), groupID, f.GetID())
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
}
|
||||
|
||||
/* Grant the requested permission to tree files */
|
||||
tree, err := repo.FindTree(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range tree {
|
||||
db := repo.db.Exec("INSERT INTO grouppermission (id, group_id, resource_id, permission) "+
|
||||
"VALUES (?, ?, ?, ?) ON CONFLICT (group_id, resource_id) DO UPDATE SET permission = ?",
|
||||
helper.NewID(), groupID, f.GetID(), permission, permission)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) RevokeGroupPermission(id string, groupID string) error {
|
||||
tree, err := repo.FindTree(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range tree {
|
||||
db := repo.db.Exec("DELETE FROM grouppermission WHERE group_id = ? AND resource_id = ?", groupID, f.GetID())
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *fileRepo) populateModelFields(entities []*fileEntity) error {
|
||||
for _, f := range entities {
|
||||
f.UserPermissions = make([]*userPermissionValue, 0)
|
||||
userPermissions, err := repo.permissionRepo.GetUserPermissions(f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range userPermissions {
|
||||
f.UserPermissions = append(f.UserPermissions, &userPermissionValue{
|
||||
UserID: p.UserID,
|
||||
Value: p.Permission,
|
||||
})
|
||||
}
|
||||
f.GroupPermissions = make([]*groupPermissionValue, 0)
|
||||
groupPermissions, err := repo.permissionRepo.GetGroupPermissions(f.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range groupPermissions {
|
||||
f.GroupPermissions = append(f.GroupPermissions, &groupPermissionValue{
|
||||
GroupID: p.GroupID,
|
||||
Value: p.Permission,
|
||||
})
|
||||
}
|
||||
snapshots, err := repo.snapshotRepo.findAllForFile(f.ID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
f.Snapshots = snapshots
|
||||
}
|
||||
return nil
|
||||
}
|
344
Downloads/Voltaserve/api/repo/group_repo.go
Normal file
344
Downloads/Voltaserve/api/repo/group_repo.go
Normal file
@@ -0,0 +1,344 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type GroupInsertOptions struct {
|
||||
ID string
|
||||
Name string
|
||||
OrganizationID string
|
||||
OwnerID string
|
||||
}
|
||||
|
||||
type GroupRepo interface {
|
||||
Insert(opts GroupInsertOptions) (model.Group, error)
|
||||
Find(id string) (model.Group, error)
|
||||
GetIDsForFile(fileID string) ([]string, error)
|
||||
GetIDsForUser(userID string) ([]string, error)
|
||||
GetIDsForOrganization(id string) ([]string, error)
|
||||
Save(group model.Group) error
|
||||
Delete(id string) error
|
||||
AddUser(id string, userID string) error
|
||||
RemoveMember(id string, userID string) error
|
||||
GetIDs() ([]string, error)
|
||||
GetMembers(id string) ([]model.User, error)
|
||||
GrantUserPermission(id string, userID string, permission string) error
|
||||
RevokeUserPermission(id string, userID string) error
|
||||
}
|
||||
|
||||
func NewGroupRepo() GroupRepo {
|
||||
return newGroupRepo()
|
||||
}
|
||||
|
||||
func NewGroup() model.Group {
|
||||
return &groupEntity{}
|
||||
}
|
||||
|
||||
type groupEntity struct {
|
||||
ID string `json:"id" gorm:"column:id"`
|
||||
Name string `json:"name" gorm:"column:name"`
|
||||
OrganizationID string `json:"organizationId" gorm:"column:organization_id"`
|
||||
UserPermissions []*userPermissionValue `json:"userPermissions" gorm:"-"`
|
||||
GroupPermissions []*groupPermissionValue `json:"groupPermissions" gorm:"-"`
|
||||
Members []string `json:"members" gorm:"-"`
|
||||
CreateTime string `json:"createTime" gorm:"column:create_time"`
|
||||
UpdateTime *string `json:"updateTime" gorm:"column:update_time"`
|
||||
}
|
||||
|
||||
func (*groupEntity) TableName() string {
|
||||
return "group"
|
||||
}
|
||||
|
||||
func (g *groupEntity) BeforeCreate(*gorm.DB) (err error) {
|
||||
g.CreateTime = time.Now().UTC().Format(time.RFC3339)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *groupEntity) BeforeSave(*gorm.DB) (err error) {
|
||||
timeNow := time.Now().UTC().Format(time.RFC3339)
|
||||
g.UpdateTime = &timeNow
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *groupEntity) GetID() string {
|
||||
return g.ID
|
||||
}
|
||||
|
||||
func (g *groupEntity) GetName() string {
|
||||
return g.Name
|
||||
}
|
||||
|
||||
func (g *groupEntity) GetOrganizationID() string {
|
||||
return g.OrganizationID
|
||||
}
|
||||
|
||||
func (g *groupEntity) GetUserPermissions() []model.CoreUserPermission {
|
||||
var res []model.CoreUserPermission
|
||||
for _, p := range g.UserPermissions {
|
||||
res = append(res, p)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (g *groupEntity) GetGroupPermissions() []model.CoreGroupPermission {
|
||||
var res []model.CoreGroupPermission
|
||||
for _, p := range g.GroupPermissions {
|
||||
res = append(res, p)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (g *groupEntity) GetUsers() []string {
|
||||
return g.Members
|
||||
}
|
||||
|
||||
func (g *groupEntity) GetCreateTime() string {
|
||||
return g.CreateTime
|
||||
}
|
||||
|
||||
func (g *groupEntity) GetUpdateTime() *string {
|
||||
return g.UpdateTime
|
||||
}
|
||||
|
||||
func (g *groupEntity) SetName(name string) {
|
||||
g.Name = name
|
||||
}
|
||||
|
||||
func (g *groupEntity) SetUpdateTime(updateTime *string) {
|
||||
g.UpdateTime = updateTime
|
||||
}
|
||||
|
||||
type groupRepo struct {
|
||||
db *gorm.DB
|
||||
permissionRepo *permissionRepo
|
||||
}
|
||||
|
||||
func newGroupRepo() *groupRepo {
|
||||
return &groupRepo{
|
||||
db: infra.GetDb(),
|
||||
permissionRepo: newPermissionRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *groupRepo) Insert(opts GroupInsertOptions) (model.Group, error) {
|
||||
group := groupEntity{
|
||||
ID: opts.ID,
|
||||
Name: opts.Name,
|
||||
OrganizationID: opts.OrganizationID,
|
||||
}
|
||||
if db := repo.db.Save(&group); db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
res, err := repo.Find(opts.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) find(id string) (*groupEntity, error) {
|
||||
var res = groupEntity{}
|
||||
db := repo.db.Where("id = ?", id).First(&res)
|
||||
if db.Error != nil {
|
||||
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, errorpkg.NewGroupNotFoundError(db.Error)
|
||||
} else {
|
||||
return nil, errorpkg.NewInternalServerError(db.Error)
|
||||
}
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) Find(id string) (model.Group, error) {
|
||||
group, err := repo.find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := repo.populateModelFields([]*groupEntity{group}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return group, nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) GetIDsForFile(fileID string) ([]string, error) {
|
||||
type Result struct {
|
||||
Result string
|
||||
}
|
||||
var results []Result
|
||||
db := repo.db.
|
||||
Raw(`SELECT DISTINCT g.id as result FROM "group" g INNER JOIN grouppermission p ON p.resource_id = ? WHERE p.group_id = g.id`, fileID).
|
||||
Scan(&results)
|
||||
if db.Error != nil {
|
||||
return []string{}, db.Error
|
||||
}
|
||||
res := []string{}
|
||||
for _, v := range results {
|
||||
res = append(res, v.Result)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) GetIDsForUser(userID string) ([]string, error) {
|
||||
type Result struct {
|
||||
Result string
|
||||
}
|
||||
var results []Result
|
||||
db := repo.db.Raw(`SELECT group_id from group_user WHERE user_id = ?`, userID).Scan(&results)
|
||||
if db.Error != nil {
|
||||
return []string{}, db.Error
|
||||
}
|
||||
res := []string{}
|
||||
for _, v := range results {
|
||||
res = append(res, v.Result)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) GetIDsForOrganization(id string) ([]string, error) {
|
||||
type Result struct {
|
||||
Result string
|
||||
}
|
||||
var results []Result
|
||||
db := repo.db.Raw(`SELECT id as result from "group" WHERE organization_id = ?`, id).Scan(&results)
|
||||
if db.Error != nil {
|
||||
return []string{}, db.Error
|
||||
}
|
||||
res := []string{}
|
||||
for _, v := range results {
|
||||
res = append(res, v.Result)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) Save(group model.Group) error {
|
||||
db := repo.db.Save(group)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) Delete(id string) error {
|
||||
db := repo.db.Exec(`DELETE FROM "group" WHERE id = ?`, id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
db = repo.db.Exec("DELETE FROM userpermission WHERE resource_id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
db = repo.db.Exec("DELETE FROM grouppermission WHERE resource_id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) AddUser(id string, userID string) error {
|
||||
db := repo.db.Exec("INSERT INTO group_user (group_id, user_id) VALUES (?, ?)", id, userID)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) RemoveMember(id string, userID string) error {
|
||||
db := repo.db.Exec("DELETE FROM group_user WHERE group_id = ? AND user_id = ?", id, userID)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) GetIDs() ([]string, error) {
|
||||
type Result struct {
|
||||
Result string
|
||||
}
|
||||
var results []Result
|
||||
db := repo.db.Raw(`SELECT id result FROM "group" ORDER BY create_time DESC`).Scan(&results)
|
||||
if db.Error != nil {
|
||||
return []string{}, db.Error
|
||||
}
|
||||
res := []string{}
|
||||
for _, v := range results {
|
||||
res = append(res, v.Result)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) GetMembers(id string) ([]model.User, error) {
|
||||
var entities []*userEntity
|
||||
db := repo.db.
|
||||
Raw(`SELECT DISTINCT u.* FROM "user" u INNER JOIN group_user gu ON u.id = gu.user_id WHERE gu.group_id = ?`, id).
|
||||
Scan(&entities)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
var res []model.User
|
||||
for _, u := range entities {
|
||||
res = append(res, u)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) GrantUserPermission(id string, userID string, permission string) error {
|
||||
db := repo.db.Exec(
|
||||
"INSERT INTO userpermission (id, user_id, resource_id, permission) VALUES (?, ?, ?, ?) ON CONFLICT (user_id, resource_id) DO UPDATE SET permission = ?",
|
||||
helper.NewID(), userID, id, permission, permission)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) RevokeUserPermission(id string, userID string) error {
|
||||
db := repo.db.Exec("DELETE FROM userpermission WHERE user_id = ? AND resource_id = ?", userID, id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *groupRepo) populateModelFields(groups []*groupEntity) error {
|
||||
for _, g := range groups {
|
||||
g.UserPermissions = make([]*userPermissionValue, 0)
|
||||
userPermissions, err := repo.permissionRepo.GetUserPermissions(g.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range userPermissions {
|
||||
g.UserPermissions = append(g.UserPermissions, &userPermissionValue{
|
||||
UserID: p.UserID,
|
||||
Value: p.Permission,
|
||||
})
|
||||
}
|
||||
g.GroupPermissions = make([]*groupPermissionValue, 0)
|
||||
groupPermissions, err := repo.permissionRepo.GetGroupPermissions(g.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range groupPermissions {
|
||||
g.GroupPermissions = append(g.GroupPermissions, &groupPermissionValue{
|
||||
GroupID: p.GroupID,
|
||||
Value: p.Permission,
|
||||
})
|
||||
}
|
||||
members, err := repo.GetMembers(g.ID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
g.Members = make([]string, 0)
|
||||
for _, u := range members {
|
||||
g.Members = append(g.Members, u.GetID())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
185
Downloads/Voltaserve/api/repo/invitation_repo.go
Normal file
185
Downloads/Voltaserve/api/repo/invitation_repo.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type InvitationInsertOptions struct {
|
||||
UserID string
|
||||
OrganizationID string
|
||||
Emails []string
|
||||
}
|
||||
|
||||
type InvitationRepo interface {
|
||||
Insert(opts InvitationInsertOptions) ([]model.Invitation, error)
|
||||
Find(id string) (model.Invitation, error)
|
||||
GetIncoming(email string) ([]model.Invitation, error)
|
||||
GetOutgoing(orgID string, userID string) ([]model.Invitation, error)
|
||||
Save(org model.Invitation) error
|
||||
Delete(id string) error
|
||||
}
|
||||
|
||||
func NewInvitationRepo() InvitationRepo {
|
||||
return newInvitationRepo()
|
||||
}
|
||||
|
||||
type invitationEntity struct {
|
||||
ID string `json:"id" gorm:"column:id"`
|
||||
OrganizationID string `json:"organizationId" gorm:"column:organization_id"`
|
||||
OwnerID string `json:"ownerId" gorm:"column:owner_id"`
|
||||
Email string `json:"email" gorm:"column:email"`
|
||||
Status string `json:"status" gorm:"column:status"`
|
||||
CreateTime string `json:"createTime" gorm:"column:create_time"`
|
||||
UpdateTime *string `json:"updateTime" gorm:"column:update_time"`
|
||||
}
|
||||
|
||||
func (*invitationEntity) TableName() string {
|
||||
return "invitation"
|
||||
}
|
||||
|
||||
func (i *invitationEntity) BeforeCreate(*gorm.DB) (err error) {
|
||||
i.CreateTime = time.Now().UTC().Format(time.RFC3339)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *invitationEntity) BeforeSave(*gorm.DB) (err error) {
|
||||
timeNow := time.Now().UTC().Format(time.RFC3339)
|
||||
i.UpdateTime = &timeNow
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *invitationEntity) GetID() string {
|
||||
return i.ID
|
||||
}
|
||||
|
||||
func (i *invitationEntity) GetOrganizationID() string {
|
||||
return i.OrganizationID
|
||||
}
|
||||
|
||||
func (i *invitationEntity) GetOwnerID() string {
|
||||
return i.OwnerID
|
||||
}
|
||||
|
||||
func (i *invitationEntity) GetEmail() string {
|
||||
return i.Email
|
||||
}
|
||||
|
||||
func (i *invitationEntity) GetStatus() string {
|
||||
return i.Status
|
||||
}
|
||||
|
||||
func (i *invitationEntity) GetCreateTime() string {
|
||||
return i.CreateTime
|
||||
}
|
||||
|
||||
func (i *invitationEntity) GetUpdateTime() *string {
|
||||
return i.UpdateTime
|
||||
}
|
||||
|
||||
func (i *invitationEntity) SetStatus(status string) {
|
||||
i.Status = status
|
||||
}
|
||||
|
||||
func (i *invitationEntity) SetUpdateTime(updateTime *string) {
|
||||
i.UpdateTime = updateTime
|
||||
}
|
||||
|
||||
type invitationRepo struct {
|
||||
db *gorm.DB
|
||||
userRepo *userRepo
|
||||
}
|
||||
|
||||
func newInvitationRepo() *invitationRepo {
|
||||
return &invitationRepo{
|
||||
db: infra.GetDb(),
|
||||
userRepo: newUserRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *invitationRepo) Insert(opts InvitationInsertOptions) ([]model.Invitation, error) {
|
||||
var res []model.Invitation
|
||||
for _, e := range opts.Emails {
|
||||
invitation := invitationEntity{
|
||||
ID: helper.NewID(),
|
||||
OrganizationID: opts.OrganizationID,
|
||||
OwnerID: opts.UserID,
|
||||
Email: e,
|
||||
Status: model.InvitationStatusPending,
|
||||
}
|
||||
if db := repo.db.Save(&invitation); db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
i, err := repo.Find(invitation.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, i)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *invitationRepo) Find(id string) (model.Invitation, error) {
|
||||
var invitation = invitationEntity{}
|
||||
db := repo.db.Where("id = ?", id).First(&invitation)
|
||||
if db.Error != nil {
|
||||
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, errorpkg.NewInvitationNotFoundError(db.Error)
|
||||
} else {
|
||||
return nil, errorpkg.NewInternalServerError(db.Error)
|
||||
}
|
||||
}
|
||||
return &invitation, nil
|
||||
}
|
||||
|
||||
func (repo *invitationRepo) GetIncoming(email string) ([]model.Invitation, error) {
|
||||
var invitations []*invitationEntity
|
||||
db := repo.db.
|
||||
Raw("SELECT * FROM invitation WHERE email = ? and status = 'pending' ORDER BY create_time DESC", email).
|
||||
Scan(&invitations)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
var res []model.Invitation
|
||||
for _, inv := range invitations {
|
||||
res = append(res, inv)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *invitationRepo) GetOutgoing(orgID string, userID string) ([]model.Invitation, error) {
|
||||
var invitations []*invitationEntity
|
||||
db := repo.db.
|
||||
Raw("SELECT * FROM invitation WHERE organization_id = ? and owner_id = ? ORDER BY create_time DESC", orgID, userID).
|
||||
Scan(&invitations)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
var res []model.Invitation
|
||||
for _, inv := range invitations {
|
||||
res = append(res, inv)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *invitationRepo) Save(org model.Invitation) error {
|
||||
db := repo.db.Save(org)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *invitationRepo) Delete(id string) error {
|
||||
db := repo.db.Exec("DELETE FROM invitation WHERE id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
319
Downloads/Voltaserve/api/repo/organization_repo.go
Normal file
319
Downloads/Voltaserve/api/repo/organization_repo.go
Normal file
@@ -0,0 +1,319 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type OrganizationInsertOptions struct {
|
||||
ID string
|
||||
Name string
|
||||
}
|
||||
|
||||
type OrganizationRepo interface {
|
||||
Insert(opts OrganizationInsertOptions) (model.Organization, error)
|
||||
Find(id string) (model.Organization, error)
|
||||
Save(org model.Organization) error
|
||||
Delete(id string) error
|
||||
GetIDs() ([]string, error)
|
||||
AddUser(id string, userID string) error
|
||||
RemoveMember(id string, userID string) error
|
||||
GetMembers(id string) ([]model.User, error)
|
||||
GetGroups(id string) ([]model.Group, error)
|
||||
GetOwnerCount(id string) (int64, error)
|
||||
GrantUserPermission(id string, userID string, permission string) error
|
||||
RevokeUserPermission(id string, userID string) error
|
||||
}
|
||||
|
||||
func NewOrganizationRepo() OrganizationRepo {
|
||||
return newOrganizationRepo()
|
||||
}
|
||||
|
||||
func NewOrganization() model.Organization {
|
||||
return &organizationEntity{}
|
||||
}
|
||||
|
||||
type organizationEntity struct {
|
||||
ID string `json:"id" gorm:"column:id"`
|
||||
Name string `json:"name" gorm:"column:name"`
|
||||
UserPermissions []*userPermissionValue `json:"userPermissions" gorm:"-"`
|
||||
GroupPermissions []*groupPermissionValue `json:"groupPermissions" gorm:"-"`
|
||||
Members []string `json:"members" gorm:"-"`
|
||||
CreateTime string `json:"createTime" gorm:"column:create_time"`
|
||||
UpdateTime *string `json:"updateTime,omitempty" gorm:"column:update_time"`
|
||||
}
|
||||
|
||||
func (*organizationEntity) TableName() string {
|
||||
return "organization"
|
||||
}
|
||||
|
||||
func (o *organizationEntity) BeforeCreate(*gorm.DB) (err error) {
|
||||
o.CreateTime = time.Now().UTC().Format(time.RFC3339)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *organizationEntity) BeforeSave(*gorm.DB) (err error) {
|
||||
timeNow := time.Now().UTC().Format(time.RFC3339)
|
||||
o.UpdateTime = &timeNow
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *organizationEntity) GetID() string {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
func (o *organizationEntity) GetName() string {
|
||||
return o.Name
|
||||
}
|
||||
|
||||
func (o *organizationEntity) GetUserPermissions() []model.CoreUserPermission {
|
||||
var res []model.CoreUserPermission
|
||||
for _, p := range o.UserPermissions {
|
||||
res = append(res, p)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (o *organizationEntity) GetGroupPermissions() []model.CoreGroupPermission {
|
||||
var res []model.CoreGroupPermission
|
||||
for _, p := range o.GroupPermissions {
|
||||
res = append(res, p)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (o *organizationEntity) GetUsers() []string {
|
||||
return o.Members
|
||||
}
|
||||
|
||||
func (o *organizationEntity) GetCreateTime() string {
|
||||
return o.CreateTime
|
||||
}
|
||||
|
||||
func (o *organizationEntity) GetUpdateTime() *string {
|
||||
return o.UpdateTime
|
||||
}
|
||||
|
||||
func (o *organizationEntity) SetName(name string) {
|
||||
o.Name = name
|
||||
}
|
||||
|
||||
func (o *organizationEntity) SetUpdateTime(updateTime *string) {
|
||||
o.UpdateTime = updateTime
|
||||
}
|
||||
|
||||
type organizationRepo struct {
|
||||
db *gorm.DB
|
||||
groupRepo *groupRepo
|
||||
permissionRepo *permissionRepo
|
||||
}
|
||||
|
||||
func newOrganizationRepo() *organizationRepo {
|
||||
return &organizationRepo{
|
||||
db: infra.GetDb(),
|
||||
groupRepo: newGroupRepo(),
|
||||
permissionRepo: newPermissionRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) Insert(opts OrganizationInsertOptions) (model.Organization, error) {
|
||||
org := organizationEntity{
|
||||
ID: opts.ID,
|
||||
Name: opts.Name,
|
||||
}
|
||||
if db := repo.db.Save(&org); db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
res, err := repo.Find(opts.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) find(id string) (*organizationEntity, error) {
|
||||
var res = organizationEntity{}
|
||||
db := repo.db.Where("id = ?", id).First(&res)
|
||||
if db.Error != nil {
|
||||
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, errorpkg.NewOrganizationNotFoundError(db.Error)
|
||||
} else {
|
||||
return nil, errorpkg.NewInternalServerError(db.Error)
|
||||
}
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) Find(id string) (model.Organization, error) {
|
||||
org, err := repo.find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := repo.populateModelFields([]*organizationEntity{org}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return org, nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) Save(org model.Organization) error {
|
||||
db := repo.db.Save(org)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) Delete(id string) error {
|
||||
db := repo.db.Exec("DELETE FROM organization WHERE id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
db = repo.db.Exec("DELETE FROM userpermission WHERE resource_id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
db = repo.db.Exec("DELETE FROM grouppermission WHERE resource_id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) GetIDs() ([]string, error) {
|
||||
type Result struct {
|
||||
Result string
|
||||
}
|
||||
var results []Result
|
||||
db := repo.db.Raw("SELECT id result FROM organization ORDER BY create_time DESC").Scan(&results)
|
||||
if db.Error != nil {
|
||||
return []string{}, db.Error
|
||||
}
|
||||
res := []string{}
|
||||
for _, v := range results {
|
||||
res = append(res, v.Result)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) AddUser(id string, userID string) error {
|
||||
db := repo.db.Exec("INSERT INTO organization_user (organization_id, user_id) VALUES (?, ?)", id, userID)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) RemoveMember(id string, userID string) error {
|
||||
db := repo.db.Exec("DELETE FROM organization_user WHERE organization_id = ? AND user_id = ?", id, userID)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) GetMembers(id string) ([]model.User, error) {
|
||||
var entities []*userEntity
|
||||
db := repo.db.
|
||||
Raw(`SELECT DISTINCT u.* FROM "user" u INNER JOIN organization_user ou ON u.id = ou.user_id WHERE ou.organization_id = ? ORDER BY u.full_name`, id).
|
||||
Scan(&entities)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
var res []model.User
|
||||
for _, u := range entities {
|
||||
res = append(res, u)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) GetGroups(id string) ([]model.Group, error) {
|
||||
var entities []*groupEntity
|
||||
db := repo.db.
|
||||
Raw(`SELECT * FROM "group" g WHERE g.organization_id = ? ORDER BY g.name`, id).
|
||||
Scan(&entities)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
if err := repo.groupRepo.populateModelFields(entities); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []model.Group
|
||||
for _, g := range entities {
|
||||
res = append(res, g)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) GetOwnerCount(id string) (int64, error) {
|
||||
type Result struct {
|
||||
Result int64
|
||||
}
|
||||
var res Result
|
||||
db := repo.db.
|
||||
Raw("SELECT count(*) as result FROM userpermission WHERE resource_id = ? and permission = ?", id, model.PermissionOwner).
|
||||
Scan(&res)
|
||||
if db.Error != nil {
|
||||
return 0, db.Error
|
||||
}
|
||||
return res.Result, nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) GrantUserPermission(id string, userID string, permission string) error {
|
||||
db := repo.db.Exec(
|
||||
"INSERT INTO userpermission (id, user_id, resource_id, permission) VALUES (?, ?, ?, ?) ON CONFLICT (user_id, resource_id) DO UPDATE SET permission = ?",
|
||||
helper.NewID(), userID, id, permission, permission)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) RevokeUserPermission(id string, userID string) error {
|
||||
db := repo.db.Exec("DELETE FROM userpermission WHERE user_id = ? AND resource_id = ?", userID, id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *organizationRepo) populateModelFields(organizations []*organizationEntity) error {
|
||||
for _, o := range organizations {
|
||||
o.UserPermissions = make([]*userPermissionValue, 0)
|
||||
userPermissions, err := repo.permissionRepo.GetUserPermissions(o.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range userPermissions {
|
||||
o.UserPermissions = append(o.UserPermissions, &userPermissionValue{
|
||||
UserID: p.UserID,
|
||||
Value: p.Permission,
|
||||
})
|
||||
}
|
||||
o.GroupPermissions = make([]*groupPermissionValue, 0)
|
||||
groupPermissions, err := repo.permissionRepo.GetGroupPermissions(o.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range groupPermissions {
|
||||
o.GroupPermissions = append(o.GroupPermissions, &groupPermissionValue{
|
||||
GroupID: p.GroupID,
|
||||
Value: p.Permission,
|
||||
})
|
||||
}
|
||||
members, err := repo.GetMembers(o.ID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
o.Members = make([]string, 0)
|
||||
for _, u := range members {
|
||||
o.Members = append(o.Members, u.GetID())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
102
Downloads/Voltaserve/api/repo/permission_repo.go
Normal file
102
Downloads/Voltaserve/api/repo/permission_repo.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"voltaserve/infra"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserPermission struct {
|
||||
ID string `json:"id" gorm:"column:id"`
|
||||
UserID string `json:"userId" gorm:"column:user_id"`
|
||||
ResourceID string `json:"resourceId" gorm:"column:resource_id"`
|
||||
Permission string `json:"permission" gorm:"column:permission"`
|
||||
CreateTime string `json:"createTime" gorm:"column:create_time"`
|
||||
}
|
||||
|
||||
type GroupPermission struct {
|
||||
ID string `json:"id" gorm:"column:id"`
|
||||
GroupID string `json:"groupId" gorm:"column:group_id"`
|
||||
ResourceID string `json:"resourceId" gorm:"column:resource_id"`
|
||||
Permission string `json:"permission" gorm:"column:permission"`
|
||||
CreateTime string `json:"createTime" gorm:"column:create_time"`
|
||||
}
|
||||
|
||||
type PermissionRepo interface {
|
||||
GetUserPermissions(id string) ([]*UserPermission, error)
|
||||
GetGroupPermissions(id string) ([]*GroupPermission, error)
|
||||
}
|
||||
|
||||
func NewPermissionRepo() PermissionRepo {
|
||||
return newPermissionRepo()
|
||||
}
|
||||
|
||||
func (UserPermission) TableName() string {
|
||||
return "userpermission"
|
||||
}
|
||||
|
||||
func (GroupPermission) TableName() string {
|
||||
return "grouppermission"
|
||||
}
|
||||
|
||||
type userPermissionValue struct {
|
||||
UserID string `json:"userId,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (p userPermissionValue) GetUserID() string {
|
||||
return p.UserID
|
||||
}
|
||||
func (p userPermissionValue) GetValue() string {
|
||||
return p.Value
|
||||
}
|
||||
|
||||
type groupPermissionValue struct {
|
||||
GroupID string `json:"groupId,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (p groupPermissionValue) GetGroupID() string {
|
||||
return p.GroupID
|
||||
}
|
||||
func (p groupPermissionValue) GetValue() string {
|
||||
return p.Value
|
||||
}
|
||||
|
||||
type permissionRepo struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func newPermissionRepo() *permissionRepo {
|
||||
return &permissionRepo{
|
||||
db: infra.GetDb(),
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *permissionRepo) GetUserPermissions(id string) ([]*UserPermission, error) {
|
||||
var res []*UserPermission
|
||||
if db := repo.db.
|
||||
Raw("SELECT * FROM userpermission WHERE resource_id = ?", id).
|
||||
Scan(&res); db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return res, nil
|
||||
} else {
|
||||
return []*UserPermission{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *permissionRepo) GetGroupPermissions(id string) ([]*GroupPermission, error) {
|
||||
var res []*GroupPermission
|
||||
if db := repo.db.
|
||||
Raw("SELECT * FROM grouppermission WHERE resource_id = ?", id).
|
||||
Scan(&res); db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return res, nil
|
||||
} else {
|
||||
return []*GroupPermission{}, nil
|
||||
}
|
||||
}
|
345
Downloads/Voltaserve/api/repo/snapshot_repo.go
Normal file
345
Downloads/Voltaserve/api/repo/snapshot_repo.go
Normal file
@@ -0,0 +1,345 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SnapshotUpdateOptions struct {
|
||||
Original *model.S3Object
|
||||
Preview *model.S3Object
|
||||
Text *model.S3Object
|
||||
Thumbnail *model.Thumbnail
|
||||
Status string
|
||||
}
|
||||
|
||||
type SnapshotRepo interface {
|
||||
Find(id string) (model.Snapshot, error)
|
||||
Save(snapshot model.Snapshot) error
|
||||
Update(id string, opts SnapshotUpdateOptions) error
|
||||
MapWithFile(id string, fileID string) error
|
||||
DeleteMappingsForFile(fileID string) error
|
||||
FindAllDangling() ([]model.Snapshot, error)
|
||||
DeleteAllDangling() error
|
||||
GetLatestVersionForFile(fileID string) (int64, error)
|
||||
}
|
||||
|
||||
func NewSnapshotRepo() SnapshotRepo {
|
||||
return newSnapshotRepo()
|
||||
}
|
||||
|
||||
func NewSnapshot() model.Snapshot {
|
||||
return &snapshotEntity{}
|
||||
}
|
||||
|
||||
type snapshotEntity struct {
|
||||
ID string `json:"id" gorm:"column:id;size:36"`
|
||||
Version int64 `json:"version" gorm:"column:version"`
|
||||
Original datatypes.JSON `json:"original,omitempty" gorm:"column:original"`
|
||||
Preview datatypes.JSON `json:"preview,omitempty" gorm:"column:preview"`
|
||||
Text datatypes.JSON `json:"text,omitempty" gorm:"column:text"`
|
||||
Thumbnail datatypes.JSON `json:"thumbnail,omitempty" gorm:"column:thumbnail"`
|
||||
Status string `json:"status,omitempty" gorm:"column,status"`
|
||||
CreateTime string `json:"createTime" gorm:"column:create_time"`
|
||||
UpdateTime *string `json:"updateTime,omitempty" gorm:"column:update_time"`
|
||||
}
|
||||
|
||||
func (*snapshotEntity) TableName() string {
|
||||
return "snapshot"
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) BeforeCreate(*gorm.DB) (err error) {
|
||||
s.CreateTime = time.Now().UTC().Format(time.RFC3339)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) BeforeSave(*gorm.DB) (err error) {
|
||||
timeNow := time.Now().UTC().Format(time.RFC3339)
|
||||
s.UpdateTime = &timeNow
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) GetID() string {
|
||||
return s.ID
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) GetVersion() int64 {
|
||||
return s.Version
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) GetOriginal() *model.S3Object {
|
||||
if s.Original.String() == "" {
|
||||
return nil
|
||||
}
|
||||
var res = model.S3Object{}
|
||||
if err := json.Unmarshal([]byte(s.Original.String()), &res); err != nil {
|
||||
log.Fatal(err)
|
||||
return nil
|
||||
}
|
||||
return &res
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) GetPreview() *model.S3Object {
|
||||
if s.Preview.String() == "" {
|
||||
return nil
|
||||
}
|
||||
var res = model.S3Object{}
|
||||
if err := json.Unmarshal([]byte(s.Preview.String()), &res); err != nil {
|
||||
log.Fatal(err)
|
||||
return nil
|
||||
}
|
||||
return &res
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) GetText() *model.S3Object {
|
||||
if s.Text.String() == "" {
|
||||
return nil
|
||||
}
|
||||
var res = model.S3Object{}
|
||||
if err := json.Unmarshal([]byte(s.Text.String()), &res); err != nil {
|
||||
log.Fatal(err)
|
||||
return nil
|
||||
}
|
||||
return &res
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) GetThumbnail() *model.Thumbnail {
|
||||
if s.Thumbnail.String() == "" {
|
||||
return nil
|
||||
}
|
||||
var res = model.Thumbnail{}
|
||||
if err := json.Unmarshal([]byte(s.Thumbnail.String()), &res); err != nil {
|
||||
log.Fatal(err)
|
||||
return nil
|
||||
}
|
||||
return &res
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) GetStatus() string {
|
||||
return s.Status
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) SetID(id string) {
|
||||
s.ID = id
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) SetVersion(version int64) {
|
||||
s.Version = version
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) SetOriginal(m *model.S3Object) {
|
||||
if m == nil {
|
||||
s.Original = nil
|
||||
} else {
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
if err := s.Original.UnmarshalJSON(b); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) SetPreview(m *model.S3Object) {
|
||||
if m == nil {
|
||||
s.Preview = nil
|
||||
} else {
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
if err := s.Preview.UnmarshalJSON(b); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) SetText(m *model.S3Object) {
|
||||
if m == nil {
|
||||
s.Text = nil
|
||||
} else {
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
if err := s.Text.UnmarshalJSON(b); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) SetThumbnail(m *model.Thumbnail) {
|
||||
if m == nil {
|
||||
s.Thumbnail = nil
|
||||
} else {
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
if err := s.Thumbnail.UnmarshalJSON(b); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) SetStatus(status string) {
|
||||
s.Status = status
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) HasOriginal() bool {
|
||||
return s.Original != nil
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) HasPreview() bool {
|
||||
return s.Preview != nil
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) HasText() bool {
|
||||
return s.Text != nil
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) HasThumbnail() bool {
|
||||
return s.Thumbnail != nil
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) GetCreateTime() string {
|
||||
return s.CreateTime
|
||||
}
|
||||
|
||||
func (s *snapshotEntity) GetUpdateTime() *string {
|
||||
return s.UpdateTime
|
||||
}
|
||||
|
||||
type snapshotRepo struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func newSnapshotRepo() *snapshotRepo {
|
||||
return &snapshotRepo{
|
||||
db: infra.GetDb(),
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) find(id string) (*snapshotEntity, error) {
|
||||
var res snapshotEntity
|
||||
if db := repo.db.Where("id = ?", id).First(&res); db.Error != nil {
|
||||
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, errorpkg.NewSnapshotNotFoundError(db.Error)
|
||||
} else {
|
||||
return nil, errorpkg.NewInternalServerError(db.Error)
|
||||
}
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) Find(id string) (model.Snapshot, error) {
|
||||
res, err := repo.find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) Save(snapshot model.Snapshot) error {
|
||||
if db := repo.db.Save(snapshot); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) Update(id string, opts SnapshotUpdateOptions) error {
|
||||
snapshot, err := repo.find(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.Thumbnail != nil {
|
||||
snapshot.SetThumbnail(opts.Thumbnail)
|
||||
}
|
||||
if opts.Original != nil {
|
||||
snapshot.SetOriginal(opts.Original)
|
||||
}
|
||||
if opts.Preview != nil {
|
||||
snapshot.SetPreview(opts.Preview)
|
||||
}
|
||||
if opts.Text != nil {
|
||||
snapshot.SetText(opts.Text)
|
||||
}
|
||||
if opts.Status != "" {
|
||||
snapshot.SetStatus(opts.Status)
|
||||
}
|
||||
if db := repo.db.Save(&snapshot); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) MapWithFile(id string, fileID string) error {
|
||||
if db := repo.db.Exec("INSERT INTO snapshot_file (snapshot_id, file_id) VALUES (?, ?)", id, fileID); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) DeleteMappingsForFile(fileID string) error {
|
||||
if db := repo.db.Exec("DELETE FROM snapshot_file WHERE file_id = ?", fileID); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) findAllForFile(fileID string) ([]*snapshotEntity, error) {
|
||||
var res []*snapshotEntity
|
||||
db := repo.db.
|
||||
Raw("SELECT * FROM snapshot s LEFT JOIN snapshot_file sf ON s.id = sf.snapshot_id WHERE sf.file_id = ? ORDER BY s.version", fileID).
|
||||
Scan(&res)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) FindAllDangling() ([]model.Snapshot, error) {
|
||||
var snapshots []*snapshotEntity
|
||||
db := repo.db.Raw("SELECT * FROM snapshot s LEFT JOIN snapshot_file sf ON s.id = sf.snapshot_id WHERE sf.snapshot_id IS NULL").Scan(&snapshots)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
var res []model.Snapshot
|
||||
for _, s := range snapshots {
|
||||
res = append(res, s)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) DeleteAllDangling() error {
|
||||
if db := repo.db.Exec("DELETE FROM snapshot WHERE id IN (SELECT s.id FROM (SELECT * FROM snapshot) s LEFT JOIN snapshot_file sf ON s.id = sf.snapshot_id WHERE sf.snapshot_id IS NULL)"); db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *snapshotRepo) GetLatestVersionForFile(fileID string) (int64, error) {
|
||||
type Result struct {
|
||||
Result int64
|
||||
}
|
||||
var res Result
|
||||
if db := repo.db.
|
||||
Raw("SELECT coalesce(max(s.version), 0) + 1 result FROM snapshot s LEFT JOIN snapshot_file map ON s.id = map.snapshot_id WHERE map.file_id = ?", fileID).
|
||||
Scan(&res); db.Error != nil {
|
||||
return 0, db.Error
|
||||
}
|
||||
return res.Result, nil
|
||||
}
|
121
Downloads/Voltaserve/api/repo/user_repo.go
Normal file
121
Downloads/Voltaserve/api/repo/user_repo.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserRepo interface {
|
||||
Find(id string) (model.User, error)
|
||||
FindByEmail(email string) (model.User, error)
|
||||
FindAll() ([]model.User, error)
|
||||
}
|
||||
|
||||
func NewUserRepo() UserRepo {
|
||||
return newUserRepo()
|
||||
}
|
||||
|
||||
func NewUser() model.User {
|
||||
return &userEntity{}
|
||||
}
|
||||
|
||||
type userEntity struct {
|
||||
ID string `json:"id" gorm:"column:id"`
|
||||
FullName string `json:"fullName" gorm:"column:full_name"`
|
||||
Username string `json:"username" gorm:"column:username"`
|
||||
Email string `json:"email" gorm:"column:email"`
|
||||
Picture *string `json:"picture" gorm:"column:picture"`
|
||||
IsEmailConfirmed bool `json:"isEmailConfirmed" gorm:"column:is_email_confirmed"`
|
||||
PasswordHash string `json:"passwordHash" gorm:"column:password_hash"`
|
||||
RefreshTokenValue *string `json:"refreshTokenValue" gorm:"column:refresh_token_value"`
|
||||
RefreshTokenValidTo *int64 `json:"refreshTokenValidTo" gorm:"column:refresh_token_valid_to"`
|
||||
ResetPasswordToken *string `json:"resetPasswordToken" gorm:"column:reset_password_token"`
|
||||
EmailConfirmationToken *string `json:"emailConfirmationToken" gorm:"column:email_confirmation_token"`
|
||||
CreateTime string `json:"createTime" gorm:"column:create_time"`
|
||||
UpdateTime *string `json:"updateTime" gorm:"column:update_time"`
|
||||
}
|
||||
|
||||
func (userEntity) TableName() string {
|
||||
return "user"
|
||||
}
|
||||
|
||||
func (u userEntity) GetID() string {
|
||||
return u.ID
|
||||
}
|
||||
|
||||
func (u userEntity) GetFullName() string {
|
||||
return u.FullName
|
||||
}
|
||||
|
||||
func (u userEntity) GetUsername() string {
|
||||
return u.Username
|
||||
}
|
||||
|
||||
func (u userEntity) GetEmail() string {
|
||||
return u.Email
|
||||
}
|
||||
|
||||
func (u userEntity) GetPicture() *string {
|
||||
return u.Picture
|
||||
}
|
||||
|
||||
func (u userEntity) GetIsEmailConfirmed() bool {
|
||||
return u.IsEmailConfirmed
|
||||
}
|
||||
|
||||
func (u userEntity) GetCreateTime() string {
|
||||
return u.CreateTime
|
||||
}
|
||||
|
||||
func (u userEntity) GetUpdateTime() *string {
|
||||
return u.UpdateTime
|
||||
}
|
||||
|
||||
type userRepo struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func newUserRepo() *userRepo {
|
||||
return &userRepo{
|
||||
db: infra.GetDb(),
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *userRepo) Find(id string) (model.User, error) {
|
||||
var res = userEntity{}
|
||||
db := repo.db.Where("id = ?", id).First(&res)
|
||||
if db.Error != nil {
|
||||
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, errorpkg.NewUserNotFoundError(db.Error)
|
||||
} else {
|
||||
return nil, errorpkg.NewInternalServerError(db.Error)
|
||||
}
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (repo *userRepo) FindByEmail(email string) (model.User, error) {
|
||||
var res = userEntity{}
|
||||
db := repo.db.Where("email = ?", email).First(&res)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (repo *userRepo) FindAll() ([]model.User, error) {
|
||||
var entities []*userEntity
|
||||
db := repo.db.Raw(`select * from "user"`).Scan(&entities)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
var res []model.User
|
||||
for _, u := range entities {
|
||||
res = append(res, u)
|
||||
}
|
||||
return res, nil
|
||||
}
|
319
Downloads/Voltaserve/api/repo/workspace_repo.go
Normal file
319
Downloads/Voltaserve/api/repo/workspace_repo.go
Normal file
@@ -0,0 +1,319 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type WorkspaceInsertOptions struct {
|
||||
ID string
|
||||
Name string
|
||||
StorageCapacity int64
|
||||
Image *string
|
||||
OrganizationID string
|
||||
RootID string
|
||||
Bucket string
|
||||
}
|
||||
|
||||
type WorkspaceRepo interface {
|
||||
Insert(opts WorkspaceInsertOptions) (model.Workspace, error)
|
||||
Find(id string) (model.Workspace, error)
|
||||
UpdateName(id string, name string) (model.Workspace, error)
|
||||
UpdateStorageCapacity(id string, storageCapacity int64) (model.Workspace, error)
|
||||
UpdateRootID(id string, rootNodeID string) error
|
||||
Delete(id string) error
|
||||
GetIDs() ([]string, error)
|
||||
GetIDsByOrganization(orgID string) ([]string, error)
|
||||
GrantUserPermission(id string, userID string, permission string) error
|
||||
}
|
||||
|
||||
func NewWorkspaceRepo() WorkspaceRepo {
|
||||
return newWorkspaceRepo()
|
||||
}
|
||||
|
||||
func NewWorkspace() model.Workspace {
|
||||
return &workspaceEntity{}
|
||||
}
|
||||
|
||||
type workspaceEntity struct {
|
||||
ID string `json:"id," gorm:"column:id;size:36"`
|
||||
Name string `json:"name" gorm:"column:name;size:255"`
|
||||
StorageCapacity int64 `json:"storageCapacity" gorm:"column:storage_capacity"`
|
||||
RootID string `json:"rootId" gorm:"column:root_id;size:36"`
|
||||
OrganizationID string `json:"organizationId" gorm:"column:organization_id;size:36"`
|
||||
UserPermissions []*userPermissionValue `json:"userPermissions" gorm:"-"`
|
||||
GroupPermissions []*groupPermissionValue `json:"groupPermissions" gorm:"-"`
|
||||
Bucket string `json:"bucket" gorm:"column:bucket;size:255"`
|
||||
CreateTime string `json:"createTime" gorm:"column:create_time"`
|
||||
UpdateTime *string `json:"updateTime,omitempty" gorm:"column:update_time"`
|
||||
}
|
||||
|
||||
func (*workspaceEntity) TableName() string {
|
||||
return "workspace"
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) BeforeCreate(*gorm.DB) (err error) {
|
||||
w.CreateTime = time.Now().UTC().Format(time.RFC3339)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) BeforeSave(*gorm.DB) (err error) {
|
||||
timeNow := time.Now().UTC().Format(time.RFC3339)
|
||||
w.UpdateTime = &timeNow
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetID() string {
|
||||
return w.ID
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetName() string {
|
||||
return w.Name
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetStorageCapacity() int64 {
|
||||
return w.StorageCapacity
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetRootID() string {
|
||||
return w.RootID
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetOrganizationID() string {
|
||||
return w.OrganizationID
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetUserPermissions() []model.CoreUserPermission {
|
||||
var res []model.CoreUserPermission
|
||||
for _, p := range w.UserPermissions {
|
||||
res = append(res, p)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetGroupPermissions() []model.CoreGroupPermission {
|
||||
var res []model.CoreGroupPermission
|
||||
for _, p := range w.GroupPermissions {
|
||||
res = append(res, p)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetBucket() string {
|
||||
return w.Bucket
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetCreateTime() string {
|
||||
return w.CreateTime
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) GetUpdateTime() *string {
|
||||
return w.UpdateTime
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) SetName(name string) {
|
||||
w.Name = name
|
||||
}
|
||||
|
||||
func (w *workspaceEntity) SetUpdateTime(updateTime *string) {
|
||||
w.UpdateTime = updateTime
|
||||
}
|
||||
|
||||
type workspaceRepo struct {
|
||||
db *gorm.DB
|
||||
permissionRepo *permissionRepo
|
||||
}
|
||||
|
||||
func newWorkspaceRepo() *workspaceRepo {
|
||||
return &workspaceRepo{
|
||||
db: infra.GetDb(),
|
||||
permissionRepo: newPermissionRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) Insert(opts WorkspaceInsertOptions) (model.Workspace, error) {
|
||||
var id string
|
||||
if len(opts.ID) > 0 {
|
||||
id = opts.ID
|
||||
} else {
|
||||
id = helper.NewID()
|
||||
}
|
||||
workspace := workspaceEntity{
|
||||
ID: id,
|
||||
Name: opts.Name,
|
||||
StorageCapacity: opts.StorageCapacity,
|
||||
RootID: opts.RootID,
|
||||
OrganizationID: opts.OrganizationID,
|
||||
Bucket: opts.Bucket,
|
||||
}
|
||||
if db := repo.db.Save(&workspace); db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
res, err := repo.find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := repo.populateModelFields([]*workspaceEntity{res}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) find(id string) (*workspaceEntity, error) {
|
||||
var res = workspaceEntity{}
|
||||
db := repo.db.Where("id = ?", id).First(&res)
|
||||
if db.Error != nil {
|
||||
if errors.Is(db.Error, gorm.ErrRecordNotFound) {
|
||||
return nil, errorpkg.NewWorkspaceNotFoundError(db.Error)
|
||||
} else {
|
||||
return nil, errorpkg.NewInternalServerError(db.Error)
|
||||
}
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) Find(id string) (model.Workspace, error) {
|
||||
workspace, err := repo.find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := repo.populateModelFields([]*workspaceEntity{workspace}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return workspace, err
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) UpdateName(id string, name string) (model.Workspace, error) {
|
||||
workspace, err := repo.find(id)
|
||||
if err != nil {
|
||||
return &workspaceEntity{}, err
|
||||
}
|
||||
workspace.Name = name
|
||||
if db := repo.db.Save(&workspace); db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
res, err := repo.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) UpdateStorageCapacity(id string, storageCapacity int64) (model.Workspace, error) {
|
||||
workspace, err := repo.find(id)
|
||||
if err != nil {
|
||||
return &workspaceEntity{}, err
|
||||
}
|
||||
workspace.StorageCapacity = storageCapacity
|
||||
db := repo.db.Save(&workspace)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
res, err := repo.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) UpdateRootID(id string, rootNodeID string) error {
|
||||
db := repo.db.Exec("UPDATE workspace SET root_id = ? WHERE id = ?", rootNodeID, id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) Delete(id string) error {
|
||||
db := repo.db.Exec("DELETE FROM workspace WHERE id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
db = repo.db.Exec("DELETE FROM userpermission WHERE resource_id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
db = repo.db.Exec("DELETE FROM grouppermission WHERE resource_id = ?", id)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) GetIDs() ([]string, error) {
|
||||
type IDResult struct {
|
||||
Result string
|
||||
}
|
||||
var ids []IDResult
|
||||
db := repo.db.Raw("SELECT id result FROM workspace ORDER BY create_time DESC").Scan(&ids)
|
||||
if db.Error != nil {
|
||||
return []string{}, db.Error
|
||||
}
|
||||
res := []string{}
|
||||
for _, id := range ids {
|
||||
res = append(res, id.Result)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) GetIDsByOrganization(orgID string) ([]string, error) {
|
||||
type IDResult struct {
|
||||
Result string
|
||||
}
|
||||
var ids []IDResult
|
||||
db := repo.db.
|
||||
Raw("SELECT id result FROM workspace WHERE organization_id = ? ORDER BY create_time DESC", orgID).
|
||||
Scan(&ids)
|
||||
if db.Error != nil {
|
||||
return nil, db.Error
|
||||
}
|
||||
res := []string{}
|
||||
for _, id := range ids {
|
||||
res = append(res, id.Result)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) GrantUserPermission(id string, userID string, permission string) error {
|
||||
db := repo.db.Exec(
|
||||
"INSERT INTO userpermission (id, user_id, resource_id, permission) VALUES (?, ?, ?, ?) ON CONFLICT (user_id, resource_id) DO UPDATE SET permission = ?",
|
||||
helper.NewID(), userID, id, permission, permission)
|
||||
if db.Error != nil {
|
||||
return db.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (repo *workspaceRepo) populateModelFields(workspaces []*workspaceEntity) error {
|
||||
for _, w := range workspaces {
|
||||
w.UserPermissions = make([]*userPermissionValue, 0)
|
||||
userPermissions, err := repo.permissionRepo.GetUserPermissions(w.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range userPermissions {
|
||||
w.UserPermissions = append(w.UserPermissions, &userPermissionValue{
|
||||
UserID: p.UserID,
|
||||
Value: p.Permission,
|
||||
})
|
||||
}
|
||||
w.GroupPermissions = make([]*groupPermissionValue, 0)
|
||||
groupPermissions, err := repo.permissionRepo.GetGroupPermissions(w.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range groupPermissions {
|
||||
w.GroupPermissions = append(w.GroupPermissions, &groupPermissionValue{
|
||||
GroupID: p.GroupID,
|
||||
Value: p.Permission,
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
11
Downloads/Voltaserve/api/router/constants.go
Normal file
11
Downloads/Voltaserve/api/router/constants.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package router
|
||||
|
||||
const (
|
||||
FileDefaultPageSize = 100
|
||||
WorkspaceDefaultPageSize = 100
|
||||
OrganizationDefaultPageSize = 100
|
||||
InvitationDefaultPageSize = 100
|
||||
GroupDefaultPageSize = 100
|
||||
UserDefaultPageSize = 100
|
||||
OCRLanguageDefaultPageSize = 100
|
||||
)
|
927
Downloads/Voltaserve/api/router/file_router.go
Normal file
927
Downloads/Voltaserve/api/router/file_router.go
Normal file
@@ -0,0 +1,927 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"voltaserve/config"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/service"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
type FileRouter struct {
|
||||
fileSvc *service.FileService
|
||||
workspaceSvc *service.WorkspaceService
|
||||
config config.Config
|
||||
}
|
||||
|
||||
func NewFileRouter() *FileRouter {
|
||||
return &FileRouter{
|
||||
fileSvc: service.NewFileService(),
|
||||
workspaceSvc: service.NewWorkspaceService(),
|
||||
config: config.GetConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FileRouter) AppendRoutes(g fiber.Router) {
|
||||
g.Post("/", r.Upload)
|
||||
g.Post("/create_folder", r.CreateFolder)
|
||||
g.Get("/list", r.ListByPath)
|
||||
g.Get("/get", r.GetByPath)
|
||||
g.Post("/batch_delete", r.BatchDelete)
|
||||
g.Post("/batch_get", r.BatchGet)
|
||||
g.Get("/:id", r.GetByID)
|
||||
g.Patch("/:id", r.Patch)
|
||||
g.Delete("/:id", r.Delete)
|
||||
g.Get("/:id/list", r.List)
|
||||
g.Get("/:id/get_item_count", r.GetItemCount)
|
||||
g.Get("/:id/get_path", r.GetPath)
|
||||
g.Get("/:id/get_ids", r.GetIDs)
|
||||
g.Post("/:id/move", r.Move)
|
||||
g.Post("/:id/rename", r.Rename)
|
||||
g.Post("/:id/copy", r.Copy)
|
||||
g.Get("/:id/get_size", r.GetSize)
|
||||
g.Post("/grant_user_permission", r.GrantUserPermission)
|
||||
g.Post("/revoke_user_permission", r.RevokeUserPermission)
|
||||
g.Post("/grant_group_permission", r.GrantGroupPermission)
|
||||
g.Post("/revoke_group_permission", r.RevokeGroupPermission)
|
||||
g.Get("/:id/get_user_permissions", r.GetUserPermissions)
|
||||
g.Get("/:id/get_group_permissions", r.GetGroupPermissions)
|
||||
}
|
||||
|
||||
// Upload godoc
|
||||
//
|
||||
// @Summary Upload
|
||||
// @Description Upload
|
||||
// @Tags Files
|
||||
// @Id files_upload
|
||||
// @Accept x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param workspace_id query string true "Workspace ID"
|
||||
// @Param parent_id query string false "Parent ID"
|
||||
// @Param name query string false "Name"
|
||||
// @Success 200 {object} service.File
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files [post]
|
||||
func (r *FileRouter) Upload(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
workspaceID := c.Query("workspace_id")
|
||||
if workspaceID == "" {
|
||||
return errorpkg.NewMissingQueryParamError("workspace_id")
|
||||
}
|
||||
parentID := c.Query("parent_id")
|
||||
if parentID == "" {
|
||||
workspace, err := r.workspaceSvc.Find(workspaceID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentID = workspace.RootID
|
||||
}
|
||||
fh, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ok, err := r.workspaceSvc.HasEnoughSpaceForByteSize(workspaceID, fh.Size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return errorpkg.NewStorageLimitExceededError()
|
||||
}
|
||||
name := c.Query("name")
|
||||
if name == "" {
|
||||
name = fh.Filename
|
||||
}
|
||||
file, err := r.fileSvc.Create(service.FileCreateOptions{
|
||||
Name: name,
|
||||
Type: model.FileTypeFile,
|
||||
ParentID: &parentID,
|
||||
WorkspaceID: workspaceID,
|
||||
}, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.FromSlash(os.TempDir() + "/" + helper.NewID() + filepath.Ext(fh.Filename))
|
||||
if err := c.SaveFile(fh, path); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(name string) {
|
||||
if err := os.Remove(name); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}(path)
|
||||
file, err = r.fileSvc.Store(file.ID, path, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Status(http.StatusCreated).JSON(file)
|
||||
}
|
||||
|
||||
// Patch godoc
|
||||
//
|
||||
// @Summary Patch
|
||||
// @Description Patch
|
||||
// @Tags Files
|
||||
// @Id files_patch
|
||||
// @Accept x-www-form-urlencoded
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {object} service.File
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id} [patch]
|
||||
func (r *FileRouter) Patch(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
files, err := r.fileSvc.FindByID([]string{c.Params("id")}, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file := files[0]
|
||||
fh, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ok, err := r.workspaceSvc.HasEnoughSpaceForByteSize(file.WorkspaceID, fh.Size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return errorpkg.NewStorageLimitExceededError()
|
||||
}
|
||||
path := filepath.FromSlash(os.TempDir() + "/" + helper.NewID() + filepath.Ext(fh.Filename))
|
||||
if err := c.SaveFile(fh, path); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(name string) {
|
||||
if err := os.Remove(name); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}(path)
|
||||
file, err = r.fileSvc.Store(file.ID, path, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(file)
|
||||
}
|
||||
|
||||
// CreateFolder godoc
|
||||
//
|
||||
// @Summary Create
|
||||
// @Description Create
|
||||
// @Tags Files
|
||||
// @Id files_create_folder
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body service.FileCreateFolderOptions true "Body"
|
||||
// @Success 200 {object} service.File
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/create_folder [post]
|
||||
func (r *FileRouter) CreateFolder(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileCreateFolderOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
parentID := opts.ParentID
|
||||
if parentID == nil {
|
||||
workspace, err := r.workspaceSvc.Find(opts.WorkspaceID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentID = &workspace.RootID
|
||||
}
|
||||
res, err := r.fileSvc.Create(service.FileCreateOptions{
|
||||
Name: opts.Name,
|
||||
Type: model.FileTypeFolder,
|
||||
ParentID: parentID,
|
||||
WorkspaceID: opts.WorkspaceID,
|
||||
}, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Status(http.StatusCreated).JSON(res)
|
||||
}
|
||||
|
||||
// GetByID godoc
|
||||
//
|
||||
// @Summary Get by ID
|
||||
// @Description Get by ID
|
||||
// @Tags Files
|
||||
// @Id files_get_by_id
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {object} service.File
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id} [get]
|
||||
func (r *FileRouter) GetByID(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
res, err := r.fileSvc.FindByID([]string{c.Params("id")}, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return errorpkg.NewFileNotFoundError(nil)
|
||||
}
|
||||
return c.JSON(res[0])
|
||||
}
|
||||
|
||||
// GetByPath godoc
|
||||
//
|
||||
// @Summary Get by Path
|
||||
// @Description Get by Path
|
||||
// @Tags Files
|
||||
// @Id files_get_by_path
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {object} service.File
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/get [get]
|
||||
func (r *FileRouter) GetByPath(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
if c.Query("path") == "" {
|
||||
return errorpkg.NewMissingQueryParamError("path")
|
||||
}
|
||||
res, err := r.fileSvc.FindByPath(c.Query("path"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// ListByPath godoc
|
||||
//
|
||||
// @Summary List by Path
|
||||
// @Description List by Path
|
||||
// @Tags Files
|
||||
// @Id files_list_by_path
|
||||
// @Produce json
|
||||
// @Param path query string true "Path"
|
||||
// @Success 200 {array} service.File
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/list [get]
|
||||
func (r *FileRouter) ListByPath(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
if c.Query("path") == "" {
|
||||
return errorpkg.NewMissingQueryParamError("path")
|
||||
}
|
||||
res, err := r.fileSvc.ListByPath(c.Query("path"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// List godoc
|
||||
//
|
||||
// @Summary List
|
||||
// @Description List
|
||||
// @Tags Files
|
||||
// @Id files_list
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param type query string false "Type"
|
||||
// @Param page query string false "Page"
|
||||
// @Param size query string false "Size"
|
||||
// @Param sort_by query string false "Sort By"
|
||||
// @Param sort_order query string false "Sort Order"
|
||||
// @Param query query string false "Query"
|
||||
// @Success 200 {object} service.FileList
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/list [get]
|
||||
func (r *FileRouter) List(c *fiber.Ctx) error {
|
||||
var err error
|
||||
var res *service.FileList
|
||||
id := c.Params("id")
|
||||
userID := GetUserID(c)
|
||||
var page int64
|
||||
if c.Query("page") == "" {
|
||||
page = 1
|
||||
} else {
|
||||
page, err = strconv.ParseInt(c.Query("page"), 10, 32)
|
||||
if err != nil {
|
||||
page = 1
|
||||
}
|
||||
}
|
||||
var size int64
|
||||
if c.Query("size") == "" {
|
||||
size = FileDefaultPageSize
|
||||
} else {
|
||||
size, err = strconv.ParseInt(c.Query("size"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sortBy := c.Query("sort_by")
|
||||
if !IsValidSortBy(sortBy) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_by")
|
||||
}
|
||||
sortOrder := c.Query("sort_order")
|
||||
if !IsValidSortOrder(sortOrder) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_order")
|
||||
}
|
||||
fileType := c.Query("type")
|
||||
if fileType != model.FileTypeFile && fileType != model.FileTypeFolder && fileType != "" {
|
||||
return errorpkg.NewInvalidQueryParamError("type")
|
||||
}
|
||||
query := c.Query("query")
|
||||
opts := service.FileListOptions{
|
||||
Page: uint(page),
|
||||
Size: uint(size),
|
||||
SortBy: sortBy,
|
||||
SortOrder: sortOrder,
|
||||
}
|
||||
if query != "" {
|
||||
bytes, err := base64.StdEncoding.DecodeString(query + strings.Repeat("=", (4-len(query)%4)%4))
|
||||
if err != nil {
|
||||
return errorpkg.NewInvalidQueryParamError("query")
|
||||
}
|
||||
if err := json.Unmarshal(bytes, &opts.Query); err != nil {
|
||||
return errorpkg.NewInvalidQueryParamError("query")
|
||||
}
|
||||
res, err = r.fileSvc.Search(id, opts, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if fileType != "" {
|
||||
opts.Query = &service.FileQuery{
|
||||
Type: &fileType,
|
||||
}
|
||||
}
|
||||
res, err = r.fileSvc.List(id, opts, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// GetIDs godoc
|
||||
//
|
||||
// @Summary Get IDs
|
||||
// @Description Get IDs
|
||||
// @Tags Files
|
||||
// @Id files_get_ids
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {array} string
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/get_ids [get]
|
||||
func (r *FileRouter) GetIDs(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
res, err := r.fileSvc.GetIDs(c.Params("id"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// GetPath godoc
|
||||
//
|
||||
// @Summary Get path
|
||||
// @Description Get path
|
||||
// @Tags Files
|
||||
// @Id files_get_path
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {array} service.File
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/get_path [get]
|
||||
func (r *FileRouter) GetPath(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
res, err := r.fileSvc.GetPath(c.Params("id"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// Copy godoc
|
||||
//
|
||||
// @Summary Copy
|
||||
// @Description Copy
|
||||
// @Tags Files
|
||||
// @Id files_copy
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.FileCopyOptions true "Body"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/copy [post]
|
||||
func (r *FileRouter) Copy(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileCopyOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
res, err := r.fileSvc.Copy(c.Params("id"), opts.IDs, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// Move godoc
|
||||
//
|
||||
// @Summary Move
|
||||
// @Description Move
|
||||
// @Tags Files
|
||||
// @Id files_move
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.FileMoveOptions true "Body"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/move [post]
|
||||
func (r *FileRouter) Move(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileMoveOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if _, err := r.fileSvc.Move(c.Params("id"), opts.IDs, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Rename godoc
|
||||
//
|
||||
// @Summary Rename
|
||||
// @Description Rename
|
||||
// @Tags Files
|
||||
// @Id files_rename
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.FileRenameOptions true "Body"
|
||||
// @Success 200 {object} service.File
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/rename [post]
|
||||
func (r *FileRouter) Rename(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileRenameOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
res, err := r.fileSvc.Rename(c.Params("id"), opts.Name, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// Delete godoc
|
||||
//
|
||||
// @Summary Delete
|
||||
// @Description Delete
|
||||
// @Tags Files
|
||||
// @Id files_delete
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id} [delete]
|
||||
func (r *FileRouter) Delete(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
_, err := r.fileSvc.Delete([]string{c.Params("id")}, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// BatchGet godoc
|
||||
//
|
||||
// @Summary Batch get
|
||||
// @Description Batch get
|
||||
// @Tags Files
|
||||
// @Id files_batch_get
|
||||
// @Produce json
|
||||
// @Param body body service.FileBatchGetOptions true "Body"
|
||||
// @Success 200 {array} service.File
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/batch_get [post]
|
||||
func (r *FileRouter) BatchGet(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileBatchGetOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
res, err := r.fileSvc.FindByID(opts.IDs, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// BatchDelete godoc
|
||||
//
|
||||
// @Summary Batch delete
|
||||
// @Description Batch delete
|
||||
// @Tags Files
|
||||
// @Id files_batch_delete
|
||||
// @Produce json
|
||||
// @Param body body service.FileBatchDeleteOptions true "Body"
|
||||
// @Success 200 {array} string
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/batch_delete [post]
|
||||
func (r *FileRouter) BatchDelete(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileBatchDeleteOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
res, err := r.fileSvc.Delete(opts.IDs, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// GetSize godoc
|
||||
//
|
||||
// @Summary Get size
|
||||
// @Description Get size
|
||||
// @Tags Files
|
||||
// @Id files_get_size
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {object} int
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/get_size [get]
|
||||
func (r *FileRouter) GetSize(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
id := c.Params("id")
|
||||
res, err := r.fileSvc.GetSize(id, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// GetItemCount godoc
|
||||
//
|
||||
// @Summary Get children count
|
||||
// @Description Get children count
|
||||
// @Tags Files
|
||||
// @Id files_get_children_count
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {object} int
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/get_item_count [get]
|
||||
func (r *FileRouter) GetItemCount(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
res, err := r.fileSvc.GetItemCount(c.Params("id"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// GrantUserPermission godoc
|
||||
//
|
||||
// @Summary Grant user permission
|
||||
// @Description Grant user permission
|
||||
// @Tags Files
|
||||
// @Id files_grant_user_permission
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.FileGrantUserPermissionOptions true "Body"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/grant_user_permission [post]
|
||||
func (r *FileRouter) GrantUserPermission(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileGrantUserPermissionOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if err := r.fileSvc.GrantUserPermission(opts.IDs, opts.UserID, opts.Permission, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// RevokeUserPermission godoc
|
||||
//
|
||||
// @Summary Revoke user permission
|
||||
// @Description Revoke user permission
|
||||
// @Tags Files
|
||||
// @Id files_revoke_user_permission
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.FileRevokeUserPermissionOptions true "Body"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/revoke_user_permission [post]
|
||||
func (r *FileRouter) RevokeUserPermission(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileRevokeUserPermissionOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if err := r.fileSvc.RevokeUserPermission(opts.IDs, opts.UserID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// GrantGroupPermission godoc
|
||||
//
|
||||
// @Summary Grant group permission
|
||||
// @Description Grant group permission
|
||||
// @Tags Files
|
||||
// @Id files_grant_group_permission
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.FileGrantGroupPermissionOptions true "Body"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/grant_group_permission [post]
|
||||
func (r *FileRouter) GrantGroupPermission(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileGrantGroupPermissionOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if err := r.fileSvc.GrantGroupPermission(opts.IDs, opts.GroupID, opts.Permission, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// RevokeGroupPermission godoc
|
||||
//
|
||||
// @Summary Revoke group permission
|
||||
// @Description Revoke group permission
|
||||
// @Tags Files
|
||||
// @Id files_revoke_group_permission
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.FileRevokeGroupPermissionOptions true "Body"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/revoke_group_permission [post]
|
||||
func (r *FileRouter) RevokeGroupPermission(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.FileRevokeGroupPermissionOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if err := r.fileSvc.RevokeGroupPermission(opts.IDs, opts.GroupID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// GetUserPermissions godoc
|
||||
//
|
||||
// @Summary Get user permissions
|
||||
// @Description Get user permissions
|
||||
// @Tags Files
|
||||
// @Id files_get_user_permissions
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {array} service.UserPermission
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/get_user_permissions [get]
|
||||
func (r *FileRouter) GetUserPermissions(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
res, err := r.fileSvc.GetUserPermissions(c.Params("id"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// GetGroupPermissions godoc
|
||||
//
|
||||
// @Summary Get group permissions
|
||||
// @Description Get group permissions
|
||||
// @Tags Files
|
||||
// @Id files_get_group_permissions
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {array} service.GroupPermission
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/get_group_permissions [get]
|
||||
func (r *FileRouter) GetGroupPermissions(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
res, err := r.fileSvc.GetGroupPermissions(c.Params("id"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
type FileDownloadRouter struct {
|
||||
fileSvc *service.FileService
|
||||
accessTokenCookieName string
|
||||
}
|
||||
|
||||
func NewFileDownloadRouter() *FileDownloadRouter {
|
||||
return &FileDownloadRouter{
|
||||
fileSvc: service.NewFileService(),
|
||||
accessTokenCookieName: "voltaserve_access_token",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *FileDownloadRouter) AppendNonJWTRoutes(g fiber.Router) {
|
||||
g.Get("/:id/original:ext", r.DownloadOriginal)
|
||||
g.Get("/:id/preview:ext", r.DownloadPreview)
|
||||
}
|
||||
|
||||
// DownloadOriginal godoc
|
||||
//
|
||||
// @Summary Download original
|
||||
// @Description Download original
|
||||
// @Tags Files
|
||||
// @Id files_download_original
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param access_token query string true "Access Token"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/original{ext} [get]
|
||||
func (r *FileDownloadRouter) DownloadOriginal(c *fiber.Ctx) error {
|
||||
accessToken := c.Cookies(r.accessTokenCookieName)
|
||||
if accessToken == "" {
|
||||
accessToken = c.Query("access_token")
|
||||
if accessToken == "" {
|
||||
return errorpkg.NewFileNotFoundError(nil)
|
||||
}
|
||||
}
|
||||
userID, err := r.getUserID(accessToken)
|
||||
if err != nil {
|
||||
return c.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
buf, file, snapshot, err := r.fileSvc.DownloadOriginalBuffer(c.Params("id"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if filepath.Ext(snapshot.GetOriginal().Key) != c.Params("ext") {
|
||||
return errorpkg.NewS3ObjectNotFoundError(nil)
|
||||
}
|
||||
bytes := buf.Bytes()
|
||||
c.Set("Content-Type", infra.DetectMimeFromBytes(bytes))
|
||||
c.Set("Content-Disposition", fmt.Sprintf("filename=\"%s\"", file.GetName()))
|
||||
return c.Send(bytes)
|
||||
}
|
||||
|
||||
// DownloadPreview godoc
|
||||
//
|
||||
// @Summary Download preview
|
||||
// @Description Download preview
|
||||
// @Tags Files
|
||||
// @Id files_download_preview
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param access_token query string true "Access Token"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/{id}/preview{ext} [get]
|
||||
func (r *FileDownloadRouter) DownloadPreview(c *fiber.Ctx) error {
|
||||
accessToken := c.Cookies(r.accessTokenCookieName)
|
||||
if accessToken == "" {
|
||||
accessToken = c.Query("access_token")
|
||||
if accessToken == "" {
|
||||
return errorpkg.NewFileNotFoundError(nil)
|
||||
}
|
||||
}
|
||||
userID, err := r.getUserID(accessToken)
|
||||
if err != nil {
|
||||
return c.SendStatus(http.StatusNotFound)
|
||||
}
|
||||
buf, file, snapshot, err := r.fileSvc.DownloadPreviewBuffer(c.Params("id"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if filepath.Ext(snapshot.GetPreview().Key) != c.Params("ext") {
|
||||
return errorpkg.NewS3ObjectNotFoundError(nil)
|
||||
}
|
||||
bytes := buf.Bytes()
|
||||
c.Set("Content-Type", infra.DetectMimeFromBytes(bytes))
|
||||
c.Set("Content-Disposition", fmt.Sprintf("filename=\"%s\"", file.GetName()))
|
||||
return c.Send(bytes)
|
||||
}
|
||||
|
||||
func (r *FileDownloadRouter) getUserID(accessToken string) (string, error) {
|
||||
token, err := jwt.Parse(accessToken, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return []byte(config.GetConfig().Security.JWTSigningKey), nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
return claims["sub"].(string), nil
|
||||
} else {
|
||||
return "", errors.New("cannot find sub claim")
|
||||
}
|
||||
}
|
||||
|
||||
type ConversionWebhookRouter struct {
|
||||
fileSvc *service.FileService
|
||||
}
|
||||
|
||||
func NewConversionWebhookRouter() *ConversionWebhookRouter {
|
||||
return &ConversionWebhookRouter{
|
||||
fileSvc: service.NewFileService(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ConversionWebhookRouter) AppendInternalRoutes(g fiber.Router) {
|
||||
g.Post("/conversion_webhook/update_snapshot", r.UpdateSnapshot)
|
||||
}
|
||||
|
||||
// UpdateSnapshot godoc
|
||||
//
|
||||
// @Summary Update snapshot
|
||||
// @Description Update snapshot
|
||||
// @Tags Files
|
||||
// @Id files_conversion_webhook_update_snapshot
|
||||
// @Produce json
|
||||
// @Param body body service.SnapshotUpdateOptions true "Body"
|
||||
// @Success 201
|
||||
// @Failure 401 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /files/conversion_webhook/update_snapshot [post]
|
||||
func (r *ConversionWebhookRouter) UpdateSnapshot(c *fiber.Ctx) error {
|
||||
apiKey := c.Query("api_key")
|
||||
if apiKey == "" {
|
||||
return errorpkg.NewMissingQueryParamError("api_key")
|
||||
}
|
||||
opts := new(service.SnapshotUpdateOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if err := r.fileSvc.UpdateSnapshot(*opts, apiKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(204)
|
||||
}
|
249
Downloads/Voltaserve/api/router/group_router.go
Normal file
249
Downloads/Voltaserve/api/router/group_router.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/service"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type GroupRouter struct {
|
||||
groupSvc *service.GroupService
|
||||
}
|
||||
|
||||
func NewGroupRouter() *GroupRouter {
|
||||
return &GroupRouter{
|
||||
groupSvc: service.NewGroupService(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GroupRouter) AppendRoutes(g fiber.Router) {
|
||||
g.Get("/", r.List)
|
||||
g.Post("/", r.Create)
|
||||
g.Get("/:id", r.GetByID)
|
||||
g.Delete("/:id", r.Delete)
|
||||
g.Post("/:id/update_name", r.UpdateName)
|
||||
g.Post("/:id/add_member", r.AddMember)
|
||||
g.Post("/:id/remove_member", r.RemoveMember)
|
||||
}
|
||||
|
||||
// Create godoc
|
||||
//
|
||||
// @Summary Create
|
||||
// @Description Create
|
||||
// @Tags Groups
|
||||
// @Id groups_create
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body service.GroupCreateOptions true "Body"
|
||||
// @Success 200 {object} service.Group
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /groups [post]
|
||||
func (r *GroupRouter) Create(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
req := new(service.GroupCreateOptions)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(req); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
res, err := r.groupSvc.Create(*req, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Status(http.StatusCreated).JSON(res)
|
||||
}
|
||||
|
||||
// GetByID godoc
|
||||
//
|
||||
// @Summary Get by ID
|
||||
// @Description Get by ID
|
||||
// @Tags Groups
|
||||
// @Id groups_get_by_id
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {object} service.Group
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /groups/{id} [get]
|
||||
func (r *GroupRouter) GetByID(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
res, err := r.groupSvc.Find(c.Params("id"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// List godoc
|
||||
//
|
||||
// @Summary List
|
||||
// @Description List
|
||||
// @Tags Groups
|
||||
// @Id groups_list
|
||||
// @Produce json
|
||||
// @Param query query string false "Query"
|
||||
// @Param organization_id query string false "Organization ID"
|
||||
// @Param page query string false "Page"
|
||||
// @Param size query string false "Size"
|
||||
// @Param sort_by query string false "Sort By"
|
||||
// @Param sort_order query string false "Sort Order"
|
||||
// @Success 200 {object} service.GroupList
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /groups [get]
|
||||
func (r *GroupRouter) List(c *fiber.Ctx) error {
|
||||
var err error
|
||||
var page int64
|
||||
if c.Query("page") == "" {
|
||||
page = 1
|
||||
} else {
|
||||
page, err = strconv.ParseInt(c.Query("page"), 10, 32)
|
||||
if err != nil {
|
||||
page = 1
|
||||
}
|
||||
}
|
||||
var size int64
|
||||
if c.Query("size") == "" {
|
||||
size = GroupDefaultPageSize
|
||||
} else {
|
||||
size, err = strconv.ParseInt(c.Query("size"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sortBy := c.Query("sort_by")
|
||||
if !IsValidSortBy(sortBy) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_by")
|
||||
}
|
||||
sortOrder := c.Query("sort_order")
|
||||
if !IsValidSortOrder(sortOrder) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_order")
|
||||
}
|
||||
res, err := r.groupSvc.List(service.GroupListOptions{
|
||||
Query: c.Query("query"),
|
||||
OrganizationID: c.Query("organization_id"),
|
||||
Page: uint(page),
|
||||
Size: uint(size),
|
||||
SortBy: sortBy,
|
||||
SortOrder: sortOrder,
|
||||
}, GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// UpdateName godoc
|
||||
//
|
||||
// @Summary Update name
|
||||
// @Description Update name
|
||||
// @Tags Groups
|
||||
// @Id groups_update_name
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.GroupUpdateNameOptions true "Body"
|
||||
// @Success 200 {object} service.Group
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /groups/{id}/update_name [post]
|
||||
func (r *GroupRouter) UpdateName(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
req := new(service.GroupUpdateNameOptions)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(req); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
res, err := r.groupSvc.UpdateName(c.Params("id"), req.Name, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// Delete godoc
|
||||
//
|
||||
// @Summary Delete
|
||||
// @Description Delete
|
||||
// @Tags Groups
|
||||
// @Id groups_delete
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /groups/{id} [delete]
|
||||
func (r *GroupRouter) Delete(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
if err := r.groupSvc.Delete(c.Params("id"), userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// AddMember godoc
|
||||
//
|
||||
// @Summary Add member
|
||||
// @Description Add member
|
||||
// @Tags Groups
|
||||
// @Id groups_add_member
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /groups/{id}/add_member [post]
|
||||
func (r *GroupRouter) AddMember(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
req := new(service.GroupAddMemberOptions)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(req); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if err := r.groupSvc.AddMember(c.Params("id"), req.UserID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// RemoveMember godoc
|
||||
//
|
||||
// @Summary Remove member
|
||||
// @Description Remove member
|
||||
// @Tags Groups
|
||||
// @Id groups_remove_member
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.GroupRemoveMemberOptions true "Body"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /groups/{id}/remove_member [post]
|
||||
func (r *GroupRouter) RemoveMember(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
req := new(service.GroupRemoveMemberOptions)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(req); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if err := r.groupSvc.RemoveMember(c.Params("id"), req.UserID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
257
Downloads/Voltaserve/api/router/invitation_router.go
Normal file
257
Downloads/Voltaserve/api/router/invitation_router.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/service"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type InvitationRouter struct {
|
||||
invitationSvc *service.InvitationService
|
||||
}
|
||||
|
||||
func NewInvitationRouter() *InvitationRouter {
|
||||
return &InvitationRouter{
|
||||
invitationSvc: service.NewInvitationService(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InvitationRouter) AppendRoutes(g fiber.Router) {
|
||||
g.Post("/", r.Create)
|
||||
g.Get("/get_incoming", r.GetIncoming)
|
||||
g.Get("/get_outgoing", r.GetOutgoing)
|
||||
g.Post("/:id/accept", r.Accept)
|
||||
g.Post("/:id/resend", r.Resend)
|
||||
g.Post("/:id/decline", r.Decline)
|
||||
g.Delete("/:id", r.Delete)
|
||||
}
|
||||
|
||||
// Create godoc
|
||||
//
|
||||
// @Summary Create
|
||||
// @Description Create
|
||||
// @Tags Invitations
|
||||
// @Id invitations_create
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.InvitationCreateOptions true "Body"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /invitations [post]
|
||||
func (r *InvitationRouter) Create(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
req := new(service.InvitationCreateOptions)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(req); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if err := r.invitationSvc.Create(*req, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// GetIncoming godoc
|
||||
//
|
||||
// @Summary Get incoming
|
||||
// @Description Get incoming
|
||||
// @Tags Invitations
|
||||
// @Id invitation_get_incoming
|
||||
// @Produce json
|
||||
// @Param page query string false "Page"
|
||||
// @Param size query string false "Size"
|
||||
// @Param sort_by query string false "Sort By"
|
||||
// @Param sort_order query string false "Sort Order"
|
||||
// @Success 200 {object} service.InvitationList
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /invitations/get_incoming [get]
|
||||
func (r *InvitationRouter) GetIncoming(c *fiber.Ctx) error {
|
||||
var err error
|
||||
var page int64
|
||||
if c.Query("page") == "" {
|
||||
page = 1
|
||||
} else {
|
||||
page, err = strconv.ParseInt(c.Query("page"), 10, 32)
|
||||
if err != nil {
|
||||
page = 1
|
||||
}
|
||||
}
|
||||
var size int64
|
||||
if c.Query("size") == "" {
|
||||
size = InvitationDefaultPageSize
|
||||
} else {
|
||||
size, err = strconv.ParseInt(c.Query("size"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sortBy := c.Query("sort_by")
|
||||
if !IsValidSortBy(sortBy) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_by")
|
||||
}
|
||||
sortOrder := c.Query("sort_order")
|
||||
if !IsValidSortOrder(sortOrder) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_order")
|
||||
}
|
||||
res, err := r.invitationSvc.GetIncoming(service.InvitationListOptions{
|
||||
Page: uint(page),
|
||||
Size: uint(size),
|
||||
SortBy: sortBy,
|
||||
SortOrder: sortOrder,
|
||||
}, GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// GetOutgoing godoc
|
||||
//
|
||||
// @Summary Get outgoing
|
||||
// @Description Get outgoing
|
||||
// @Tags Invitations
|
||||
// @Id invitation_get_outgoing
|
||||
// @Produce json
|
||||
// @Param organization_id query string true "Organization ID"
|
||||
// @Param page query string false "Page"
|
||||
// @Param size query string false "Size"
|
||||
// @Param sort_by query string false "Sort By"
|
||||
// @Param sort_order query string false "Sort Order"
|
||||
// @Success 200 {object} service.InvitationList
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /invitations/get_outgoing [get]
|
||||
func (r *InvitationRouter) GetOutgoing(c *fiber.Ctx) error {
|
||||
orgID := c.Query("organization_id")
|
||||
if orgID == "" {
|
||||
return errorpkg.NewMissingQueryParamError("org")
|
||||
}
|
||||
var err error
|
||||
var page int64
|
||||
if c.Query("page") == "" {
|
||||
page = 1
|
||||
} else {
|
||||
page, err = strconv.ParseInt(c.Query("page"), 10, 32)
|
||||
if err != nil {
|
||||
page = 1
|
||||
}
|
||||
}
|
||||
var size int64
|
||||
if c.Query("size") == "" {
|
||||
size = InvitationDefaultPageSize
|
||||
} else {
|
||||
size, err = strconv.ParseInt(c.Query("size"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sortBy := c.Query("sort_by")
|
||||
if !IsValidSortBy(sortBy) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_by")
|
||||
}
|
||||
sortOrder := c.Query("sort_order")
|
||||
if !IsValidSortOrder(sortOrder) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_order")
|
||||
}
|
||||
res, err := r.invitationSvc.GetOutgoing(orgID, service.InvitationListOptions{
|
||||
Page: uint(page),
|
||||
Size: uint(size),
|
||||
SortBy: sortBy,
|
||||
SortOrder: sortOrder,
|
||||
}, GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// Delete godoc
|
||||
//
|
||||
// @Summary Delete
|
||||
// @Description Delete
|
||||
// @Tags Invitations
|
||||
// @Id invitations_delete
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /invitations/{id} [delete]
|
||||
func (r *InvitationRouter) Delete(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
if err := r.invitationSvc.Delete(c.Params("id"), userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Resend godoc
|
||||
//
|
||||
// @Summary Resend
|
||||
// @Description Resend
|
||||
// @Tags Invitations
|
||||
// @Id invitations_resend
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /invitations/{id}/resend [post]
|
||||
func (r *InvitationRouter) Resend(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
if err := r.invitationSvc.Resend(c.Params("id"), userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Accept godoc
|
||||
//
|
||||
// @Summary Accept
|
||||
// @Description Accept
|
||||
// @Tags Invitations
|
||||
// @Id invitation_accept
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /invitations/{id}/accept [post]
|
||||
func (r *InvitationRouter) Accept(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
if err := r.invitationSvc.Accept(c.Params("id"), userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// Decline godoc
|
||||
//
|
||||
// @Summary Delete
|
||||
// @Description Delete
|
||||
// @Tags Invitations
|
||||
// @Id invitations_decline
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /invitations/{id}/decline [post]
|
||||
func (r *InvitationRouter) Decline(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
if err := r.invitationSvc.Decline(c.Params("id"), userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
40
Downloads/Voltaserve/api/router/notification_router.go
Normal file
40
Downloads/Voltaserve/api/router/notification_router.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"voltaserve/service"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type NotificationRouter struct {
|
||||
notificationSvc *service.NotificationService
|
||||
}
|
||||
|
||||
func NewNotificationRouter() *NotificationRouter {
|
||||
return &NotificationRouter{
|
||||
notificationSvc: service.NewNotificationService(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *NotificationRouter) AppendRoutes(g fiber.Router) {
|
||||
g.Get("/", r.GetAll)
|
||||
}
|
||||
|
||||
// GetAll godoc
|
||||
//
|
||||
// @Summary Get notifications
|
||||
// @Description Get notifications
|
||||
// @Tags Notifications
|
||||
// @Id notification_get_all
|
||||
// @Produce json
|
||||
// @Success 200 {array} service.Notification
|
||||
// @Failure 500
|
||||
// @Router /notifications [get]
|
||||
func (r *NotificationRouter) GetAll(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
res, err := r.notificationSvc.GetAll(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
243
Downloads/Voltaserve/api/router/organization_router.go
Normal file
243
Downloads/Voltaserve/api/router/organization_router.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/service"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type OrganizationRouter struct {
|
||||
orgSvc *service.OrganizationService
|
||||
}
|
||||
|
||||
func NewOrganizationRouter() *OrganizationRouter {
|
||||
return &OrganizationRouter{
|
||||
orgSvc: service.NewOrganizationService(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *OrganizationRouter) AppendRoutes(g fiber.Router) {
|
||||
g.Get("/", r.List)
|
||||
g.Post("/", r.Create)
|
||||
g.Get("/:id", r.GetByID)
|
||||
g.Delete("/:id", r.Delete)
|
||||
g.Post("/:id/update_name", r.UpdateName)
|
||||
g.Post("/:id/leave", r.Leave)
|
||||
g.Post("/:id/remove_member", r.RemoveMember)
|
||||
}
|
||||
|
||||
// Create godoc
|
||||
//
|
||||
// @Summary Create
|
||||
// @Description Create
|
||||
// @Tags Organizations
|
||||
// @Id organizations_create
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body service.OrganizationCreateOptions true "Body"
|
||||
// @Success 200 {object} service.Organization
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /organizations [post]
|
||||
func (r *OrganizationRouter) Create(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
req := new(service.OrganizationCreateOptions)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(req); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
res, err := r.orgSvc.Create(service.OrganizationCreateOptions{
|
||||
Name: req.Name,
|
||||
Image: req.Image,
|
||||
}, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Status(http.StatusCreated).JSON(res)
|
||||
}
|
||||
|
||||
// GetByID godoc
|
||||
//
|
||||
// @Summary Get by ID
|
||||
// @Description Get by ID
|
||||
// @Tags Organizations
|
||||
// @Id organizations_get_by_id
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {object} service.Organization
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /organizations/{id} [get]
|
||||
func (r *OrganizationRouter) GetByID(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
res, err := r.orgSvc.Find(c.Params("id"), userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// Delete godoc
|
||||
//
|
||||
// @Summary Delete
|
||||
// @Description Delete
|
||||
// @Tags Organizations
|
||||
// @Id organizations_delete
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /organizations/{id} [delete]
|
||||
func (r *OrganizationRouter) Delete(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
if err := r.orgSvc.Delete(c.Params("id"), userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// UpdateName godoc
|
||||
//
|
||||
// @Summary Update name
|
||||
// @Description Update name
|
||||
// @Tags Organizations
|
||||
// @Id organizations_update_name
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.OrganizationUpdateNameOptions true "Body"
|
||||
// @Success 200 {object} service.Organization
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /organizations/{id}/update_name [post]
|
||||
func (r *OrganizationRouter) UpdateName(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
req := new(service.OrganizationUpdateNameOptions)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(req); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
res, err := r.orgSvc.UpdateName(c.Params("id"), req.Name, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// List godoc
|
||||
//
|
||||
// @Summary List
|
||||
// @Description List
|
||||
// @Tags Organizations
|
||||
// @Id organizations_list
|
||||
// @Produce json
|
||||
// @Param query query string false "Query"
|
||||
// @Param page query string false "Page"
|
||||
// @Param size query string false "Size"
|
||||
// @Param sort_by query string false "Sort By"
|
||||
// @Param sort_order query string false "Sort Order"
|
||||
// @Success 200 {object} service.WorkspaceList
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /organizations [get]
|
||||
func (r *OrganizationRouter) List(c *fiber.Ctx) error {
|
||||
var err error
|
||||
var page int64
|
||||
if c.Query("page") == "" {
|
||||
page = 1
|
||||
} else {
|
||||
page, err = strconv.ParseInt(c.Query("page"), 10, 32)
|
||||
if err != nil {
|
||||
page = 1
|
||||
}
|
||||
}
|
||||
var size int64
|
||||
if c.Query("size") == "" {
|
||||
size = OrganizationDefaultPageSize
|
||||
} else {
|
||||
size, err = strconv.ParseInt(c.Query("size"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sortBy := c.Query("sort_by")
|
||||
if !IsValidSortBy(sortBy) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_by")
|
||||
}
|
||||
sortOrder := c.Query("sort_order")
|
||||
if !IsValidSortOrder(sortOrder) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_order")
|
||||
}
|
||||
res, err := r.orgSvc.List(service.OrganizationListOptions{
|
||||
Query: c.Query("query"),
|
||||
Page: uint(page),
|
||||
Size: uint(size),
|
||||
SortBy: sortBy,
|
||||
SortOrder: sortOrder,
|
||||
}, GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// Leave godoc
|
||||
//
|
||||
// @Summary Leave
|
||||
// @Description Leave
|
||||
// @Tags Organizations
|
||||
// @Id organizations_leave\
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /organizations/{id}/leave [post]
|
||||
func (r *OrganizationRouter) Leave(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
if err := r.orgSvc.RemoveMember(c.Params("id"), userID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// RemoveMember godoc
|
||||
//
|
||||
// @Summary Remove member
|
||||
// @Description Remove member
|
||||
// @Tags Organizations
|
||||
// @Id organizations_remove_member
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.OrganizationRemoveMemberOptions true "Body"
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /organizations/{id}/remove_member [post]
|
||||
func (r *OrganizationRouter) RemoveMember(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
req := new(service.OrganizationRemoveMemberOptions)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(req); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
if err := r.orgSvc.RemoveMember(c.Params("id"), req.UserID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
88
Downloads/Voltaserve/api/router/storage_router.go
Normal file
88
Downloads/Voltaserve/api/router/storage_router.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/service"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type StorageRouter struct {
|
||||
storageSvc *service.StorageService
|
||||
}
|
||||
|
||||
func NewStorageRouter() *StorageRouter {
|
||||
return &StorageRouter{
|
||||
storageSvc: service.NewStorageService(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *StorageRouter) AppendRoutes(g fiber.Router) {
|
||||
g.Get("/get_account_usage", r.GetAccountUsage)
|
||||
g.Get("/get_workspace_usage", r.GetWorkspaceUsage)
|
||||
g.Get("/get_file_usage", r.GetFileUsage)
|
||||
}
|
||||
|
||||
// GetAccountUsage godoc
|
||||
//
|
||||
// @Summary Get account usage
|
||||
// @Description Get account usage
|
||||
// @Tags Storage
|
||||
// @Id storage_get_account_usage
|
||||
// @Produce json
|
||||
// @Success 200 {object} service.StorageUsage
|
||||
// @Failure 500
|
||||
// @Router /storage/get_account_usage [get]
|
||||
func (r *StorageRouter) GetAccountUsage(c *fiber.Ctx) error {
|
||||
res, err := r.storageSvc.GetAccountUsage(GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// GetWorkspaceUsage godoc
|
||||
//
|
||||
// @Summary Get workspace usage
|
||||
// @Description Get workspace usage
|
||||
// @Tags Storage
|
||||
// @Id storage_get_workspace_usage
|
||||
// @Produce json
|
||||
// @Param id query string true "Workspace ID"
|
||||
// @Success 200 {object} service.StorageUsage
|
||||
// @Failure 500
|
||||
// @Router /storage/get_workspace_usage [get]
|
||||
func (r *StorageRouter) GetWorkspaceUsage(c *fiber.Ctx) error {
|
||||
id := c.Query("id")
|
||||
if id == "" {
|
||||
return errorpkg.NewMissingQueryParamError("id")
|
||||
}
|
||||
res, err := r.storageSvc.GetWorkspaceUsage(id, GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// GetFileUsage godoc
|
||||
//
|
||||
// @Summary Get file usage
|
||||
// @Description Get file usage
|
||||
// @Tags Storage
|
||||
// @Id storage_get_file_usage
|
||||
// @Produce json
|
||||
// @Param id query string true "File ID"
|
||||
// @Success 200 {object} service.StorageUsage
|
||||
// @Failure 500
|
||||
// @Router /storage/get_file_usage [get]
|
||||
func (r *StorageRouter) GetFileUsage(c *fiber.Ctx) error {
|
||||
id := c.Query("id")
|
||||
if id == "" {
|
||||
return errorpkg.NewMissingQueryParamError("id")
|
||||
}
|
||||
res, err := r.storageSvc.GetFileUsage(id, GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
12
Downloads/Voltaserve/api/router/user_id.go
Normal file
12
Downloads/Voltaserve/api/router/user_id.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func GetUserID(c *fiber.Ctx) string {
|
||||
user := c.Locals("user").(*jwt.Token)
|
||||
claims := user.Claims.(jwt.MapClaims)
|
||||
return claims["sub"].(string)
|
||||
}
|
93
Downloads/Voltaserve/api/router/user_router.go
Normal file
93
Downloads/Voltaserve/api/router/user_router.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/service"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type UserRouter struct {
|
||||
userSvc *service.UserService
|
||||
}
|
||||
|
||||
func NewUserRouter() *UserRouter {
|
||||
return &UserRouter{
|
||||
userSvc: service.NewUserService(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *UserRouter) AppendRoutes(g fiber.Router) {
|
||||
g.Get("/", r.List)
|
||||
}
|
||||
|
||||
// List godoc
|
||||
//
|
||||
// @Summary List
|
||||
// @Description List
|
||||
// @Tags Users
|
||||
// @Id users_list
|
||||
// @Produce json
|
||||
// @Param query query string false "Query"
|
||||
// @Param organization_id query string false "Organization ID"
|
||||
// @Param group query string false "Group ID"
|
||||
// @Param page query string false "Page"
|
||||
// @Param size query string false "Size"
|
||||
// @Param sort_by query string false "Sort By"
|
||||
// @Param sort_order query string false "Sort Order"
|
||||
// @Success 200 {object} service.UserList
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /users [get]
|
||||
func (r *UserRouter) List(c *fiber.Ctx) error {
|
||||
var err error
|
||||
var page int64
|
||||
if c.Query("page") == "" {
|
||||
page = 1
|
||||
} else {
|
||||
page, err = strconv.ParseInt(c.Query("page"), 10, 32)
|
||||
if err != nil {
|
||||
page = 1
|
||||
}
|
||||
}
|
||||
var size int64
|
||||
if c.Query("size") == "" {
|
||||
size = UserDefaultPageSize
|
||||
} else {
|
||||
size, err = strconv.ParseInt(c.Query("size"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sortBy := c.Query("sort_by")
|
||||
if !IsValidSortBy(sortBy) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_by")
|
||||
}
|
||||
sortOrder := c.Query("sort_order")
|
||||
if !IsValidSortOrder(sortOrder) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_order")
|
||||
}
|
||||
userID := GetUserID(c)
|
||||
var nonGroupMembersOnly bool
|
||||
if c.Query("non_group_members_only") != "" {
|
||||
nonGroupMembersOnly, err = strconv.ParseBool(c.Query("non_group_members_only"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
res, err := r.userSvc.List(service.UserListOptions{
|
||||
Query: c.Query("query"),
|
||||
OrganizationID: c.Query("organization_id"),
|
||||
GroupID: c.Query("group_id"),
|
||||
NonGroupMembersOnly: nonGroupMembersOnly,
|
||||
SortBy: sortBy,
|
||||
SortOrder: sortOrder,
|
||||
Page: uint(page),
|
||||
Size: uint(size),
|
||||
}, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
11
Downloads/Voltaserve/api/router/validation.go
Normal file
11
Downloads/Voltaserve/api/router/validation.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package router
|
||||
|
||||
import "voltaserve/service"
|
||||
|
||||
func IsValidSortBy(value string) bool {
|
||||
return value == "" || value == service.SortByName || value == service.SortByKind || value == service.SortBySize || value == service.SortByDateCreated || value == service.SortByDateModified || value == service.SortByEmail || value == service.SortByFullName
|
||||
}
|
||||
|
||||
func IsValidSortOrder(value string) bool {
|
||||
return value == "" || value == service.SortOrderAsc || value == service.SortOrderDesc
|
||||
}
|
208
Downloads/Voltaserve/api/router/workspace_router.go
Normal file
208
Downloads/Voltaserve/api/router/workspace_router.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/service"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type WorkspaceRouter struct {
|
||||
workspaceSvc *service.WorkspaceService
|
||||
}
|
||||
|
||||
func NewWorkspaceRouter() *WorkspaceRouter {
|
||||
return &WorkspaceRouter{
|
||||
workspaceSvc: service.NewWorkspaceService(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *WorkspaceRouter) AppendRoutes(g fiber.Router) {
|
||||
g.Get("/", r.List)
|
||||
g.Post("/", r.Create)
|
||||
g.Get("/:id", r.GetByID)
|
||||
g.Delete("/:id", r.Delete)
|
||||
g.Post("/:id/update_name", r.UpdateName)
|
||||
g.Post("/:id/update_storage_capacity", r.UpdateStorageCapacity)
|
||||
}
|
||||
|
||||
// Create godoc
|
||||
//
|
||||
// @Summary Create
|
||||
// @Description Create
|
||||
// @Tags Workspaces
|
||||
// @Id workspaces_create
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param body body service.WorkspaceCreateOptions true "Body"
|
||||
// @Success 200 {object} service.Workspace
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /workspaces [post]
|
||||
func (r *WorkspaceRouter) Create(c *fiber.Ctx) error {
|
||||
userID := GetUserID(c)
|
||||
opts := new(service.WorkspaceCreateOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validator.New().Struct(opts); err != nil {
|
||||
return errorpkg.NewRequestBodyValidationError(err)
|
||||
}
|
||||
res, err := r.workspaceSvc.Create(*opts, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Status(http.StatusCreated).JSON(res)
|
||||
}
|
||||
|
||||
// GetByID godoc
|
||||
//
|
||||
// @Summary Get by ID
|
||||
// @Description Get by ID
|
||||
// @Tags Workspaces
|
||||
// @Id workspaces_get_by_id
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200 {object} service.Workspace
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /workspaces/{id} [get]
|
||||
func (r *WorkspaceRouter) GetByID(c *fiber.Ctx) error {
|
||||
res, err := r.workspaceSvc.Find(c.Params("id"), GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// List godoc
|
||||
//
|
||||
// @Summary List
|
||||
// @Description List
|
||||
// @Tags Workspaces
|
||||
// @Id workspaces_list
|
||||
// @Produce json
|
||||
// @Param query query string false "Query"
|
||||
// @Param page query string false "Page"
|
||||
// @Param size query string false "Size"
|
||||
// @Param sort_by query string false "Sort By"
|
||||
// @Param sort_order query string false "Sort Order"
|
||||
// @Success 200 {object} service.WorkspaceList
|
||||
// @Failure 404 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /workspaces [get]
|
||||
func (r *WorkspaceRouter) List(c *fiber.Ctx) error {
|
||||
var err error
|
||||
var page int64
|
||||
if c.Query("page") == "" {
|
||||
page = 1
|
||||
} else {
|
||||
page, err = strconv.ParseInt(c.Query("page"), 10, 32)
|
||||
if err != nil {
|
||||
page = 1
|
||||
}
|
||||
}
|
||||
var size int64
|
||||
if c.Query("size") == "" {
|
||||
size = WorkspaceDefaultPageSize
|
||||
} else {
|
||||
size, err = strconv.ParseInt(c.Query("size"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
sortBy := c.Query("sort_by")
|
||||
if !IsValidSortBy(sortBy) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_by")
|
||||
}
|
||||
sortOrder := c.Query("sort_order")
|
||||
if !IsValidSortOrder(sortOrder) {
|
||||
return errorpkg.NewInvalidQueryParamError("sort_order")
|
||||
}
|
||||
res, err := r.workspaceSvc.List(service.WorkspaceListOptions{
|
||||
Query: c.Query("query"),
|
||||
Page: uint(page),
|
||||
Size: uint(size),
|
||||
SortBy: sortBy,
|
||||
SortOrder: sortOrder,
|
||||
}, GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// UpdateName godoc
|
||||
//
|
||||
// @Summary Update name
|
||||
// @Description Update name
|
||||
// @Tags Workspaces
|
||||
// @Id workspaces_update_name
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Param body body service.WorkspaceUpdateNameOptions true "Body"
|
||||
// @Success 200 {object} service.Workspace
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /workspaces/{id}/update_name [post]
|
||||
func (r *WorkspaceRouter) UpdateName(c *fiber.Ctx) error {
|
||||
opts := new(service.WorkspaceUpdateNameOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := r.workspaceSvc.UpdateName(c.Params("id"), opts.Name, GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// UpdateStorageCapacity godoc
|
||||
//
|
||||
// @Summary Update storage capacity
|
||||
// @Description Update storage capacity
|
||||
// @Tags Workspaces
|
||||
// @Id workspaces_update_storage_capacity
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "Id"
|
||||
// @Param body body service.WorkspaceUpdateStorageCapacityOptions true "Body"
|
||||
// @Success 200 {object} service.Workspace
|
||||
// @Failure 400 {object} errorpkg.ErrorResponse
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /workspaces/{id}/update_storage_capacity [post]
|
||||
func (r *WorkspaceRouter) UpdateStorageCapacity(c *fiber.Ctx) error {
|
||||
opts := new(service.WorkspaceUpdateStorageCapacityOptions)
|
||||
if err := c.BodyParser(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := r.workspaceSvc.UpdateStorageCapacity(c.Params("id"), opts.StorageCapacity, GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.JSON(res)
|
||||
}
|
||||
|
||||
// Delete godoc
|
||||
//
|
||||
// @Summary Delete
|
||||
// @Description Delete
|
||||
// @Tags Workspaces
|
||||
// @Id workspaces_delete
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "ID"
|
||||
// @Success 200
|
||||
// @Failure 500 {object} errorpkg.ErrorResponse
|
||||
// @Router /workspaces/{id} [delete]
|
||||
func (r *WorkspaceRouter) Delete(c *fiber.Ctx) error {
|
||||
err := r.workspaceSvc.Delete(c.Params("id"), GetUserID(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
}
|
103
Downloads/Voltaserve/api/search/file_search.go
Normal file
103
Downloads/Voltaserve/api/search/file_search.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type FileSearch struct {
|
||||
search *infra.SearchManager
|
||||
index string
|
||||
s3 *infra.S3Manager
|
||||
}
|
||||
|
||||
func NewFileSearch() *FileSearch {
|
||||
return &FileSearch{
|
||||
index: infra.FileSearchIndex,
|
||||
search: infra.NewSearchManager(),
|
||||
s3: infra.NewS3Manager(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FileSearch) Index(files []model.File) (err error) {
|
||||
if len(files) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err = s.populateTextField(files); err != nil {
|
||||
return err
|
||||
}
|
||||
var res []infra.SearchModel
|
||||
for _, f := range files {
|
||||
res = append(res, f)
|
||||
}
|
||||
if err := s.search.Index(s.index, res); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileSearch) Update(files []model.File) (err error) {
|
||||
if len(files) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err = s.populateTextField(files); err != nil {
|
||||
return err
|
||||
}
|
||||
var res []infra.SearchModel
|
||||
for _, f := range files {
|
||||
res = append(res, f)
|
||||
}
|
||||
if err := s.search.Update(s.index, res); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileSearch) Delete(ids []string) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := s.search.Delete(s.index, ids); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileSearch) Query(query string) ([]model.File, error) {
|
||||
hits, err := s.search.Query(s.index, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []model.File
|
||||
for _, v := range hits {
|
||||
var b []byte
|
||||
b, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file := repo.NewFile()
|
||||
if err = json.Unmarshal(b, &file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, file)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *FileSearch) populateTextField(files []model.File) error {
|
||||
for _, f := range files {
|
||||
if f.GetSnapshots() != nil &&
|
||||
len(f.GetSnapshots()) > 0 &&
|
||||
f.GetSnapshots()[0].HasText() {
|
||||
var text string
|
||||
text, err := s.s3.GetText(f.GetSnapshots()[0].GetText().Key, f.GetSnapshots()[0].GetText().Bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SetText(&text)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
81
Downloads/Voltaserve/api/search/group_search.go
Normal file
81
Downloads/Voltaserve/api/search/group_search.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type GroupSearch struct {
|
||||
index string
|
||||
search *infra.SearchManager
|
||||
groupRepo repo.GroupRepo
|
||||
}
|
||||
|
||||
func NewGroupSearch() *GroupSearch {
|
||||
return &GroupSearch{
|
||||
index: infra.GroupSearchIndex,
|
||||
search: infra.NewSearchManager(),
|
||||
groupRepo: repo.NewGroupRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *GroupSearch) Index(groups []model.Group) error {
|
||||
if len(groups) == 0 {
|
||||
return nil
|
||||
}
|
||||
var res []infra.SearchModel
|
||||
for _, g := range groups {
|
||||
res = append(res, g)
|
||||
}
|
||||
if err := s.search.Index(s.index, res); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *GroupSearch) Update(groups []model.Group) error {
|
||||
if len(groups) == 0 {
|
||||
return nil
|
||||
}
|
||||
var res []infra.SearchModel
|
||||
for _, g := range groups {
|
||||
res = append(res, g)
|
||||
}
|
||||
if err := s.search.Update(s.index, res); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *GroupSearch) Delete(ids []string) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := s.search.Delete(s.index, ids); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *GroupSearch) Query(query string) ([]model.Group, error) {
|
||||
hits, err := s.search.Query(s.index, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []model.Group
|
||||
for _, v := range hits {
|
||||
var b []byte
|
||||
b, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
group := repo.NewGroup()
|
||||
if err = json.Unmarshal(b, &group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, group)
|
||||
}
|
||||
return res, nil
|
||||
}
|
81
Downloads/Voltaserve/api/search/organization_search.go
Normal file
81
Downloads/Voltaserve/api/search/organization_search.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type OrganizationSearch struct {
|
||||
index string
|
||||
search *infra.SearchManager
|
||||
orgRepo repo.OrganizationRepo
|
||||
}
|
||||
|
||||
func NewOrganizationSearch() *OrganizationSearch {
|
||||
return &OrganizationSearch{
|
||||
index: infra.OrganizationSearchIndex,
|
||||
search: infra.NewSearchManager(),
|
||||
orgRepo: repo.NewOrganizationRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OrganizationSearch) Index(orgs []model.Organization) error {
|
||||
if len(orgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
var res []infra.SearchModel
|
||||
for _, o := range orgs {
|
||||
res = append(res, o)
|
||||
}
|
||||
if err := s.search.Index(s.index, res); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *OrganizationSearch) Update(orgs []model.Organization) error {
|
||||
if len(orgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
var res []infra.SearchModel
|
||||
for _, o := range orgs {
|
||||
res = append(res, o)
|
||||
}
|
||||
if err := s.search.Update(s.index, res); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *OrganizationSearch) Delete(ids []string) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := s.search.Delete(s.index, ids); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *OrganizationSearch) Query(query string) ([]model.Organization, error) {
|
||||
hits, err := s.search.Query(s.index, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []model.Organization
|
||||
for _, v := range hits {
|
||||
var b []byte
|
||||
b, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org := repo.NewOrganization()
|
||||
if err = json.Unmarshal(b, &org); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, org)
|
||||
}
|
||||
return res, nil
|
||||
}
|
40
Downloads/Voltaserve/api/search/user_search.go
Normal file
40
Downloads/Voltaserve/api/search/user_search.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type UserSearch struct {
|
||||
index string
|
||||
search *infra.SearchManager
|
||||
}
|
||||
|
||||
func NewUserSearch() *UserSearch {
|
||||
return &UserSearch{
|
||||
index: infra.UserSearchIndex,
|
||||
search: infra.NewSearchManager(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UserSearch) Query(query string) ([]model.User, error) {
|
||||
hits, err := s.search.Query(s.index, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := []model.User{}
|
||||
for _, v := range hits {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user := repo.NewUser()
|
||||
if err := json.Unmarshal(b, &user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, user)
|
||||
}
|
||||
return res, nil
|
||||
}
|
81
Downloads/Voltaserve/api/search/workspace_search.go
Normal file
81
Downloads/Voltaserve/api/search/workspace_search.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type WorkspaceSearch struct {
|
||||
index string
|
||||
search *infra.SearchManager
|
||||
workspaceRepo repo.WorkspaceRepo
|
||||
}
|
||||
|
||||
func NewWorkspaceSearch() *WorkspaceSearch {
|
||||
return &WorkspaceSearch{
|
||||
index: infra.WorkspaceSearchIndex,
|
||||
search: infra.NewSearchManager(),
|
||||
workspaceRepo: repo.NewWorkspaceRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WorkspaceSearch) Index(workspaces []model.Workspace) error {
|
||||
if len(workspaces) == 0 {
|
||||
return nil
|
||||
}
|
||||
var res []infra.SearchModel
|
||||
for _, w := range workspaces {
|
||||
res = append(res, w)
|
||||
}
|
||||
if err := s.search.Index(s.index, res); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *WorkspaceSearch) Update(workspaces []model.Workspace) error {
|
||||
if len(workspaces) == 0 {
|
||||
return nil
|
||||
}
|
||||
var res []infra.SearchModel
|
||||
for _, w := range workspaces {
|
||||
res = append(res, w)
|
||||
}
|
||||
if err := s.search.Update(s.index, res); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *WorkspaceSearch) Delete(ids []string) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := s.search.Delete(s.index, ids); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *WorkspaceSearch) Query(query string) ([]model.Workspace, error) {
|
||||
hits, err := s.search.Query(s.index, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []model.Workspace
|
||||
for _, v := range hits {
|
||||
var b []byte
|
||||
b, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspace := repo.NewWorkspace()
|
||||
if err = json.Unmarshal(b, &workspace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, workspace)
|
||||
}
|
||||
return res, nil
|
||||
}
|
12
Downloads/Voltaserve/api/service/constants.go
Normal file
12
Downloads/Voltaserve/api/service/constants.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package service
|
||||
|
||||
const SortByEmail = "email"
|
||||
const SortByFullName = "full_name"
|
||||
const SortByName = "name"
|
||||
const SortByKind = "kind"
|
||||
const SortBySize = "size"
|
||||
const SortByDateCreated = "date_created"
|
||||
const SortByDateModified = "date_modified"
|
||||
|
||||
const SortOrderAsc = "asc"
|
||||
const SortOrderDesc = "desc"
|
1922
Downloads/Voltaserve/api/service/file_service.go
Normal file
1922
Downloads/Voltaserve/api/service/file_service.go
Normal file
File diff suppressed because it is too large
Load Diff
520
Downloads/Voltaserve/api/service/group_service.go
Normal file
520
Downloads/Voltaserve/api/service/group_service.go
Normal file
@@ -0,0 +1,520 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
"voltaserve/cache"
|
||||
"voltaserve/config"
|
||||
"voltaserve/guard"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
"voltaserve/search"
|
||||
)
|
||||
|
||||
type Group struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Image *string `json:"image,omitempty"`
|
||||
Organization Organization `json:"organization"`
|
||||
Permission string `json:"permission"`
|
||||
CreateTime string `json:"createTime,omitempty"`
|
||||
UpdateTime *string `json:"updateTime"`
|
||||
}
|
||||
|
||||
type GroupList struct {
|
||||
Data []*Group `json:"data"`
|
||||
TotalPages uint `json:"totalPages"`
|
||||
TotalElements uint `json:"totalElements"`
|
||||
Page uint `json:"page"`
|
||||
Size uint `json:"size"`
|
||||
}
|
||||
|
||||
type GroupCreateOptions struct {
|
||||
Name string `json:"name" validate:"required,max=255"`
|
||||
Image *string `json:"image"`
|
||||
OrganizationID string `json:"organizationId" validate:"required"`
|
||||
}
|
||||
|
||||
type GroupListOptions struct {
|
||||
Query string
|
||||
OrganizationID string
|
||||
Page uint
|
||||
Size uint
|
||||
SortBy string
|
||||
SortOrder string
|
||||
}
|
||||
|
||||
type GroupUpdateNameOptions struct {
|
||||
Name string `json:"name" validate:"required,max=255"`
|
||||
}
|
||||
|
||||
type GroupUpdateImageOptions struct {
|
||||
Image string `json:"image" validate:"required,base64"`
|
||||
}
|
||||
|
||||
type GroupAddMemberOptions struct {
|
||||
UserID string `json:"userId" validate:"required"`
|
||||
}
|
||||
|
||||
type GroupRemoveMemberOptions struct {
|
||||
UserID string `json:"userId" validate:"required"`
|
||||
}
|
||||
|
||||
type GroupService struct {
|
||||
groupRepo repo.GroupRepo
|
||||
groupGuard *guard.GroupGuard
|
||||
groupSearch *search.GroupSearch
|
||||
groupMapper *groupMapper
|
||||
groupCache *cache.GroupCache
|
||||
userRepo repo.UserRepo
|
||||
userSearch *search.UserSearch
|
||||
userMapper *userMapper
|
||||
workspaceRepo repo.WorkspaceRepo
|
||||
workspaceCache *cache.WorkspaceCache
|
||||
fileRepo repo.FileRepo
|
||||
fileCache *cache.FileCache
|
||||
fileGuard *guard.FileGuard
|
||||
orgRepo repo.OrganizationRepo
|
||||
orgCache *cache.OrganizationCache
|
||||
orgGuard *guard.OrganizationGuard
|
||||
config config.Config
|
||||
}
|
||||
|
||||
func NewGroupService() *GroupService {
|
||||
return &GroupService{
|
||||
groupRepo: repo.NewGroupRepo(),
|
||||
groupGuard: guard.NewGroupGuard(),
|
||||
groupCache: cache.NewGroupCache(),
|
||||
groupSearch: search.NewGroupSearch(),
|
||||
groupMapper: newGroupMapper(),
|
||||
userRepo: repo.NewUserRepo(),
|
||||
userSearch: search.NewUserSearch(),
|
||||
userMapper: newUserMapper(),
|
||||
workspaceRepo: repo.NewWorkspaceRepo(),
|
||||
workspaceCache: cache.NewWorkspaceCache(),
|
||||
fileRepo: repo.NewFileRepo(),
|
||||
fileCache: cache.NewFileCache(),
|
||||
orgRepo: repo.NewOrganizationRepo(),
|
||||
orgGuard: guard.NewOrganizationGuard(),
|
||||
orgCache: cache.NewOrganizationCache(),
|
||||
fileGuard: guard.NewFileGuard(),
|
||||
config: config.GetConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *GroupService) Create(opts GroupCreateOptions, userID string) (*Group, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org, err := svc.orgCache.Get(opts.OrganizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.orgGuard.Authorize(user, org, model.PermissionEditor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
group, err := svc.groupRepo.Insert(repo.GroupInsertOptions{
|
||||
ID: helper.NewID(),
|
||||
Name: opts.Name,
|
||||
OrganizationID: opts.OrganizationID,
|
||||
OwnerID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.groupRepo.GrantUserPermission(group.GetID(), userID, model.PermissionOwner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
group, err = svc.groupRepo.Find(group.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.groupSearch.Index([]model.Group{group}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.groupCache.Set(group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := svc.groupMapper.mapOne(group, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) Find(id string, userID string) (*Group, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
group, err := svc.groupCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.groupGuard.Authorize(user, group, model.PermissionViewer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := svc.groupMapper.mapOne(group, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) List(opts GroupListOptions, userID string) (*GroupList, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var authorized []model.Group
|
||||
if opts.Query == "" {
|
||||
if opts.OrganizationID == "" {
|
||||
ids, err := svc.groupRepo.GetIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorized, err = svc.doAuthorizationByIDs(ids, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
groups, err := svc.orgRepo.GetGroups(opts.OrganizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorized, err = svc.doAuthorization(groups, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
groups, err := svc.groupSearch.Query(opts.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var filtered []model.Group
|
||||
if opts.OrganizationID == "" {
|
||||
filtered = groups
|
||||
} else {
|
||||
for _, g := range groups {
|
||||
if g.GetOrganizationID() == opts.OrganizationID {
|
||||
filtered = append(filtered, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
authorized, err = svc.doAuthorization(filtered, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if opts.SortBy == "" {
|
||||
opts.SortBy = SortByDateCreated
|
||||
}
|
||||
if opts.SortOrder == "" {
|
||||
opts.SortOrder = SortOrderAsc
|
||||
}
|
||||
sorted := svc.doSorting(authorized, opts.SortBy, opts.SortOrder)
|
||||
paged, totalElements, totalPages := svc.doPagination(sorted, opts.Page, opts.Size)
|
||||
mapped, err := svc.groupMapper.mapMany(paged, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &GroupList{
|
||||
Data: mapped,
|
||||
TotalPages: totalPages,
|
||||
TotalElements: totalElements,
|
||||
Page: opts.Page,
|
||||
Size: uint(len(mapped)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) UpdateName(id string, name string, userID string) (*Group, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
group, err := svc.groupCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.groupGuard.Authorize(user, group, model.PermissionEditor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
group.SetName(name)
|
||||
if err := svc.groupRepo.Save(group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.groupSearch.Update([]model.Group{group}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = svc.groupCache.Set(group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := svc.groupMapper.mapOne(group, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) Delete(id string, userID string) error {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group, err := svc.groupCache.Get(id)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if err := svc.groupGuard.Authorize(user, group, model.PermissionOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.groupRepo.Delete(id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.groupSearch.Delete([]string{group.GetID()}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.refreshCacheForOrganization(group.GetOrganizationID()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) AddMember(id string, memberID string, userID string) error {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group, err := svc.groupCache.Get(id)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if err := svc.groupGuard.Authorize(user, group, model.PermissionOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := svc.userRepo.Find(memberID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.groupRepo.AddUser(id, memberID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.groupRepo.GrantUserPermission(group.GetID(), memberID, model.PermissionViewer); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := svc.groupCache.Refresh(group.GetID()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.refreshCacheForOrganization(group.GetOrganizationID()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) RemoveMember(id string, memberID string, userID string) error {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group, err := svc.groupCache.Get(id)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if err := svc.groupGuard.Authorize(user, group, model.PermissionOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.RemoveMemberUnauthorized(id, memberID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) RemoveMemberUnauthorized(id string, memberID string) error {
|
||||
group, err := svc.groupCache.Get(id)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if _, err := svc.userRepo.Find(memberID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.groupRepo.RemoveMember(id, memberID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.groupRepo.RevokeUserPermission(id, memberID); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := svc.groupCache.Refresh(group.GetID()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.refreshCacheForOrganization(group.GetOrganizationID()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) refreshCacheForOrganization(orgID string) error {
|
||||
workspaceIDs, err := svc.workspaceRepo.GetIDsByOrganization(orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, workspaceID := range workspaceIDs {
|
||||
if _, err := svc.workspaceCache.Refresh(workspaceID); err != nil {
|
||||
return err
|
||||
}
|
||||
filesIDs, err := svc.fileRepo.GetIDsByWorkspace(workspaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, id := range filesIDs {
|
||||
if _, err := svc.fileCache.Refresh(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) doAuthorization(data []model.Group, user model.User) ([]model.Group, error) {
|
||||
var res []model.Group
|
||||
for _, g := range data {
|
||||
if svc.groupGuard.IsAuthorized(user, g, model.PermissionViewer) {
|
||||
res = append(res, g)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) doAuthorizationByIDs(ids []string, user model.User) ([]model.Group, error) {
|
||||
var res []model.Group
|
||||
for _, id := range ids {
|
||||
var o model.Group
|
||||
o, err := svc.groupCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if svc.groupGuard.IsAuthorized(user, o, model.PermissionViewer) {
|
||||
res = append(res, o)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *GroupService) doSorting(data []model.Group, sortBy string, sortOrder string) []model.Group {
|
||||
if sortBy == SortByName {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if sortOrder == SortOrderDesc {
|
||||
return data[i].GetName() > data[j].GetName()
|
||||
} else {
|
||||
return data[i].GetName() < data[j].GetName()
|
||||
}
|
||||
})
|
||||
return data
|
||||
} else if sortBy == SortByDateCreated {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
a, _ := time.Parse(time.RFC3339, data[i].GetCreateTime())
|
||||
b, _ := time.Parse(time.RFC3339, data[j].GetCreateTime())
|
||||
if sortOrder == SortOrderDesc {
|
||||
return a.UnixMilli() > b.UnixMilli()
|
||||
} else {
|
||||
return a.UnixMilli() < b.UnixMilli()
|
||||
}
|
||||
})
|
||||
return data
|
||||
} else if sortBy == SortByDateModified {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if data[i].GetUpdateTime() != nil && data[j].GetUpdateTime() != nil {
|
||||
a, _ := time.Parse(time.RFC3339, *data[i].GetUpdateTime())
|
||||
b, _ := time.Parse(time.RFC3339, *data[j].GetUpdateTime())
|
||||
if sortOrder == SortOrderDesc {
|
||||
return a.UnixMilli() > b.UnixMilli()
|
||||
} else {
|
||||
return a.UnixMilli() < b.UnixMilli()
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (svc *GroupService) doPagination(data []model.Group, page, size uint) ([]model.Group, uint, uint) {
|
||||
totalElements := uint(len(data))
|
||||
totalPages := (totalElements + size - 1) / size
|
||||
if page > totalPages {
|
||||
return nil, totalElements, totalPages
|
||||
}
|
||||
startIndex := (page - 1) * size
|
||||
endIndex := startIndex + size
|
||||
if endIndex > totalElements {
|
||||
endIndex = totalElements
|
||||
}
|
||||
pageData := data[startIndex:endIndex]
|
||||
return pageData, totalElements, totalPages
|
||||
}
|
||||
|
||||
type groupMapper struct {
|
||||
orgCache *cache.OrganizationCache
|
||||
orgMapper *organizationMapper
|
||||
groupCache *cache.GroupCache
|
||||
}
|
||||
|
||||
func newGroupMapper() *groupMapper {
|
||||
return &groupMapper{
|
||||
orgCache: cache.NewOrganizationCache(),
|
||||
orgMapper: newOrganizationMapper(),
|
||||
groupCache: cache.NewGroupCache(),
|
||||
}
|
||||
}
|
||||
|
||||
func (mp *groupMapper) mapOne(m model.Group, userID string) (*Group, error) {
|
||||
org, err := mp.orgCache.Get(m.GetOrganizationID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := mp.orgMapper.mapOne(org, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &Group{
|
||||
ID: m.GetID(),
|
||||
Name: m.GetName(),
|
||||
Organization: *v,
|
||||
CreateTime: m.GetCreateTime(),
|
||||
UpdateTime: m.GetUpdateTime(),
|
||||
}
|
||||
res.Permission = ""
|
||||
for _, p := range m.GetUserPermissions() {
|
||||
if p.GetUserID() == userID && model.GetPermissionWeight(p.GetValue()) > model.GetPermissionWeight(res.Permission) {
|
||||
res.Permission = p.GetValue()
|
||||
}
|
||||
}
|
||||
for _, p := range m.GetGroupPermissions() {
|
||||
g, err := mp.groupCache.Get(p.GetGroupID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, u := range g.GetUsers() {
|
||||
if u == userID && model.GetPermissionWeight(p.GetValue()) > model.GetPermissionWeight(res.Permission) {
|
||||
res.Permission = p.GetValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (mp *groupMapper) mapMany(groups []model.Group, userID string) ([]*Group, error) {
|
||||
res := []*Group{}
|
||||
for _, g := range groups {
|
||||
v, err := mp.mapOne(g, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, v)
|
||||
}
|
||||
return res, nil
|
||||
}
|
428
Downloads/Voltaserve/api/service/invitation_service.go
Normal file
428
Downloads/Voltaserve/api/service/invitation_service.go
Normal file
@@ -0,0 +1,428 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"voltaserve/cache"
|
||||
"voltaserve/config"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/guard"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type Invitation struct {
|
||||
ID string `json:"id"`
|
||||
Owner *User `json:"owner,omitempty"`
|
||||
Email string `json:"email"`
|
||||
Organization *Organization `json:"organization,omitempty"`
|
||||
Status string `json:"status"`
|
||||
CreateTime string `json:"createTime"`
|
||||
UpdateTime *string `json:"updateTime"`
|
||||
}
|
||||
|
||||
type InvitationList struct {
|
||||
Data []*Invitation `json:"data"`
|
||||
TotalPages uint `json:"totalPages"`
|
||||
TotalElements uint `json:"totalElements"`
|
||||
Page uint `json:"page"`
|
||||
Size uint `json:"size"`
|
||||
}
|
||||
|
||||
type InvitationCreateOptions struct {
|
||||
OrganizationID string `json:"organizationId" validate:"required"`
|
||||
Emails []string `json:"emails" validate:"required,dive,email"`
|
||||
}
|
||||
|
||||
type InvitationListOptions struct {
|
||||
Page uint
|
||||
Size uint
|
||||
SortBy string
|
||||
SortOrder string
|
||||
}
|
||||
|
||||
type InvitationService struct {
|
||||
orgRepo repo.OrganizationRepo
|
||||
orgMapper *organizationMapper
|
||||
invitationRepo repo.InvitationRepo
|
||||
invitationMapper *invitationMapper
|
||||
orgCache *cache.OrganizationCache
|
||||
orgGuard *guard.OrganizationGuard
|
||||
userRepo repo.UserRepo
|
||||
mailTmpl *infra.MailTemplate
|
||||
config config.Config
|
||||
}
|
||||
|
||||
func NewInvitationService() *InvitationService {
|
||||
return &InvitationService{
|
||||
orgRepo: repo.NewOrganizationRepo(),
|
||||
orgCache: cache.NewOrganizationCache(),
|
||||
orgGuard: guard.NewOrganizationGuard(),
|
||||
invitationRepo: repo.NewInvitationRepo(),
|
||||
invitationMapper: newInvitationMapper(),
|
||||
userRepo: repo.NewUserRepo(),
|
||||
mailTmpl: infra.NewMailTemplate(),
|
||||
orgMapper: newOrganizationMapper(),
|
||||
config: config.GetConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *InvitationService) Create(opts InvitationCreateOptions, userID string) error {
|
||||
for i := range opts.Emails {
|
||||
opts.Emails[i] = strings.ToLower(opts.Emails[i])
|
||||
}
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
org, err := svc.orgCache.Get(opts.OrganizationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.orgGuard.Authorize(user, org, model.PermissionOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
orgMembers, err := svc.orgRepo.GetMembers(opts.OrganizationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outgoingInvitations, err := svc.invitationRepo.GetOutgoing(opts.OrganizationID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var emails []string
|
||||
|
||||
/* Collect emails of non existing members and outgoing invitations */
|
||||
for _, e := range opts.Emails {
|
||||
existing := false
|
||||
for _, u := range orgMembers {
|
||||
if e == u.GetEmail() {
|
||||
existing = true
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, i := range outgoingInvitations {
|
||||
if e == i.GetEmail() && i.GetStatus() == model.InvitationStatusPending {
|
||||
existing = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !existing {
|
||||
emails = append(emails, e)
|
||||
}
|
||||
}
|
||||
|
||||
/* Persist invitations */
|
||||
invitations, err := svc.invitationRepo.Insert(repo.InvitationInsertOptions{
|
||||
UserID: userID,
|
||||
OrganizationID: opts.OrganizationID,
|
||||
Emails: emails,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/* Send emails */
|
||||
for _, inv := range invitations {
|
||||
variables := map[string]string{
|
||||
"USER_FULL_NAME": user.GetFullName(),
|
||||
"ORGANIZATION_NAME": org.GetName(),
|
||||
"UI_URL": svc.config.PublicUIURL,
|
||||
}
|
||||
_, err := svc.userRepo.FindByEmail(inv.GetEmail())
|
||||
var templateName string
|
||||
if err == nil {
|
||||
templateName = "join-organization"
|
||||
} else {
|
||||
templateName = "signup-and-join-organization"
|
||||
}
|
||||
if err := svc.mailTmpl.Send(templateName, inv.GetEmail(), variables); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *InvitationService) GetIncoming(opts InvitationListOptions, userID string) (*InvitationList, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invitations, err := svc.invitationRepo.GetIncoming(user.GetEmail())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.SortBy == "" {
|
||||
opts.SortBy = SortByDateCreated
|
||||
}
|
||||
if opts.SortOrder == "" {
|
||||
opts.SortOrder = SortOrderAsc
|
||||
}
|
||||
sorted := svc.doSorting(invitations, opts.SortBy, opts.SortOrder)
|
||||
paged, totalElements, totalPages := svc.doPagination(sorted, opts.Page, opts.Size)
|
||||
mapped, err := svc.invitationMapper.mapMany(paged, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &InvitationList{
|
||||
Data: mapped,
|
||||
TotalPages: totalPages,
|
||||
TotalElements: totalElements,
|
||||
Page: opts.Page,
|
||||
Size: uint(len(mapped)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc *InvitationService) GetOutgoing(orgID string, opts InvitationListOptions, userID string) (*InvitationList, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invitations, err := svc.invitationRepo.GetOutgoing(orgID, user.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.SortBy == "" {
|
||||
opts.SortBy = SortByDateCreated
|
||||
}
|
||||
if opts.SortOrder == "" {
|
||||
opts.SortOrder = SortOrderAsc
|
||||
}
|
||||
sorted := svc.doSorting(invitations, opts.SortBy, opts.SortOrder)
|
||||
paged, totalElements, totalPages := svc.doPagination(sorted, opts.Page, opts.Size)
|
||||
mapped, err := svc.invitationMapper.mapMany(paged, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &InvitationList{
|
||||
Data: mapped,
|
||||
TotalPages: totalPages,
|
||||
TotalElements: totalElements,
|
||||
Page: opts.Page,
|
||||
Size: uint(len(mapped)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc *InvitationService) Accept(id string, userID string) error {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
invitation, err := svc.invitationRepo.Find(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if invitation.GetStatus() != model.InvitationStatusPending {
|
||||
return errorpkg.NewCannotAcceptNonPendingInvitationError(invitation)
|
||||
}
|
||||
if user.GetEmail() != invitation.GetEmail() {
|
||||
return errorpkg.NewUserNotAllowedToAcceptInvitationError(user, invitation)
|
||||
}
|
||||
org, err := svc.orgCache.Get(invitation.GetOrganizationID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, u := range org.GetUsers() {
|
||||
if u == userID {
|
||||
return errorpkg.NewUserAlreadyMemberOfOrganizationError(user, org)
|
||||
}
|
||||
}
|
||||
invitation.SetStatus(model.InvitationStatusAccepted)
|
||||
if err := svc.invitationRepo.Save(invitation); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.orgRepo.AddUser(invitation.GetOrganizationID(), userID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.orgRepo.GrantUserPermission(invitation.GetOrganizationID(), userID, model.PermissionViewer); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := svc.orgCache.Refresh(invitation.GetOrganizationID()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *InvitationService) Decline(id string, userID string) error {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
invitation, err := svc.invitationRepo.Find(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if invitation.GetStatus() != model.InvitationStatusPending {
|
||||
return errorpkg.NewCannotDeclineNonPendingInvitationError(invitation)
|
||||
}
|
||||
if user.GetEmail() != invitation.GetEmail() {
|
||||
return errorpkg.NewUserNotAllowedToDeclineInvitationError(user, invitation)
|
||||
}
|
||||
invitation.SetStatus(model.InvitationStatusDeclined)
|
||||
if err := svc.invitationRepo.Save(invitation); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *InvitationService) Resend(id string, userID string) error {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
invitation, err := svc.invitationRepo.Find(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if invitation.GetStatus() != model.InvitationStatusPending {
|
||||
return errorpkg.NewCannotResendNonPendingInvitationError(invitation)
|
||||
}
|
||||
org, err := svc.orgCache.Get(invitation.GetOrganizationID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
variables := map[string]string{
|
||||
"USER_FULL_NAME": user.GetFullName(),
|
||||
"ORGANIZATION_NAME": org.GetName(),
|
||||
"UI_URL": svc.config.PublicUIURL,
|
||||
}
|
||||
_, err = svc.userRepo.FindByEmail(invitation.GetEmail())
|
||||
var templateName string
|
||||
if err == nil {
|
||||
templateName = "join-organization"
|
||||
} else {
|
||||
templateName = "signup-and-join-organization"
|
||||
}
|
||||
if err := svc.mailTmpl.Send(templateName, invitation.GetEmail(), variables); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *InvitationService) Delete(id string, userID string) error {
|
||||
invitation, err := svc.invitationRepo.Find(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if userID != invitation.GetOwnerID() {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return errorpkg.NewUserNotAllowedToDeleteInvitationError(user, invitation)
|
||||
}
|
||||
if err := svc.invitationRepo.Delete(invitation.GetID()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *InvitationService) doSorting(data []model.Invitation, sortBy string, sortOrder string) []model.Invitation {
|
||||
if sortBy == SortByEmail {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if sortOrder == SortOrderDesc {
|
||||
return data[i].GetEmail() > data[j].GetEmail()
|
||||
} else {
|
||||
return data[i].GetEmail() < data[j].GetEmail()
|
||||
}
|
||||
})
|
||||
return data
|
||||
} else if sortBy == SortByDateCreated {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
a, _ := time.Parse(time.RFC3339, data[i].GetCreateTime())
|
||||
b, _ := time.Parse(time.RFC3339, data[j].GetCreateTime())
|
||||
if sortOrder == SortOrderDesc {
|
||||
return a.UnixMilli() > b.UnixMilli()
|
||||
} else {
|
||||
return a.UnixMilli() < b.UnixMilli()
|
||||
}
|
||||
})
|
||||
return data
|
||||
} else if sortBy == SortByDateModified {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if data[i].GetUpdateTime() != nil && data[j].GetUpdateTime() != nil {
|
||||
a, _ := time.Parse(time.RFC3339, *data[i].GetUpdateTime())
|
||||
b, _ := time.Parse(time.RFC3339, *data[j].GetUpdateTime())
|
||||
if sortOrder == SortOrderDesc {
|
||||
return a.UnixMilli() > b.UnixMilli()
|
||||
} else {
|
||||
return a.UnixMilli() < b.UnixMilli()
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (svc *InvitationService) doPagination(data []model.Invitation, page, size uint) ([]model.Invitation, uint, uint) {
|
||||
totalElements := uint(len(data))
|
||||
totalPages := (totalElements + size - 1) / size
|
||||
if page > totalPages {
|
||||
return nil, totalElements, totalPages
|
||||
}
|
||||
startIndex := (page - 1) * size
|
||||
endIndex := startIndex + size
|
||||
if endIndex > totalElements {
|
||||
endIndex = totalElements
|
||||
}
|
||||
pageData := data[startIndex:endIndex]
|
||||
return pageData, totalElements, totalPages
|
||||
}
|
||||
|
||||
type invitationMapper struct {
|
||||
orgCache *cache.OrganizationCache
|
||||
userRepo repo.UserRepo
|
||||
userMapper *userMapper
|
||||
orgMapper *organizationMapper
|
||||
}
|
||||
|
||||
func newInvitationMapper() *invitationMapper {
|
||||
return &invitationMapper{
|
||||
orgCache: cache.NewOrganizationCache(),
|
||||
userRepo: repo.NewUserRepo(),
|
||||
userMapper: newUserMapper(),
|
||||
orgMapper: newOrganizationMapper(),
|
||||
}
|
||||
}
|
||||
|
||||
func (mp *invitationMapper) mapOne(m model.Invitation, userID string) (*Invitation, error) {
|
||||
owner, err := mp.userRepo.Find(m.GetOwnerID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org, err := mp.orgCache.Get(m.GetOrganizationID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := mp.orgMapper.mapOne(org, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Invitation{
|
||||
ID: m.GetID(),
|
||||
Owner: mp.userMapper.mapOne(owner),
|
||||
Email: m.GetEmail(),
|
||||
Organization: v,
|
||||
Status: m.GetStatus(),
|
||||
CreateTime: m.GetCreateTime(),
|
||||
UpdateTime: m.GetUpdateTime(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (mp *invitationMapper) mapMany(invitations []model.Invitation, userID string) ([]*Invitation, error) {
|
||||
res := make([]*Invitation, 0)
|
||||
for _, m := range invitations {
|
||||
v, err := mp.mapOne(m, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, v)
|
||||
}
|
||||
return res, nil
|
||||
}
|
45
Downloads/Voltaserve/api/service/notification_service.go
Normal file
45
Downloads/Voltaserve/api/service/notification_service.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package service
|
||||
|
||||
import "voltaserve/repo"
|
||||
|
||||
type Notification struct {
|
||||
Type string `json:"type"`
|
||||
Body interface{} `json:"body"`
|
||||
}
|
||||
|
||||
type NotificationService struct {
|
||||
userRepo repo.UserRepo
|
||||
invitationRepo repo.InvitationRepo
|
||||
invitationMapper *invitationMapper
|
||||
}
|
||||
|
||||
func NewNotificationService() *NotificationService {
|
||||
return &NotificationService{
|
||||
userRepo: repo.NewUserRepo(),
|
||||
invitationRepo: repo.NewInvitationRepo(),
|
||||
invitationMapper: newInvitationMapper(),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *NotificationService) GetAll(userID string) ([]*Notification, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invitations, err := svc.invitationRepo.GetIncoming(user.GetEmail())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
notifications := make([]*Notification, 0)
|
||||
for _, inv := range invitations {
|
||||
v, err := svc.invitationMapper.mapOne(inv, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
notifications = append(notifications, &Notification{
|
||||
Type: "new_invitation",
|
||||
Body: &v,
|
||||
})
|
||||
}
|
||||
return notifications, nil
|
||||
}
|
418
Downloads/Voltaserve/api/service/organization_service.go
Normal file
418
Downloads/Voltaserve/api/service/organization_service.go
Normal file
@@ -0,0 +1,418 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
"voltaserve/cache"
|
||||
"voltaserve/config"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/guard"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
"voltaserve/search"
|
||||
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
)
|
||||
|
||||
type Organization struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Image *string `json:"image,omitempty"`
|
||||
Permission string `json:"permission"`
|
||||
CreateTime string `json:"createTime"`
|
||||
UpdateTime *string `json:"updateTime,omitempty"`
|
||||
}
|
||||
|
||||
type OrganizationList struct {
|
||||
Data []*Organization `json:"data"`
|
||||
TotalPages uint `json:"totalPages"`
|
||||
TotalElements uint `json:"totalElements"`
|
||||
Page uint `json:"page"`
|
||||
Size uint `json:"size"`
|
||||
}
|
||||
|
||||
type OrganizationCreateOptions struct {
|
||||
Name string `json:"name" validate:"required,max=255"`
|
||||
Image *string `json:"image"`
|
||||
}
|
||||
|
||||
type OrganizationListOptions struct {
|
||||
Query string
|
||||
Page uint
|
||||
Size uint
|
||||
SortBy string
|
||||
SortOrder string
|
||||
}
|
||||
|
||||
type OrganizationUpdateNameOptions struct {
|
||||
Name string `json:"name" validate:"required,max=255"`
|
||||
}
|
||||
|
||||
type OrganizationUpdateImageOptions struct {
|
||||
Image string `json:"image" validate:"required,base64"`
|
||||
}
|
||||
|
||||
type OrganizationRemoveMemberOptions struct {
|
||||
UserID string `json:"userId" validate:"required"`
|
||||
}
|
||||
|
||||
type OrganizationService struct {
|
||||
orgRepo repo.OrganizationRepo
|
||||
orgCache *cache.OrganizationCache
|
||||
orgGuard *guard.OrganizationGuard
|
||||
orgMapper *organizationMapper
|
||||
orgSearch *search.OrganizationSearch
|
||||
userRepo repo.UserRepo
|
||||
userSearch *search.UserSearch
|
||||
userMapper *userMapper
|
||||
groupRepo repo.GroupRepo
|
||||
groupService *GroupService
|
||||
groupMapper *groupMapper
|
||||
config config.Config
|
||||
}
|
||||
|
||||
func NewOrganizationService() *OrganizationService {
|
||||
return &OrganizationService{
|
||||
orgRepo: repo.NewOrganizationRepo(),
|
||||
orgCache: cache.NewOrganizationCache(),
|
||||
orgGuard: guard.NewOrganizationGuard(),
|
||||
orgSearch: search.NewOrganizationSearch(),
|
||||
orgMapper: newOrganizationMapper(),
|
||||
userRepo: repo.NewUserRepo(),
|
||||
userSearch: search.NewUserSearch(),
|
||||
groupRepo: repo.NewGroupRepo(),
|
||||
groupService: NewGroupService(),
|
||||
groupMapper: newGroupMapper(),
|
||||
userMapper: newUserMapper(),
|
||||
config: config.GetConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) Create(opts OrganizationCreateOptions, userID string) (*Organization, error) {
|
||||
org, err := svc.orgRepo.Insert(repo.OrganizationInsertOptions{
|
||||
ID: helper.NewID(),
|
||||
Name: opts.Name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.orgRepo.GrantUserPermission(org.GetID(), userID, model.PermissionOwner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org, err = svc.orgRepo.Find(org.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.orgSearch.Index([]model.Organization{org}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.orgCache.Set(org); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
res, err := svc.orgMapper.mapOne(org, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) Find(id string, userID string) (*Organization, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org, err := svc.orgCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.orgGuard.Authorize(user, org, model.PermissionViewer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := svc.orgMapper.mapOne(org, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) List(opts OrganizationListOptions, userID string) (*OrganizationList, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var authorized []model.Organization
|
||||
if opts.Query == "" {
|
||||
ids, err := svc.orgRepo.GetIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorized, err = svc.doAuthorizationByIDs(ids, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
orgs, err := svc.orgSearch.Query(opts.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorized, err = svc.doAuthorization(orgs, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if opts.SortBy == "" {
|
||||
opts.SortBy = SortByDateCreated
|
||||
}
|
||||
if opts.SortOrder == "" {
|
||||
opts.SortOrder = SortOrderAsc
|
||||
}
|
||||
sorted := svc.doSorting(authorized, opts.SortBy, opts.SortOrder)
|
||||
paged, totalElements, totalPages := svc.doPagination(sorted, opts.Page, opts.Size)
|
||||
mapped, err := svc.orgMapper.mapMany(paged, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &OrganizationList{
|
||||
Data: mapped,
|
||||
TotalPages: totalPages,
|
||||
TotalElements: totalElements,
|
||||
Page: opts.Page,
|
||||
Size: uint(len(mapped)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) UpdateName(id string, name string, userID string) (*Organization, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org, err := svc.orgCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.orgGuard.Authorize(user, org, model.PermissionEditor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org.SetName(name)
|
||||
if err := svc.orgRepo.Save(org); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.orgSearch.Update([]model.Organization{org}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = svc.orgCache.Set(org)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := svc.orgMapper.mapOne(org, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) Delete(id string, userID string) error {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
org, err := svc.orgCache.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.orgGuard.Authorize(user, org, model.PermissionOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.orgRepo.Delete(id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.orgCache.Delete(org.GetID()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.orgSearch.Delete([]string{org.GetID()}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) RemoveMember(id string, memberID string, userID string) error {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
member, err := svc.userRepo.Find(memberID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
org, err := svc.orgCache.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/* Make sure member is not the last remaining owner of the organization */
|
||||
ownerCount, err := svc.orgRepo.GetOwnerCount(org.GetID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if svc.orgGuard.IsAuthorized(member, org, model.PermissionOwner) && ownerCount == 1 {
|
||||
return errorpkg.NewCannotRemoveLastRemainingOwnerOfOrganizationError(org.GetID())
|
||||
}
|
||||
|
||||
if userID != member.GetID() {
|
||||
if err := svc.orgGuard.Authorize(user, org, model.PermissionOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove member from all groups belonging to this organization */
|
||||
groupsIDs, err := svc.groupRepo.GetIDsForOrganization(org.GetID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, groupID := range groupsIDs {
|
||||
if err := svc.groupService.RemoveMemberUnauthorized(groupID, member.GetID()); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := svc.orgRepo.RevokeUserPermission(id, member.GetID()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := svc.orgRepo.RemoveMember(id, member.GetID()); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := svc.orgCache.Refresh(org.GetID()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) doAuthorization(data []model.Organization, user model.User) ([]model.Organization, error) {
|
||||
var res []model.Organization
|
||||
for _, o := range data {
|
||||
if svc.orgGuard.IsAuthorized(user, o, model.PermissionViewer) {
|
||||
res = append(res, o)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) doAuthorizationByIDs(ids []string, user model.User) ([]model.Organization, error) {
|
||||
var res []model.Organization
|
||||
for _, id := range ids {
|
||||
var o model.Organization
|
||||
o, err := svc.orgCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if svc.orgGuard.IsAuthorized(user, o, model.PermissionViewer) {
|
||||
res = append(res, o)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) doSorting(data []model.Organization, sortBy string, sortOrder string) []model.Organization {
|
||||
if sortBy == SortByName {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if sortOrder == SortOrderDesc {
|
||||
return data[i].GetName() > data[j].GetName()
|
||||
} else {
|
||||
return data[i].GetName() < data[j].GetName()
|
||||
}
|
||||
})
|
||||
return data
|
||||
} else if sortBy == SortByDateCreated {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
a, _ := time.Parse(time.RFC3339, data[i].GetCreateTime())
|
||||
b, _ := time.Parse(time.RFC3339, data[j].GetCreateTime())
|
||||
if sortOrder == SortOrderDesc {
|
||||
return a.UnixMilli() > b.UnixMilli()
|
||||
} else {
|
||||
return a.UnixMilli() < b.UnixMilli()
|
||||
}
|
||||
})
|
||||
return data
|
||||
} else if sortBy == SortByDateModified {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if data[i].GetUpdateTime() != nil && data[j].GetUpdateTime() != nil {
|
||||
a, _ := time.Parse(time.RFC3339, *data[i].GetUpdateTime())
|
||||
b, _ := time.Parse(time.RFC3339, *data[j].GetUpdateTime())
|
||||
if sortOrder == SortOrderDesc {
|
||||
return a.UnixMilli() > b.UnixMilli()
|
||||
} else {
|
||||
return a.UnixMilli() < b.UnixMilli()
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (svc *OrganizationService) doPagination(data []model.Organization, page, size uint) ([]model.Organization, uint, uint) {
|
||||
totalElements := uint(len(data))
|
||||
totalPages := (totalElements + size - 1) / size
|
||||
if page > totalPages {
|
||||
return nil, totalElements, totalPages
|
||||
}
|
||||
startIndex := (page - 1) * size
|
||||
endIndex := startIndex + size
|
||||
if endIndex > totalElements {
|
||||
endIndex = totalElements
|
||||
}
|
||||
pageData := data[startIndex:endIndex]
|
||||
return pageData, totalElements, totalPages
|
||||
}
|
||||
|
||||
type organizationMapper struct {
|
||||
groupCache *cache.GroupCache
|
||||
}
|
||||
|
||||
func newOrganizationMapper() *organizationMapper {
|
||||
return &organizationMapper{
|
||||
groupCache: cache.NewGroupCache(),
|
||||
}
|
||||
}
|
||||
|
||||
func (mp *organizationMapper) mapOne(m model.Organization, userID string) (*Organization, error) {
|
||||
res := &Organization{
|
||||
ID: m.GetID(),
|
||||
Name: m.GetName(),
|
||||
CreateTime: m.GetCreateTime(),
|
||||
UpdateTime: m.GetUpdateTime(),
|
||||
}
|
||||
res.Permission = ""
|
||||
for _, p := range m.GetUserPermissions() {
|
||||
if p.GetUserID() == userID && model.GetPermissionWeight(p.GetValue()) > model.GetPermissionWeight(res.Permission) {
|
||||
res.Permission = p.GetValue()
|
||||
}
|
||||
}
|
||||
for _, p := range m.GetGroupPermissions() {
|
||||
g, err := mp.groupCache.Get(p.GetGroupID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, u := range g.GetUsers() {
|
||||
if u == userID && model.GetPermissionWeight(p.GetValue()) > model.GetPermissionWeight(res.Permission) {
|
||||
res.Permission = p.GetValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (mp *organizationMapper) mapMany(orgs []model.Organization, userID string) ([]*Organization, error) {
|
||||
res := make([]*Organization, 0)
|
||||
for _, f := range orgs {
|
||||
v, err := mp.mapOne(f, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, v)
|
||||
}
|
||||
return res, nil
|
||||
}
|
142
Downloads/Voltaserve/api/service/storage_service.go
Normal file
142
Downloads/Voltaserve/api/service/storage_service.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"voltaserve/cache"
|
||||
"voltaserve/guard"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
)
|
||||
|
||||
type StorageUsage struct {
|
||||
Bytes int64 `json:"bytes"`
|
||||
MaxBytes int64 `json:"maxBytes"`
|
||||
Percentage int `json:"percentage"`
|
||||
}
|
||||
|
||||
type StorageService struct {
|
||||
workspaceRepo repo.WorkspaceRepo
|
||||
workspaceCache *cache.WorkspaceCache
|
||||
workspaceGuard *guard.WorkspaceGuard
|
||||
fileRepo repo.FileRepo
|
||||
fileCache *cache.FileCache
|
||||
fileGuard *guard.FileGuard
|
||||
storageMapper *storageMapper
|
||||
userRepo repo.UserRepo
|
||||
}
|
||||
|
||||
func NewStorageService() *StorageService {
|
||||
return &StorageService{
|
||||
workspaceRepo: repo.NewWorkspaceRepo(),
|
||||
workspaceCache: cache.NewWorkspaceCache(),
|
||||
workspaceGuard: guard.NewWorkspaceGuard(),
|
||||
fileRepo: repo.NewFileRepo(),
|
||||
fileCache: cache.NewFileCache(),
|
||||
fileGuard: guard.NewFileGuard(),
|
||||
storageMapper: newStorageMapper(),
|
||||
userRepo: repo.NewUserRepo(),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *StorageService) GetAccountUsage(userID string) (*StorageUsage, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids, err := svc.workspaceRepo.GetIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspaces := []model.Workspace{}
|
||||
for _, id := range ids {
|
||||
var workspace model.Workspace
|
||||
workspace, err = svc.workspaceCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if svc.workspaceGuard.IsAuthorized(user, workspace, model.PermissionOwner) {
|
||||
workspaces = append(workspaces, workspace)
|
||||
}
|
||||
}
|
||||
var maxBytes int64 = 0
|
||||
var b int64 = 0
|
||||
for _, w := range workspaces {
|
||||
root, err := svc.fileCache.Get(w.GetRootID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size, err := svc.fileRepo.GetSize(root.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = b + size
|
||||
maxBytes = maxBytes + w.GetStorageCapacity()
|
||||
}
|
||||
return svc.storageMapper.mapStorageUsage(b, maxBytes), nil
|
||||
}
|
||||
|
||||
func (svc *StorageService) GetWorkspaceUsage(workspaceID string, userID string) (*StorageUsage, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspace, err := svc.workspaceCache.Get(workspaceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceGuard.Authorize(user, workspace, model.PermissionViewer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root, err := svc.fileCache.Get(workspace.GetRootID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.fileGuard.Authorize(user, root, model.PermissionViewer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size, err := svc.fileRepo.GetSize(root.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.storageMapper.mapStorageUsage(size, workspace.GetStorageCapacity()), nil
|
||||
}
|
||||
|
||||
func (svc *StorageService) GetFileUsage(fileID string, userID string) (*StorageUsage, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file, err := svc.fileCache.Get(fileID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.fileGuard.Authorize(user, file, model.PermissionViewer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size, err := svc.fileRepo.GetSize(file.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspace, err := svc.workspaceCache.Get(file.GetWorkspaceID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.storageMapper.mapStorageUsage(size, workspace.GetStorageCapacity()), nil
|
||||
}
|
||||
|
||||
type storageMapper struct {
|
||||
}
|
||||
|
||||
func newStorageMapper() *storageMapper {
|
||||
return &storageMapper{}
|
||||
}
|
||||
|
||||
func (mp *storageMapper) mapStorageUsage(byteCount int64, maxBytes int64) *StorageUsage {
|
||||
res := StorageUsage{
|
||||
Bytes: byteCount,
|
||||
MaxBytes: maxBytes,
|
||||
}
|
||||
if maxBytes != 0 {
|
||||
res.Percentage = int(byteCount * 100 / maxBytes)
|
||||
}
|
||||
return &res
|
||||
}
|
247
Downloads/Voltaserve/api/service/user_service.go
Normal file
247
Downloads/Voltaserve/api/service/user_service.go
Normal file
@@ -0,0 +1,247 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"voltaserve/cache"
|
||||
"voltaserve/config"
|
||||
"voltaserve/guard"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
"voltaserve/search"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID string `json:"id"`
|
||||
FullName string `json:"fullName"`
|
||||
Picture *string `json:"picture,omitempty"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
CreateTime string `json:"createTime"`
|
||||
UpdateTime *string `json:"updateTime"`
|
||||
}
|
||||
|
||||
type UserList struct {
|
||||
Data []*User `json:"data"`
|
||||
TotalPages uint `json:"totalPages"`
|
||||
TotalElements uint `json:"totalElements"`
|
||||
Page uint `json:"page"`
|
||||
Size uint `json:"size"`
|
||||
}
|
||||
|
||||
type UserListOptions struct {
|
||||
Query string
|
||||
OrganizationID string
|
||||
GroupID string
|
||||
NonGroupMembersOnly bool
|
||||
SortBy string
|
||||
SortOrder string
|
||||
Page uint
|
||||
Size uint
|
||||
}
|
||||
|
||||
type UserService struct {
|
||||
userRepo repo.UserRepo
|
||||
userMapper *userMapper
|
||||
userSearch *search.UserSearch
|
||||
orgRepo repo.OrganizationRepo
|
||||
orgCache *cache.OrganizationCache
|
||||
orgGuard *guard.OrganizationGuard
|
||||
groupRepo repo.GroupRepo
|
||||
groupGuard *guard.GroupGuard
|
||||
groupCache *cache.GroupCache
|
||||
config config.Config
|
||||
}
|
||||
|
||||
func NewUserService() *UserService {
|
||||
return &UserService{
|
||||
userRepo: repo.NewUserRepo(),
|
||||
userMapper: newUserMapper(),
|
||||
userSearch: search.NewUserSearch(),
|
||||
orgRepo: repo.NewOrganizationRepo(),
|
||||
orgCache: cache.NewOrganizationCache(),
|
||||
orgGuard: guard.NewOrganizationGuard(),
|
||||
groupRepo: repo.NewGroupRepo(),
|
||||
groupGuard: guard.NewGroupGuard(),
|
||||
groupCache: cache.NewGroupCache(),
|
||||
config: config.GetConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *UserService) List(opts UserListOptions, userID string) (*UserList, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.OrganizationID == "" && opts.GroupID == "" {
|
||||
return &UserList{
|
||||
Data: []*User{},
|
||||
TotalPages: 1,
|
||||
TotalElements: 0,
|
||||
Page: 1,
|
||||
Size: 0,
|
||||
}, nil
|
||||
}
|
||||
var org model.Organization
|
||||
if opts.OrganizationID != "" {
|
||||
org, err = svc.orgCache.Get(opts.OrganizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.orgGuard.Authorize(user, org, model.PermissionViewer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var group model.Group
|
||||
if opts.GroupID != "" {
|
||||
group, err = svc.groupCache.Get(opts.GroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.groupGuard.Authorize(user, group, model.PermissionViewer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
res := []model.User{}
|
||||
if opts.Query == "" {
|
||||
if opts.OrganizationID != "" && opts.GroupID != "" && opts.NonGroupMembersOnly {
|
||||
orgMembers, err := svc.orgRepo.GetMembers(group.GetOrganizationID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupMembers, err := svc.groupRepo.GetMembers(opts.GroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, om := range orgMembers {
|
||||
found := false
|
||||
for _, tm := range groupMembers {
|
||||
if om.GetID() == tm.GetID() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
res = append(res, om)
|
||||
}
|
||||
}
|
||||
} else if opts.OrganizationID != "" {
|
||||
res, err = svc.orgRepo.GetMembers(opts.OrganizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if opts.GroupID != "" {
|
||||
res, err = svc.groupRepo.GetMembers(opts.GroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
users, err := svc.userSearch.Query(opts.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var members []model.User
|
||||
if opts.OrganizationID != "" {
|
||||
members, err = svc.orgRepo.GetMembers(opts.OrganizationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if opts.GroupID != "" {
|
||||
members, err = svc.groupRepo.GetMembers(opts.GroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, m := range members {
|
||||
for _, u := range users {
|
||||
if u.GetID() == m.GetID() {
|
||||
res = append(res, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if opts.SortBy == "" {
|
||||
opts.SortBy = SortByDateCreated
|
||||
}
|
||||
if opts.SortOrder == "" {
|
||||
opts.SortOrder = SortOrderAsc
|
||||
}
|
||||
sorted := svc.doSorting(res, opts.SortBy, opts.SortOrder)
|
||||
paged, totalElements, totalPages := svc.doPagination(sorted, opts.Page, opts.Size)
|
||||
mapped, err := svc.userMapper.mapMany(paged)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UserList{
|
||||
Data: mapped,
|
||||
TotalPages: totalPages,
|
||||
TotalElements: totalElements,
|
||||
Page: opts.Page,
|
||||
Size: uint(len(mapped)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc *UserService) doSorting(data []model.User, sortBy string, sortOrder string) []model.User {
|
||||
if sortBy == SortByEmail {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if sortOrder == SortOrderDesc {
|
||||
return data[i].GetEmail() > data[j].GetEmail()
|
||||
} else {
|
||||
return data[i].GetEmail() < data[j].GetEmail()
|
||||
}
|
||||
})
|
||||
return data
|
||||
} else if sortBy == SortByFullName {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if sortOrder == SortOrderDesc {
|
||||
return data[i].GetFullName() > data[j].GetFullName()
|
||||
} else {
|
||||
return data[i].GetFullName() < data[j].GetFullName()
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (svc *UserService) doPagination(data []model.User, page, size uint) ([]model.User, uint, uint) {
|
||||
totalElements := uint(len(data))
|
||||
totalPages := (totalElements + size - 1) / size
|
||||
if page > totalPages {
|
||||
return nil, totalElements, totalPages
|
||||
}
|
||||
startIndex := (page - 1) * size
|
||||
endIndex := startIndex + size
|
||||
if endIndex > totalElements {
|
||||
endIndex = totalElements
|
||||
}
|
||||
pageData := data[startIndex:endIndex]
|
||||
return pageData, totalElements, totalPages
|
||||
}
|
||||
|
||||
type userMapper struct {
|
||||
}
|
||||
|
||||
func newUserMapper() *userMapper {
|
||||
return &userMapper{}
|
||||
}
|
||||
|
||||
func (mp *userMapper) mapOne(user model.User) *User {
|
||||
return &User{
|
||||
ID: user.GetID(),
|
||||
FullName: user.GetFullName(),
|
||||
Picture: user.GetPicture(),
|
||||
Email: user.GetEmail(),
|
||||
Username: user.GetUsername(),
|
||||
CreateTime: user.GetCreateTime(),
|
||||
UpdateTime: user.GetUpdateTime(),
|
||||
}
|
||||
}
|
||||
|
||||
func (mp *userMapper) mapMany(users []model.User) ([]*User, error) {
|
||||
res := []*User{}
|
||||
for _, u := range users {
|
||||
res = append(res, mp.mapOne(u))
|
||||
}
|
||||
return res, nil
|
||||
}
|
502
Downloads/Voltaserve/api/service/workspace_service.go
Normal file
502
Downloads/Voltaserve/api/service/workspace_service.go
Normal file
@@ -0,0 +1,502 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"voltaserve/cache"
|
||||
"voltaserve/config"
|
||||
"voltaserve/errorpkg"
|
||||
"voltaserve/guard"
|
||||
"voltaserve/helper"
|
||||
"voltaserve/infra"
|
||||
"voltaserve/model"
|
||||
"voltaserve/repo"
|
||||
"voltaserve/search"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Workspace struct {
|
||||
ID string `json:"id"`
|
||||
Image *string `json:"image,omitempty"`
|
||||
Name string `json:"name"`
|
||||
RootID string `json:"rootId,omitempty"`
|
||||
StorageCapacity int64 `json:"storageCapacity"`
|
||||
Permission string `json:"permission"`
|
||||
Organization Organization `json:"organization"`
|
||||
CreateTime string `json:"createTime"`
|
||||
UpdateTime *string `json:"updateTime,omitempty"`
|
||||
}
|
||||
|
||||
type WorkspaceList struct {
|
||||
Data []*Workspace `json:"data"`
|
||||
TotalPages uint `json:"totalPages"`
|
||||
TotalElements uint `json:"totalElements"`
|
||||
Page uint `json:"page"`
|
||||
Size uint `json:"size"`
|
||||
}
|
||||
|
||||
type WorkspaceCreateOptions struct {
|
||||
Name string `json:"name" validate:"required,max=255"`
|
||||
Image *string `json:"image"`
|
||||
OrganizationID string `json:"organizationId" validate:"required"`
|
||||
StorageCapacity int64 `json:"storageCapacity"`
|
||||
}
|
||||
|
||||
type WorkspaceListOptions struct {
|
||||
Query string
|
||||
Page uint
|
||||
Size uint
|
||||
SortBy string
|
||||
SortOrder string
|
||||
}
|
||||
|
||||
type WorkspaceUpdateNameOptions struct {
|
||||
Name string `json:"name" validate:"required,max=255"`
|
||||
}
|
||||
|
||||
type WorkspaceUpdateStorageCapacityOptions struct {
|
||||
StorageCapacity int64 `json:"storageCapacity" validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type WorkspaceUpdateIsAutomaticOCREnabledOptions struct {
|
||||
IsEnabled bool `json:"isEnabled" validate:"required"`
|
||||
}
|
||||
|
||||
type WorkspaceService struct {
|
||||
workspaceRepo repo.WorkspaceRepo
|
||||
workspaceCache *cache.WorkspaceCache
|
||||
workspaceGuard *guard.WorkspaceGuard
|
||||
workspaceSearch *search.WorkspaceSearch
|
||||
workspaceMapper *workspaceMapper
|
||||
fileRepo repo.FileRepo
|
||||
fileCache *cache.FileCache
|
||||
fileGuard *guard.FileGuard
|
||||
fileMapper *FileMapper
|
||||
userRepo repo.UserRepo
|
||||
s3 *infra.S3Manager
|
||||
config config.Config
|
||||
}
|
||||
|
||||
func NewWorkspaceService() *WorkspaceService {
|
||||
return &WorkspaceService{
|
||||
workspaceRepo: repo.NewWorkspaceRepo(),
|
||||
workspaceCache: cache.NewWorkspaceCache(),
|
||||
workspaceSearch: search.NewWorkspaceSearch(),
|
||||
workspaceGuard: guard.NewWorkspaceGuard(),
|
||||
workspaceMapper: newWorkspaceMapper(),
|
||||
fileRepo: repo.NewFileRepo(),
|
||||
fileCache: cache.NewFileCache(),
|
||||
fileGuard: guard.NewFileGuard(),
|
||||
fileMapper: NewFileMapper(),
|
||||
userRepo: repo.NewUserRepo(),
|
||||
s3: infra.NewS3Manager(),
|
||||
config: config.GetConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) Create(opts WorkspaceCreateOptions, userID string) (*Workspace, error) {
|
||||
id := helper.NewID()
|
||||
bucket := strings.ReplaceAll(uuid.NewString(), "-", "")
|
||||
if err := svc.s3.CreateBucket(bucket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.StorageCapacity == 0 {
|
||||
opts.StorageCapacity = svc.config.Defaults.WorkspaceStorageCapacityBytes
|
||||
}
|
||||
workspace, err := svc.workspaceRepo.Insert(repo.WorkspaceInsertOptions{
|
||||
ID: id,
|
||||
Name: opts.Name,
|
||||
StorageCapacity: opts.StorageCapacity,
|
||||
OrganizationID: opts.OrganizationID,
|
||||
Image: opts.Image,
|
||||
Bucket: bucket,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.workspaceRepo.GrantUserPermission(workspace.GetID(), userID, model.PermissionOwner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspace, err = svc.workspaceRepo.Find(workspace.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root, err := svc.fileRepo.Insert(repo.FileInsertOptions{
|
||||
Name: "root",
|
||||
WorkspaceID: workspace.GetID(),
|
||||
Type: model.FileTypeFolder,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.fileRepo.GrantUserPermission(root.GetID(), userID, model.PermissionOwner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceRepo.UpdateRootID(workspace.GetID(), root.GetID()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if workspace, err = svc.workspaceRepo.Find(workspace.GetID()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceSearch.Index([]model.Workspace{workspace}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if root, err = svc.fileRepo.Find(root.GetID()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := svc.fileCache.Set(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceCache.Set(workspace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := svc.workspaceMapper.mapOne(workspace, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) Find(id string, userID string) (*Workspace, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspace, err := svc.workspaceCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceGuard.Authorize(user, workspace, model.PermissionViewer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := svc.workspaceMapper.mapOne(workspace, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) List(opts WorkspaceListOptions, userID string) (*WorkspaceList, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var authorized []model.Workspace
|
||||
if opts.Query == "" {
|
||||
ids, err := svc.workspaceRepo.GetIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorized, err = svc.doAuthorizationByIDs(ids, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
workspaces, err := svc.workspaceSearch.Query(opts.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorized, err = svc.doAuthorization(workspaces, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if opts.SortBy == "" {
|
||||
opts.SortBy = SortByDateCreated
|
||||
}
|
||||
if opts.SortOrder == "" {
|
||||
opts.SortOrder = SortOrderAsc
|
||||
}
|
||||
sorted := svc.doSorting(authorized, opts.SortBy, opts.SortOrder)
|
||||
paged, totalElements, totalPages := svc.doPagination(sorted, opts.Page, opts.Size)
|
||||
mapped, err := svc.workspaceMapper.mapMany(paged, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &WorkspaceList{
|
||||
Data: mapped,
|
||||
TotalPages: totalPages,
|
||||
TotalElements: totalElements,
|
||||
Page: opts.Page,
|
||||
Size: uint(len(mapped)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) UpdateName(id string, name string, userID string) (*Workspace, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspace, err := svc.workspaceCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceGuard.Authorize(user, workspace, model.PermissionEditor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if workspace, err = svc.workspaceRepo.UpdateName(id, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceSearch.Update([]model.Workspace{workspace}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceCache.Set(workspace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := svc.workspaceMapper.mapOne(workspace, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) UpdateStorageCapacity(id string, storageCapacity int64, userID string) (*Workspace, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspace, err := svc.workspaceCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceGuard.Authorize(user, workspace, model.PermissionEditor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size, err := svc.fileRepo.GetSize(workspace.GetRootID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if storageCapacity < size {
|
||||
return nil, errorpkg.NewInsufficientStorageCapacityError()
|
||||
}
|
||||
if workspace, err = svc.workspaceRepo.UpdateStorageCapacity(id, storageCapacity); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceSearch.Update([]model.Workspace{workspace}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = svc.workspaceCache.Set(workspace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := svc.workspaceMapper.mapOne(workspace, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) Delete(id string, userID string) error {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
workspace, err := svc.workspaceCache.Get(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = svc.workspaceGuard.Authorize(user, workspace, model.PermissionOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
if workspace, err = svc.workspaceRepo.Find(id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = svc.workspaceRepo.Delete(id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = svc.workspaceSearch.Delete([]string{workspace.GetID()}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = svc.workspaceCache.Delete(id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = svc.s3.RemoveBucket(workspace.GetBucket()); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) HasEnoughSpaceForByteSize(id string, byteSize int64) (bool, error) {
|
||||
workspace, err := svc.workspaceRepo.Find(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
root, err := svc.fileRepo.Find(workspace.GetRootID())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
usage, err := svc.fileRepo.GetSize(root.GetID())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
expectedUsage := usage + byteSize
|
||||
if expectedUsage > workspace.GetStorageCapacity() {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) findAll(userID string) ([]*Workspace, error) {
|
||||
user, err := svc.userRepo.Find(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids, err := svc.workspaceRepo.GetIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorized, err := svc.doAuthorizationByIDs(ids, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapped, err := svc.workspaceMapper.mapMany(authorized, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mapped, nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) doAuthorization(data []model.Workspace, user model.User) ([]model.Workspace, error) {
|
||||
var res []model.Workspace
|
||||
for _, w := range data {
|
||||
if svc.workspaceGuard.IsAuthorized(user, w, model.PermissionViewer) {
|
||||
res = append(res, w)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) doAuthorizationByIDs(ids []string, user model.User) ([]model.Workspace, error) {
|
||||
var res []model.Workspace
|
||||
for _, id := range ids {
|
||||
var w model.Workspace
|
||||
w, err := svc.workspaceCache.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if svc.workspaceGuard.IsAuthorized(user, w, model.PermissionViewer) {
|
||||
res = append(res, w)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) doSorting(data []model.Workspace, sortBy string, sortOrder string) []model.Workspace {
|
||||
if sortBy == SortByName {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if sortOrder == SortOrderDesc {
|
||||
return data[i].GetName() > data[j].GetName()
|
||||
} else {
|
||||
return data[i].GetName() < data[j].GetName()
|
||||
}
|
||||
})
|
||||
return data
|
||||
} else if sortBy == SortByDateCreated {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
a, _ := time.Parse(time.RFC3339, data[i].GetCreateTime())
|
||||
b, _ := time.Parse(time.RFC3339, data[j].GetCreateTime())
|
||||
if sortOrder == SortOrderDesc {
|
||||
return a.UnixMilli() > b.UnixMilli()
|
||||
} else {
|
||||
return a.UnixMilli() < b.UnixMilli()
|
||||
}
|
||||
})
|
||||
return data
|
||||
} else if sortBy == SortByDateModified {
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
if data[i].GetUpdateTime() != nil && data[j].GetUpdateTime() != nil {
|
||||
a, _ := time.Parse(time.RFC3339, *data[i].GetUpdateTime())
|
||||
b, _ := time.Parse(time.RFC3339, *data[j].GetUpdateTime())
|
||||
if sortOrder == SortOrderDesc {
|
||||
return a.UnixMilli() > b.UnixMilli()
|
||||
} else {
|
||||
return a.UnixMilli() < b.UnixMilli()
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
return data
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (svc *WorkspaceService) doPagination(data []model.Workspace, page, size uint) ([]model.Workspace, uint, uint) {
|
||||
totalElements := uint(len(data))
|
||||
totalPages := (totalElements + size - 1) / size
|
||||
if page > totalPages {
|
||||
return nil, totalElements, totalPages
|
||||
}
|
||||
startIndex := (page - 1) * size
|
||||
endIndex := startIndex + size
|
||||
if endIndex > totalElements {
|
||||
endIndex = totalElements
|
||||
}
|
||||
pageData := data[startIndex:endIndex]
|
||||
return pageData, totalElements, totalPages
|
||||
}
|
||||
|
||||
type workspaceMapper struct {
|
||||
orgCache *cache.OrganizationCache
|
||||
orgMapper *organizationMapper
|
||||
groupCache *cache.GroupCache
|
||||
}
|
||||
|
||||
func newWorkspaceMapper() *workspaceMapper {
|
||||
return &workspaceMapper{
|
||||
orgCache: cache.NewOrganizationCache(),
|
||||
orgMapper: newOrganizationMapper(),
|
||||
groupCache: cache.NewGroupCache(),
|
||||
}
|
||||
}
|
||||
|
||||
func (mp *workspaceMapper) mapOne(m model.Workspace, userID string) (*Workspace, error) {
|
||||
org, err := mp.orgCache.Get(m.GetOrganizationID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := mp.orgMapper.mapOne(org, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &Workspace{
|
||||
ID: m.GetID(),
|
||||
Name: m.GetName(),
|
||||
RootID: m.GetRootID(),
|
||||
StorageCapacity: m.GetStorageCapacity(),
|
||||
Organization: *v,
|
||||
CreateTime: m.GetCreateTime(),
|
||||
UpdateTime: m.GetUpdateTime(),
|
||||
}
|
||||
res.Permission = ""
|
||||
for _, p := range m.GetUserPermissions() {
|
||||
if p.GetUserID() == userID && model.GetPermissionWeight(p.GetValue()) > model.GetPermissionWeight(res.Permission) {
|
||||
res.Permission = p.GetValue()
|
||||
}
|
||||
}
|
||||
for _, p := range m.GetGroupPermissions() {
|
||||
g, err := mp.groupCache.Get(p.GetGroupID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, u := range g.GetUsers() {
|
||||
if u == userID && model.GetPermissionWeight(p.GetValue()) > model.GetPermissionWeight(res.Permission) {
|
||||
res.Permission = p.GetValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (mp *workspaceMapper) mapMany(workspaces []model.Workspace, userID string) ([]*Workspace, error) {
|
||||
res := make([]*Workspace, 0)
|
||||
for _, f := range workspaces {
|
||||
v, err := mp.mapOne(f, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, v)
|
||||
}
|
||||
return res, nil
|
||||
}
|
@@ -0,0 +1 @@
|
||||
subject: "Invitation to join a Voltaserve organization"
|
@@ -0,0 +1,41 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
.container {
|
||||
font-family: "IBM Plex Sans", sans-serif;
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
width: 580px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.link {
|
||||
color: #0c4cf3 !important;
|
||||
}
|
||||
.link:visited {
|
||||
color: #0c4cf3 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<p>Hello,</p>
|
||||
<p>
|
||||
You have been invited by <b>{{.USER_FULL_NAME}}</b> to join the
|
||||
organization <b>{{.ORGANIZATION_NAME}}</b>. Please follow this link to
|
||||
view your incoming invitations:
|
||||
</p>
|
||||
<p>
|
||||
<a class="link" href="{{.UI_URL}}/account/invitation">
|
||||
View incoming invitations
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,2 @@
|
||||
You have been invited by {{.USER_FULL_NAME}} to join the organization {{.ORGANIZATION_NAME}}.
|
||||
Please follow this link to sign up: {{.UI_URL}}/sign-up
|
@@ -0,0 +1 @@
|
||||
subject: "Invitation to join a Voltaserve organization"
|
@@ -0,0 +1,39 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
.container {
|
||||
font-family: "IBM Plex Sans", sans-serif;
|
||||
font-size: 14px;
|
||||
color: black;
|
||||
width: 580px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.link {
|
||||
color: #0c4cf3 !important;
|
||||
}
|
||||
.link:visited {
|
||||
color: #0c4cf3 !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<p>Hello,</p>
|
||||
<p>
|
||||
You have been invited by <b>{{.USER_FULL_NAME}}</b> to join the
|
||||
organization <b>{{.ORGANIZATION_NAME}}</b> in Voltaserve. Please follow
|
||||
this link to sign up:
|
||||
</p>
|
||||
<p>
|
||||
<a class="link" href="{{.UI_URL}}/sign-up">Sign up</a>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,2 @@
|
||||
You have been invited by {{.USER_FULL_NAME}} to join the organization {{.ORGANIZATION_NAME}}.
|
||||
Please follow this link to view your incoming invitations: {{.UI_URL}}/account/incoming-invitations
|
Reference in New Issue
Block a user