chore: remove resource public id (#1912)

* chore: remove resource public id

* chore: update
This commit is contained in:
boojack 2023-07-08 11:29:50 +08:00 committed by GitHub
parent 2157651d17
commit 7e391bd53d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 82 additions and 93 deletions

View File

@ -8,10 +8,10 @@ import (
"github.com/labstack/echo/v4"
"github.com/pkg/errors"
"github.com/usememos/memos/api/v1/auth"
"github.com/usememos/memos/common/util"
"github.com/usememos/memos/plugin/idp"
"github.com/usememos/memos/plugin/idp/oauth2"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store"
"golang.org/x/crypto/bcrypt"
)

View File

@ -10,8 +10,8 @@ import (
"github.com/golang-jwt/jwt/v4"
"github.com/labstack/echo/v4"
"github.com/pkg/errors"
"github.com/usememos/memos/api/v1/auth"
"github.com/usememos/memos/common/util"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store"
)

View File

@ -43,7 +43,6 @@ type Resource struct {
ExternalLink string `json:"externalLink"`
Type string `json:"type"`
Size int64 `json:"size"`
PublicID string `json:"publicId"`
// Related fields
LinkedMemoAmount int `json:"linkedMemoAmount"`
@ -54,7 +53,6 @@ type CreateResourceRequest struct {
InternalPath string `json:"internalPath"`
ExternalLink string `json:"externalLink"`
Type string `json:"type"`
PublicID string `json:"publicId"`
DownloadToLocal bool `json:"downloadToLocal"`
}
@ -62,12 +60,10 @@ type FindResourceRequest struct {
ID *int `json:"id"`
CreatorID *int `json:"creatorId"`
Filename *string `json:"filename"`
PublicID *string `json:"publicId"`
}
type UpdateResourceRequest struct {
Filename *string `json:"filename"`
ResetPublicID *bool `json:"resetPublicId"`
Filename *string `json:"filename"`
}
const (
@ -101,7 +97,6 @@ func (s *APIV1Service) registerResourceRoutes(g *echo.Group) {
Filename: request.Filename,
ExternalLink: request.ExternalLink,
Type: request.Type,
PublicID: util.GenUUID(),
}
if request.ExternalLink != "" {
// Only allow those external links scheme with http/https
@ -195,7 +190,6 @@ func (s *APIV1Service) registerResourceRoutes(g *echo.Group) {
}
defer sourceFile.Close()
create := &store.Resource{}
systemSettingStorageServiceID, err := s.Store.GetSystemSetting(ctx, &store.FindSystemSetting{Name: SystemSettingStorageServiceIDName.String()})
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find storage").SetInternal(err)
@ -208,7 +202,7 @@ func (s *APIV1Service) registerResourceRoutes(g *echo.Group) {
}
}
publicID := util.GenUUID()
var create *store.Resource
if storageServiceID == DatabaseStorage {
fileBytes, err := io.ReadAll(sourceFile)
if err != nil {
@ -229,7 +223,7 @@ func (s *APIV1Service) registerResourceRoutes(g *echo.Group) {
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find local storage path setting").SetInternal(err)
}
localStoragePath := "assets/{publicid}"
localStoragePath := "assets/{filename}"
if systemSettingLocalStoragePath != nil && systemSettingLocalStoragePath.Value != "" {
err = json.Unmarshal([]byte(systemSettingLocalStoragePath.Value), &localStoragePath)
if err != nil {
@ -237,10 +231,10 @@ func (s *APIV1Service) registerResourceRoutes(g *echo.Group) {
}
}
filePath := filepath.FromSlash(localStoragePath)
if !strings.Contains(filePath, "{publicid}") {
filePath = filepath.Join(filePath, "{publicid}")
if !strings.Contains(filePath, "{filename}") {
filePath = filepath.Join(filePath, "{filename}")
}
filePath = filepath.Join(s.Profile.Data, replacePathTemplate(filePath, file.Filename, publicID+filepath.Ext(file.Filename)))
filePath = filepath.Join(s.Profile.Data, replacePathTemplate(filePath, file.Filename))
dir := filepath.Dir(filePath)
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
@ -292,10 +286,10 @@ func (s *APIV1Service) registerResourceRoutes(g *echo.Group) {
}
filePath := s3Config.Path
if !strings.Contains(filePath, "{publicid}") {
filePath = path.Join(filePath, "{publicid}")
if !strings.Contains(filePath, "{filename}") {
filePath = path.Join(filePath, "{filename}")
}
filePath = replacePathTemplate(filePath, file.Filename, publicID+filepath.Ext(file.Filename))
filePath = replacePathTemplate(filePath, file.Filename)
_, filename := filepath.Split(filePath)
link, err := s3Client.UploadFile(ctx, filePath, filetype, sourceFile)
if err != nil {
@ -313,7 +307,6 @@ func (s *APIV1Service) registerResourceRoutes(g *echo.Group) {
}
}
create.PublicID = publicID
resource, err := s.Store.CreateResource(ctx, create)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create resource").SetInternal(err)
@ -389,10 +382,6 @@ func (s *APIV1Service) registerResourceRoutes(g *echo.Group) {
if request.Filename != nil && *request.Filename != "" {
update.Filename = request.Filename
}
if request.ResetPublicID != nil && *request.ResetPublicID {
publicID := util.GenUUID()
update.PublicID = &publicID
}
resource, err = s.Store.UpdateResource(ctx, update)
if err != nil {
@ -431,7 +420,7 @@ func (s *APIV1Service) registerResourceRoutes(g *echo.Group) {
}
ext := filepath.Ext(resource.Filename)
thumbnailPath := path.Join(s.Profile.Data, thumbnailImagePath, fmt.Sprintf("%d-%s%s", resource.ID, resource.PublicID, ext))
thumbnailPath := path.Join(s.Profile.Data, thumbnailImagePath, fmt.Sprintf("%d%s", resource.ID, ext))
if err := os.Remove(thumbnailPath); err != nil {
log.Warn(fmt.Sprintf("failed to delete local thumbnail with path %s", thumbnailPath), zap.Error(err))
}
@ -496,7 +485,7 @@ func (s *APIV1Service) registerResourcePublicRoutes(g *echo.Group) {
if c.QueryParam("thumbnail") == "1" && util.HasPrefixes(resource.Type, "image/png", "image/jpeg") {
ext := filepath.Ext(resource.Filename)
thumbnailPath := path.Join(s.Profile.Data, thumbnailImagePath, fmt.Sprintf("%d-%s%s", resource.ID, resource.PublicID, ext))
thumbnailPath := path.Join(s.Profile.Data, thumbnailImagePath, fmt.Sprintf("%d%s", resource.ID, ext))
thumbnailBlob, err := getOrGenerateThumbnailImage(blob, thumbnailPath)
if err != nil {
log.Warn(fmt.Sprintf("failed to get or generate local thumbnail with path %s", thumbnailPath), zap.Error(err))
@ -516,8 +505,8 @@ func (s *APIV1Service) registerResourcePublicRoutes(g *echo.Group) {
}
return c.Stream(http.StatusOK, resourceType, bytes.NewReader(blob))
}
g.GET("/r/:resourceId", f)
g.GET("/r/:resourceId/", f)
g.GET("/r/:resourceId/*", f)
}
@ -543,12 +532,10 @@ func (s *APIV1Service) createResourceCreateActivity(ctx context.Context, resourc
return err
}
func replacePathTemplate(path, filename, publicID string) string {
func replacePathTemplate(path, filename string) string {
t := time.Now()
path = fileKeyPattern.ReplaceAllStringFunc(path, func(s string) string {
switch s {
case "{publicid}":
return publicID
case "{filename}":
return filename
case "{timestamp}":
@ -624,7 +611,7 @@ func checkResourceVisibility(ctx context.Context, s *store.Store, resourceID int
return store.Private, err
}
// If resource is belongs to no memo, it'll always PRIVATE
// If resource is belongs to no memo, it'll always PRIVATE.
if len(memoResources) == 0 {
return store.Private, nil
}
@ -640,7 +627,7 @@ func checkResourceVisibility(ctx context.Context, s *store.Store, resourceID int
var isProtected bool
for _, visibility := range visibilityList {
// If any memo is PUBLIC, resource do
// If any memo is PUBLIC, resource should be PUBLIC too.
if visibility == store.Public {
return store.Public, nil
}
@ -650,12 +637,10 @@ func checkResourceVisibility(ctx context.Context, s *store.Store, resourceID int
}
}
// If no memo is PUBLIC, but any memo is PROTECTED, resource do
if isProtected {
return store.Protected, nil
}
// If all memo is PRIVATE, the resource do
return store.Private, nil
}
@ -671,7 +656,6 @@ func convertResourceFromStore(resource *store.Resource) *Resource {
ExternalLink: resource.ExternalLink,
Type: resource.Type,
Size: resource.Size,
PublicID: resource.PublicID,
LinkedMemoAmount: resource.LinkedMemoAmount,
}
}

View File

@ -114,7 +114,7 @@ func (s *APIV1Service) generateRSSFromMemoList(ctx context.Context, memoList []*
if resource.ExternalLink != "" {
enclosure.Url = resource.ExternalLink
} else {
enclosure.Url = baseURL + "/o/r/" + strconv.Itoa(resource.ID) + "/" + resource.PublicID + "/" + resource.Filename
enclosure.Url = baseURL + "/o/r/" + strconv.Itoa(resource.ID)
}
enclosure.Length = strconv.Itoa(int(resource.Size))
enclosure.Type = resource.Type

View File

@ -9,7 +9,6 @@ import (
"github.com/pkg/errors"
apiv1 "github.com/usememos/memos/api/v1"
"github.com/usememos/memos/common/util"
"github.com/usememos/memos/plugin/telegram"
"github.com/usememos/memos/store"
)
@ -94,7 +93,6 @@ func (t *telegramHandler) MessageHandle(ctx context.Context, bot *telegram.Bot,
Type: mime,
Size: int64(len(blob)),
Blob: blob,
PublicID: util.GenUUID(),
})
if err != nil {
_, err := bot.EditMessage(ctx, message.Chat.ID, reply.MessageID, fmt.Sprintf("failed to CreateResource: %s", err), nil)

View File

@ -76,9 +76,7 @@ CREATE TABLE resource (
external_link TEXT NOT NULL DEFAULT '',
type TEXT NOT NULL DEFAULT '',
size INTEGER NOT NULL DEFAULT 0,
internal_path TEXT NOT NULL DEFAULT '',
public_id TEXT NOT NULL DEFAULT '',
UNIQUE(id, public_id)
internal_path TEXT NOT NULL DEFAULT ''
);
-- memo_resource

View File

@ -0,0 +1,25 @@
DROP TABLE IF EXISTS resource_temp;
CREATE TABLE resource_temp (
id INTEGER PRIMARY KEY AUTOINCREMENT,
creator_id INTEGER NOT NULL,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
filename TEXT NOT NULL DEFAULT '',
blob BLOB DEFAULT NULL,
external_link TEXT NOT NULL DEFAULT '',
type TEXT NOT NULL DEFAULT '',
size INTEGER NOT NULL DEFAULT 0,
internal_path TEXT NOT NULL DEFAULT ''
);
INSERT INTO
resource_temp (id, creator_id, created_ts, updated_ts, filename, blob, external_link, type, size, internal_path)
SELECT
id, creator_id, created_ts, updated_ts, filename, blob, external_link, type, size, internal_path
FROM
resource;
DROP TABLE resource;
ALTER TABLE resource_temp RENAME TO resource;

View File

@ -76,9 +76,7 @@ CREATE TABLE resource (
external_link TEXT NOT NULL DEFAULT '',
type TEXT NOT NULL DEFAULT '',
size INTEGER NOT NULL DEFAULT 0,
internal_path TEXT NOT NULL DEFAULT '',
public_id TEXT NOT NULL DEFAULT '',
UNIQUE(id, public_id)
internal_path TEXT NOT NULL DEFAULT ''
);
-- memo_resource

View File

@ -22,7 +22,6 @@ type Resource struct {
ExternalLink string
Type string
Size int64
PublicID string
LinkedMemoAmount int
}
@ -32,7 +31,6 @@ type FindResource struct {
CreatorID *int
Filename *string
MemoID *int
PublicID *string
Limit *int
Offset *int
}
@ -41,7 +39,6 @@ type UpdateResource struct {
ID int
UpdatedTs *int64
Filename *string
PublicID *string
}
type DeleteResource struct {
@ -63,13 +60,12 @@ func (s *Store) CreateResource(ctx context.Context, create *Resource) (*Resource
type,
size,
creator_id,
internal_path,
public_id
internal_path
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?)
RETURNING id, created_ts, updated_ts
`,
create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.PublicID,
create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath,
).Scan(&create.ID, &create.CreatedTs, &create.UpdatedTs); err != nil {
return nil, err
}
@ -98,10 +94,6 @@ func (s *Store) ListResources(ctx context.Context, find *FindResource) ([]*Resou
return nil, err
}
if err := tx.Commit(); err != nil {
return nil, err
}
return resources, nil
}
@ -143,12 +135,9 @@ func (s *Store) UpdateResource(ctx context.Context, update *UpdateResource) (*Re
if v := update.Filename; v != nil {
set, args = append(set, "filename = ?"), append(args, *v)
}
if v := update.PublicID; v != nil {
set, args = append(set, "public_id = ?"), append(args, *v)
}
args = append(args, update.ID)
fields := []string{"id", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path", "public_id"}
fields := []string{"id", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path"}
query := `
UPDATE resource
SET ` + strings.Join(set, ", ") + `
@ -165,7 +154,6 @@ func (s *Store) UpdateResource(ctx context.Context, update *UpdateResource) (*Re
&resource.CreatedTs,
&resource.UpdatedTs,
&resource.InternalPath,
&resource.PublicID,
}
if err := tx.QueryRowContext(ctx, query, args...).Scan(dests...); err != nil {
return nil, err
@ -215,11 +203,8 @@ func listResources(ctx context.Context, tx *sql.Tx, find *FindResource) ([]*Reso
if v := find.MemoID; v != nil {
where, args = append(where, "resource.id in (SELECT resource_id FROM memo_resource WHERE memo_id = ?)"), append(args, *v)
}
if v := find.PublicID; v != nil {
where, args = append(where, "resource.public_id = ?"), append(args, *v)
}
fields := []string{"resource.id", "resource.filename", "resource.external_link", "resource.type", "resource.size", "resource.creator_id", "resource.created_ts", "resource.updated_ts", "internal_path", "public_id"}
fields := []string{"resource.id", "resource.filename", "resource.external_link", "resource.type", "resource.size", "resource.creator_id", "resource.created_ts", "resource.updated_ts", "internal_path"}
if find.GetBlob {
fields = append(fields, "resource.blob")
}
@ -261,7 +246,6 @@ func listResources(ctx context.Context, tx *sql.Tx, find *FindResource) ([]*Reso
&resource.CreatedTs,
&resource.UpdatedTs,
&resource.InternalPath,
&resource.PublicID,
}
if find.GetBlob {
dests = append(dests, &resource.Blob)

View File

@ -19,7 +19,6 @@ func TestResourceStore(t *testing.T) {
ExternalLink: "",
Type: "application/epub+zip",
Size: 637607,
PublicID: "a02748e2-9b56-46b2-8b1f-72d686d52f77",
})
require.NoError(t, err)

View File

@ -0,0 +1,26 @@
import { Tooltip } from "@mui/joy";
import Icon from "./Icon";
import { useTranslation } from "react-i18next";
interface Props {
className?: string;
url: string;
title?: string;
}
const LearnMore: React.FC<Props> = (props: Props) => {
const { className, url, title } = props;
const { t } = useTranslation();
return (
<>
<Tooltip title={title ?? t("common.learn-more")} placement="top">
<a className={`text-gray-500 dark:text-gray-400 hover:text-blue-600 ${className}`} href={url} target="_blank">
<Icon.ExternalLink className="w-4 h-auto" />
</a>
</Tooltip>
</>
);
};
export default LearnMore;

View File

@ -37,21 +37,6 @@ const ResourceItemDropdown = ({ resource }: Props) => {
toast.success(t("message.succeed-copy-resource-link"));
};
const handleResetResourceLinkBtnClick = (resource: Resource) => {
showCommonDialog({
title: t("resource.reset-resource-link"),
content: t("resource.reset-link-prompt"),
style: "warning",
dialogName: "reset-resource-link-dialog",
onConfirm: async () => {
await resourceStore.patchResource({
id: resource.id,
resetPublicId: true,
});
},
});
};
const handleRenameBtnClick = (resource: Resource) => {
showChangeResourceFilenameDialog(resource.id, resource.filename);
};
@ -91,12 +76,6 @@ const ResourceItemDropdown = ({ resource }: Props) => {
>
{t("resource.copy-link")}
</button>
<button
className="w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick={() => handleResetResourceLinkBtnClick(resource)}
>
{t("resource.reset-link")}
</button>
<button
className="w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick={() => handleRenameBtnClick(resource)}

View File

@ -1,12 +1,12 @@
import { Button, Input } from "@mui/joy";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "react-hot-toast";
import { useGlobalStore } from "@/store/module";
import * as api from "@/helpers/api";
import { generateDialog } from "./Dialog";
import Icon from "./Icon";
import HelpButton from "./kit/HelpButton";
import { useTranslation } from "react-i18next";
import LearnMore from "./LearnMore";
interface Props extends DialogProps {
localStoragePath?: string;
@ -51,8 +51,8 @@ const UpdateLocalStorageDialog: React.FC<Props> = (props: Props) => {
<div className="dialog-content-container max-w-xs">
<p className="text-sm break-words mb-1">{t("setting.storage-section.update-local-path-description")}</p>
<div className="flex flex-row">
<p className="text-sm text-gray-400 mb-2 break-all">e.g. {"assets/{publicid}"}</p>
<HelpButton hint={t("common.learn-more")} url="https://usememos.com/docs/local-storage" />
<p className="text-sm text-gray-400 mb-2 break-all">e.g. {"assets/{filename}"}</p>
<LearnMore url="https://usememos.com/docs/local-storage" />
</div>
<Input
className="mb-2"

View File

@ -214,7 +214,7 @@ const ResourcesDashboard = () => {
<div className="w-full flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-700 text-black dark:text-gray-300">
<div className="relative w-full flex flex-row justify-between items-center">
<p className="flex flex-row justify-start items-center select-none rounded">
<Icon.Paperclip className="w-5 h-auto mr-1" /> {t("common.resources")}
<Icon.Paperclip className="w-5 h-auto mr-1 ml-2" /> {t("common.resources")}
</p>
<ResourceSearchBar setQuery={handleSearchResourceInputChange} />
</div>

View File

@ -10,7 +10,6 @@ interface Resource {
externalLink: string;
type: string;
size: string;
publicId: string;
linkedMemoAmount: number;
}
@ -25,7 +24,6 @@ interface ResourceCreate {
interface ResourcePatch {
id: ResourceId;
filename?: string;
resetPublicId?: boolean;
}
interface ResourceFind {

View File

@ -3,5 +3,5 @@ export const getResourceUrl = (resource: Resource, withOrigin = true) => {
return resource.externalLink;
}
return `${withOrigin ? window.location.origin : ""}/o/r/${resource.id}/${resource.publicId}/${encodeURIComponent(resource.filename)}`;
return `${withOrigin ? window.location.origin : ""}/o/r/${resource.id}`;
};