From 7e8011ba34d164bf4683a4657c9332b925cc727d Mon Sep 17 00:00:00 2001 From: boojack Date: Wed, 15 Feb 2023 22:54:46 +0800 Subject: [PATCH] chore: support deleting storage (#1095) --- api/system_setting.go | 15 +++++--- go.mod | 4 +-- go.sum | 6 ++-- plugin/storage/s3/s3.go | 6 ++-- server/auth.go | 3 +- server/memo.go | 9 +++-- server/resource.go | 30 +++++----------- server/storage.go | 15 ++++++++ server/system.go | 14 ++++---- store/system_setting.go | 5 +-- .../components/CreateStorageServiceDialog.tsx | 34 +++++++++++++++++-- 11 files changed, 86 insertions(+), 55 deletions(-) diff --git a/api/system_setting.go b/api/system_setting.go index 60545db3..79ed1794 100644 --- a/api/system_setting.go +++ b/api/system_setting.go @@ -25,8 +25,8 @@ const ( SystemSettingAdditionalScriptName SystemSettingName = "additionalScript" // SystemSettingCustomizedProfileName is the key type of customized server profile. SystemSettingCustomizedProfileName SystemSettingName = "customizedProfile" - // SystemSettingStorageServiceID is the key type of sotrage service ID. - SystemSettingStorageServiceID SystemSettingName = "storageServiceId" + // SystemSettingStorageServiceIDName is the key type of storage service ID. + SystemSettingStorageServiceIDName SystemSettingName = "storageServiceId" ) // CustomizedProfile is the struct definition for SystemSettingCustomizedProfileName system setting item. @@ -61,7 +61,7 @@ func (key SystemSettingName) String() string { return "additionalScript" case SystemSettingCustomizedProfileName: return "customizedProfile" - case SystemSettingStorageServiceID: + case SystemSettingStorageServiceIDName: return "storageServiceId" } return "" @@ -154,7 +154,12 @@ func (upsert SystemSettingUpsert) Validate() error { if !slices.Contains(UserSettingAppearanceValue, customizedProfile.Appearance) { return fmt.Errorf("invalid appearance value") } - } else if upsert.Name == SystemSettingStorageServiceID { + } else if upsert.Name == SystemSettingStorageServiceIDName { + value := 0 + err := json.Unmarshal([]byte(upsert.Value), &value) + if err != nil { + return fmt.Errorf("failed to unmarshal system setting storage service id value") + } return nil } else { return fmt.Errorf("invalid system setting name") @@ -164,5 +169,5 @@ func (upsert SystemSettingUpsert) Validate() error { } type SystemSettingFind struct { - Name *SystemSettingName `json:"name"` + Name SystemSettingName `json:"name"` } diff --git a/go.mod b/go.mod index 5c7da413..047e825b 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.20 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect @@ -63,7 +63,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.18.12 github.com/aws/aws-sdk-go-v2/credentials v1.13.12 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.51 - github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2 + github.com/aws/aws-sdk-go-v2/service/s3 v1.30.3 github.com/pkg/errors v0.9.1 github.com/segmentio/analytics-go v3.1.0+incompatible github.com/stretchr/testify v1.8.0 diff --git a/go.sum b/go.sum index c4544881..bbc17136 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,9 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKF github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19 h1:FGvpyTg2LKEmMrLlpjOgkoNp9XF5CGeyAyo33LdqZW8= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.19/go.mod h1:8W88sW3PjamQpKFUQvHWWKay6ARsNvZnzU7+a4apubw= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.20 h1:YIvKIfPXQVp0EhXUV644kmQo6cQPPSRmC44A1HSoJeg= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.20/go.mod h1:8W88sW3PjamQpKFUQvHWWKay6ARsNvZnzU7+a4apubw= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.23 h1:c5+bNdV8E4fIPteWx4HZSkqI07oY9exbfQ7JH7Yx4PI= @@ -26,8 +27,9 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFu github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22 h1:ISLJ2BKXe4zzyZ7mp5ewKECiw0U7KpLgS3S6OxY9Cm0= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22/go.mod h1:QFVbqK54XArazLvn2wvWMRBi/jGrWii46qbr5DyPGjc= -github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2 h1:5EQWIFO+Hc8E2hFcXQJ1vm6ufl/PMt/6RVRDZRju2vM= github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2/go.mod h1:SXDHd6fI2RhqB7vmAzyYQCTQnpZrIprVJvYxpzW3JAM= +github.com/aws/aws-sdk-go-v2/service/s3 v1.30.3 h1:PVieHTwugdlHedlxLpYLQsOZAq736RScuEb/m4zhzc4= +github.com/aws/aws-sdk-go-v2/service/s3 v1.30.3/go.mod h1:XN3YcdmnWYZ3Hrnojvo5p2mc/wfF973nkq3ClXPDMHk= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 h1:lQKN/LNa3qqu2cDOQZybP7oL4nMGGiFqob0jZJaR8/4= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 h1:0bLhH6DRAqox+g0LatcjGKjjhU6Eudyys6HB6DJVPj8= diff --git a/plugin/storage/s3/s3.go b/plugin/storage/s3/s3.go index 7fee14c6..3092a663 100644 --- a/plugin/storage/s3/s3.go +++ b/plugin/storage/s3/s3.go @@ -45,7 +45,7 @@ func NewClient(ctx context.Context, storage *api.Storage) (*Client, error) { }, nil } -func (client *Client) UploadFile(ctx context.Context, filename string, fileType string, src io.Reader, storage *api.Storage) (*string, error) { +func (client *Client) UploadFile(ctx context.Context, filename string, fileType string, src io.Reader, storage *api.Storage) (string, error) { uploader := manager.NewUploader(client.Client) resp, err := uploader.Upload(ctx, &awss3.PutObjectInput{ Bucket: aws.String(client.BucketName), @@ -55,7 +55,7 @@ func (client *Client) UploadFile(ctx context.Context, filename string, fileType ACL: types.ObjectCannedACL(*aws.String("public-read")), }) if err != nil { - return nil, err + return "", err } var link string if storage.URLPrefix == "" { @@ -63,5 +63,5 @@ func (client *Client) UploadFile(ctx context.Context, filename string, fileType } else { link = fmt.Sprintf("%s/%s", storage.URLPrefix, filename) } - return &link, nil + return link, nil } diff --git a/server/auth.go b/server/auth.go index 5af8d617..5a881f4a 100644 --- a/server/auth.go +++ b/server/auth.go @@ -81,9 +81,8 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { // Change the default role to host if there is no host user. userCreate.Role = api.Host } else { - systemSettingAllowSignUpName := api.SystemSettingAllowSignUpName allowSignUpSetting, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{ - Name: &systemSettingAllowSignUpName, + Name: api.SystemSettingAllowSignUpName, }) if err != nil && common.ErrorCode(err) != common.NotFound { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find system setting").SetInternal(err) diff --git a/server/memo.go b/server/memo.go index f0e7e667..503afa0a 100644 --- a/server/memo.go +++ b/server/memo.go @@ -54,20 +54,19 @@ func (s *Server) registerMemoRoutes(g *echo.Group) { } // Find system settings - disablePublicMemosSystemSettingKey := api.SystemSettingDisablePublicMemosName disablePublicMemosSystemSetting, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{ - Name: &disablePublicMemosSystemSettingKey, + Name: api.SystemSettingDisablePublicMemosName, }) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find system setting").SetInternal(err) } if disablePublicMemosSystemSetting != nil { - disablePublicMemosValue := false - err = json.Unmarshal([]byte(disablePublicMemosSystemSetting.Value), &disablePublicMemosValue) + disablePublicMemos := false + err = json.Unmarshal([]byte(disablePublicMemosSystemSetting.Value), &disablePublicMemos) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to unmarshal system setting").SetInternal(err) } - if disablePublicMemosValue { + if disablePublicMemos { memoCreate.Visibility = api.Private } } diff --git a/server/resource.go b/server/resource.go index 8b5c5fd8..063e3dd1 100644 --- a/server/resource.go +++ b/server/resource.go @@ -85,26 +85,20 @@ func (s *Server) registerResourceRoutes(g *echo.Group) { } defer src.Close() - var resourceCreate *api.ResourceCreate - systemSettingStorageServiceName := api.SystemSettingStorageServiceID - systemSetting, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{Name: &systemSettingStorageServiceName}) + systemSetting, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{Name: api.SystemSettingStorageServiceIDName}) if err != nil && common.ErrorCode(err) != common.NotFound { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find storage").SetInternal(err) } - storeLocal := false - if common.ErrorCode(err) == common.NotFound { - storeLocal = true - } else { - var value int - err = json.Unmarshal([]byte(systemSetting.Value), &value) + storageServiceID := 0 + if systemSetting != nil { + err = json.Unmarshal([]byte(systemSetting.Value), &storageServiceID) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to unmarshal storage service id").SetInternal(err) } - if value == 0 { - storeLocal = true - } } - if storeLocal { + + var resourceCreate *api.ResourceCreate + if storageServiceID == 0 { fileBytes, err := io.ReadAll(src) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to read file").SetInternal(err) @@ -117,12 +111,7 @@ func (s *Server) registerResourceRoutes(g *echo.Group) { Blob: fileBytes, } } else { - storageID, err := strconv.Atoi(systemSetting.Value) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to convert storageID").SetInternal(err) - } - - storage, err := s.Store.FindStorage(ctx, &api.StorageFind{ID: &storageID}) + storage, err := s.Store.FindStorage(ctx, &api.StorageFind{ID: &storageServiceID}) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find storage").SetInternal(err) } @@ -136,12 +125,11 @@ func (s *Server) registerResourceRoutes(g *echo.Group) { if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upload via s3 client").SetInternal(err) } - resourceCreate = &api.ResourceCreate{ CreatorID: userID, Filename: filename, Type: filetype, - ExternalLink: *link, + ExternalLink: link, } } diff --git a/server/storage.go b/server/storage.go index 9c77f1fd..bf6191ea 100644 --- a/server/storage.go +++ b/server/storage.go @@ -117,6 +117,21 @@ func (s *Server) registerStorageRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("storageId"))).SetInternal(err) } + systemSetting, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{Name: api.SystemSettingStorageServiceIDName}) + if err != nil && common.ErrorCode(err) != common.NotFound { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find storage").SetInternal(err) + } + if systemSetting != nil { + storageServiceID := 0 + err = json.Unmarshal([]byte(systemSetting.Value), &storageServiceID) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to unmarshal storage service id").SetInternal(err) + } + if storageServiceID == storageID { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Storage service %d is using", storageID)) + } + } + storage, err := s.Store.FindStorage(ctx, &api.StorageFind{ ID: &storageID, }) diff --git a/server/system.go b/server/system.go index 7096f70a..3be8fc03 100644 --- a/server/system.go +++ b/server/system.go @@ -104,8 +104,8 @@ func (s *Server) registerSystemRoutes(g *echo.Group) { if v := valueMap["externalUrl"]; v != nil { systemStatus.CustomizedProfile.ExternalURL = v.(string) } - } else if systemSetting.Name == api.SystemSettingStorageServiceID { - systemStatus.StorageServiceID = int(value.(float64)) + } else if systemSetting.Name == api.SystemSettingStorageServiceIDName { + systemStatus.StorageServiceID = value.(int) } } @@ -209,16 +209,15 @@ func (s *Server) registerSystemRoutes(g *echo.Group) { } func (s *Server) getSystemServerID(ctx context.Context) (string, error) { - serverIDKey := api.SystemSettingServerID serverIDValue, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{ - Name: &serverIDKey, + Name: api.SystemSettingServerID, }) if err != nil && common.ErrorCode(err) != common.NotFound { return "", err } if serverIDValue == nil || serverIDValue.Value == "" { serverIDValue, err = s.Store.UpsertSystemSetting(ctx, &api.SystemSettingUpsert{ - Name: serverIDKey, + Name: api.SystemSettingServerID, Value: uuid.NewString(), }) if err != nil { @@ -229,16 +228,15 @@ func (s *Server) getSystemServerID(ctx context.Context) (string, error) { } func (s *Server) getSystemSecretSessionName(ctx context.Context) (string, error) { - secretSessionNameKey := api.SystemSettingSecretSessionName secretSessionNameValue, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{ - Name: &secretSessionNameKey, + Name: api.SystemSettingSecretSessionName, }) if err != nil && common.ErrorCode(err) != common.NotFound { return "", err } if secretSessionNameValue == nil || secretSessionNameValue.Value == "" { secretSessionNameValue, err = s.Store.UpsertSystemSetting(ctx, &api.SystemSettingUpsert{ - Name: secretSessionNameKey, + Name: api.SystemSettingSecretSessionName, Value: uuid.NewString(), }) if err != nil { diff --git a/store/system_setting.go b/store/system_setting.go index 0302ac3b..21841acb 100644 --- a/store/system_setting.go +++ b/store/system_setting.go @@ -114,10 +114,7 @@ func upsertSystemSetting(ctx context.Context, tx *sql.Tx, upsert *api.SystemSett func findSystemSettingList(ctx context.Context, tx *sql.Tx, find *api.SystemSettingFind) ([]*systemSettingRaw, error) { where, args := []string{"1 = 1"}, []interface{}{} - - if v := find.Name; v != nil { - where, args = append(where, "name = ?"), append(args, v.String()) - } + where, args = append(where, "name = ?"), append(args, find.Name.String()) query := ` SELECT diff --git a/web/src/components/CreateStorageServiceDialog.tsx b/web/src/components/CreateStorageServiceDialog.tsx index 4664ef22..be27b832 100644 --- a/web/src/components/CreateStorageServiceDialog.tsx +++ b/web/src/components/CreateStorageServiceDialog.tsx @@ -1,10 +1,11 @@ +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import Icon from "./Icon"; -import { generateDialog } from "./Dialog"; import { Button, Input, Typography } from "@mui/joy"; -import { useEffect, useState } from "react"; import { useStorageStore } from "../store/module"; +import { generateDialog } from "./Dialog"; +import Icon from "./Icon"; import toastHelper from "./Toast"; +import { showCommonDialog } from "./Dialog/CommonDialog"; interface Props extends DialogProps { storage?: Storage; @@ -66,6 +67,28 @@ const CreateStorageServiceDialog: React.FC = (props: Props) => { destroy(); }; + const handleDeleteBtnClick = async () => { + if (isCreating) { + return; + } + + showCommonDialog({ + title: t("setting.storage-section.delete-storage"), + content: t("setting.storage-section.warning-text"), + style: "warning", + dialogName: "delete-storage-dialog", + onConfirm: async () => { + try { + await storageStore.deleteStorageById(storage.id); + } catch (error: any) { + console.error(error); + toastHelper.error(error.response.data.message); + } + destroy(); + }, + }); + }; + const handleNameChange = (event: React.ChangeEvent) => { const name = event.target.value; setStorageCreate({ @@ -165,6 +188,11 @@ const CreateStorageServiceDialog: React.FC = (props: Props) => { + {!isCreating && ( + + )}