feat: add user-defined id to resource

This commit is contained in:
Steven 2024-01-21 10:49:30 +08:00
parent 40bd75c725
commit 582cc6609c
12 changed files with 100 additions and 106 deletions

View File

@ -15,6 +15,7 @@ import (
"time" "time"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/lithammer/shortuuid/v4"
"github.com/pkg/errors" "github.com/pkg/errors"
"go.uber.org/zap" "go.uber.org/zap"
@ -139,6 +140,7 @@ func (s *APIV1Service) CreateResource(c echo.Context) error {
} }
create := &store.Resource{ create := &store.Resource{
ResourceName: shortuuid.New(),
CreatorID: userID, CreatorID: userID,
Filename: request.Filename, Filename: request.Filename,
ExternalLink: request.ExternalLink, ExternalLink: request.ExternalLink,
@ -215,10 +217,11 @@ func (s *APIV1Service) UploadResource(c echo.Context) error {
defer sourceFile.Close() defer sourceFile.Close()
create := &store.Resource{ create := &store.Resource{
CreatorID: userID, ResourceName: shortuuid.New(),
Filename: file.Filename, CreatorID: userID,
Type: file.Header.Get("Content-Type"), Filename: file.Filename,
Size: file.Size, Type: file.Header.Get("Content-Type"),
Size: file.Size,
} }
err = SaveResourceBlob(ctx, s.Store, create, sourceFile) err = SaveResourceBlob(ctx, s.Store, create, sourceFile)
if err != nil { if err != nil {

View File

@ -5,6 +5,7 @@ import (
"net/url" "net/url"
"time" "time"
"github.com/lithammer/shortuuid/v4"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
@ -30,6 +31,7 @@ func (s *APIV2Service) CreateResource(ctx context.Context, request *apiv2pb.Crea
} }
create := &store.Resource{ create := &store.Resource{
ResourceName: shortuuid.New(),
CreatorID: user.ID, CreatorID: user.ID,
Filename: request.Filename, Filename: request.Filename,
ExternalLink: request.ExternalLink, ExternalLink: request.ExternalLink,

View File

@ -86,11 +86,12 @@ func (t *TelegramHandler) MessageHandle(ctx context.Context, bot *telegram.Bot,
for _, attachment := range attachments { for _, attachment := range attachments {
// Fill the common field of create // Fill the common field of create
create := store.Resource{ create := store.Resource{
CreatorID: creatorID, ResourceName: shortuuid.New(),
Filename: filepath.Base(attachment.FileName), CreatorID: creatorID,
Type: attachment.GetMimeType(), Filename: filepath.Base(attachment.FileName),
Size: attachment.FileSize, Type: attachment.GetMimeType(),
MemoID: &memoMessage.ID, Size: attachment.FileSize,
MemoID: &memoMessage.ID,
} }
err := apiv1.SaveResourceBlob(ctx, t.store, &create, bytes.NewReader(attachment.Data)) err := apiv1.SaveResourceBlob(ctx, t.store, &create, bytes.NewReader(attachment.Data))

View File

@ -64,6 +64,7 @@ CREATE TABLE `memo_relation` (
-- resource -- resource
CREATE TABLE `resource` ( CREATE TABLE `resource` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`resource_name` VARCHAR(256) NOT NULL UNIQUE,
`creator_id` INT NOT NULL, `creator_id` INT NOT NULL,
`created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

View File

@ -6,15 +6,13 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/pkg/errors"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) { func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) {
fields := []string{"`filename`", "`blob`", "`external_link`", "`type`", "`size`", "`creator_id`", "`internal_path`", "`memo_id`"} fields := []string{"`resource_name`", "`filename`", "`blob`", "`external_link`", "`type`", "`size`", "`creator_id`", "`internal_path`", "`memo_id`"}
placeholder := []string{"?", "?", "?", "?", "?", "?", "?", "?"} placeholder := []string{"?", "?", "?", "?", "?", "?", "?", "?", "?"}
args := []any{create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.MemoID} args := []any{create.ResourceName, create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.MemoID}
stmt := "INSERT INTO `resource` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ")" stmt := "INSERT INTO `resource` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ")"
result, err := d.db.ExecContext(ctx, stmt, args...) result, err := d.db.ExecContext(ctx, stmt, args...)
@ -28,15 +26,7 @@ func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store
} }
id32 := int32(id) id32 := int32(id)
list, err := d.ListResources(ctx, &store.FindResource{ID: &id32}) return d.GetResource(ctx, &store.FindResource{ID: &id32})
if err != nil {
return nil, err
}
if len(list) != 1 {
return nil, errors.Wrapf(nil, "unexpected resource count: %d", len(list))
}
return list[0], nil
} }
func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*store.Resource, error) { func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*store.Resource, error) {
@ -45,6 +35,9 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
if v := find.ID; v != nil { if v := find.ID; v != nil {
where, args = append(where, "`id` = ?"), append(args, *v) where, args = append(where, "`id` = ?"), append(args, *v)
} }
if v := find.ResourceName; v != nil {
where, args = append(where, "`resource_name` = ?"), append(args, *v)
}
if v := find.CreatorID; v != nil { if v := find.CreatorID; v != nil {
where, args = append(where, "`creator_id` = ?"), append(args, *v) where, args = append(where, "`creator_id` = ?"), append(args, *v)
} }
@ -58,7 +51,7 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
where = append(where, "`memo_id` IS NOT NULL") where = append(where, "`memo_id` IS NOT NULL")
} }
fields := []string{"`id`", "`filename`", "`external_link`", "`type`", "`size`", "`creator_id`", "UNIX_TIMESTAMP(`created_ts`)", "UNIX_TIMESTAMP(`updated_ts`)", "`internal_path`", "`memo_id`"} fields := []string{"`id`", "`resource_name`", "`filename`", "`external_link`", "`type`", "`size`", "`creator_id`", "UNIX_TIMESTAMP(`created_ts`)", "UNIX_TIMESTAMP(`updated_ts`)", "`internal_path`", "`memo_id`"}
if find.GetBlob { if find.GetBlob {
fields = append(fields, "`blob`") fields = append(fields, "`blob`")
} }
@ -83,6 +76,7 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
var memoID sql.NullInt32 var memoID sql.NullInt32
dests := []any{ dests := []any{
&resource.ID, &resource.ID,
&resource.ResourceName,
&resource.Filename, &resource.Filename,
&resource.ExternalLink, &resource.ExternalLink,
&resource.Type, &resource.Type,
@ -112,9 +106,24 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
return list, nil return list, nil
} }
func (d *DB) GetResource(ctx context.Context, find *store.FindResource) (*store.Resource, error) {
list, err := d.ListResources(ctx, find)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, nil
}
return list[0], nil
}
func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) { func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) {
set, args := []string{}, []any{} set, args := []string{}, []any{}
if v := update.ResourceName; v != nil {
set, args = append(set, "`resource_name` = ?"), append(args, *v)
}
if v := update.UpdatedTs; v != nil { if v := update.UpdatedTs; v != nil {
set, args = append(set, "`updated_ts` = FROM_UNIXTIME(?)"), append(args, *v) set, args = append(set, "`updated_ts` = FROM_UNIXTIME(?)"), append(args, *v)
} }
@ -137,15 +146,7 @@ func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (
return nil, err return nil, err
} }
list, err := d.ListResources(ctx, &store.FindResource{ID: &update.ID}) return d.GetResource(ctx, &store.FindResource{ID: &update.ID})
if err != nil {
return nil, err
}
if len(list) != 1 {
return nil, errors.Wrapf(nil, "unexpected resource count: %d", len(list))
}
return list[0], nil
} }
func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error { func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error {

View File

@ -64,6 +64,7 @@ CREATE TABLE memo_relation (
-- resource -- resource
CREATE TABLE resource ( CREATE TABLE resource (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
resource_name TEXT NOT NULL UNIQUE,
creator_id INTEGER NOT NULL, creator_id INTEGER NOT NULL,
created_ts BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()), created_ts BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()),
updated_ts BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()), updated_ts BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()),

View File

@ -10,8 +10,8 @@ import (
) )
func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) { func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) {
fields := []string{"filename", "blob", "external_link", "type", "size", "creator_id", "internal_path", "memo_id"} fields := []string{"resource_name", "filename", "blob", "external_link", "type", "size", "creator_id", "internal_path", "memo_id"}
args := []any{create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.MemoID} args := []any{create.ResourceName, create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.MemoID}
stmt := "INSERT INTO resource (" + strings.Join(fields, ", ") + ") VALUES (" + placeholders(len(args)) + ") RETURNING id, created_ts, updated_ts" stmt := "INSERT INTO resource (" + strings.Join(fields, ", ") + ") VALUES (" + placeholders(len(args)) + ") RETURNING id, created_ts, updated_ts"
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(&create.ID, &create.CreatedTs, &create.UpdatedTs); err != nil { if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(&create.ID, &create.CreatedTs, &create.UpdatedTs); err != nil {
@ -26,6 +26,9 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
if v := find.ID; v != nil { if v := find.ID; v != nil {
where, args = append(where, "id = "+placeholder(len(args)+1)), append(args, *v) where, args = append(where, "id = "+placeholder(len(args)+1)), append(args, *v)
} }
if v := find.ResourceName; v != nil {
where, args = append(where, "resource_name = "+placeholder(len(args)+1)), append(args, *v)
}
if v := find.CreatorID; v != nil { if v := find.CreatorID; v != nil {
where, args = append(where, "creator_id = "+placeholder(len(args)+1)), append(args, *v) where, args = append(where, "creator_id = "+placeholder(len(args)+1)), append(args, *v)
} }
@ -39,7 +42,7 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
where = append(where, "memo_id IS NOT NULL") where = append(where, "memo_id IS NOT NULL")
} }
fields := []string{"id", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path", "memo_id"} fields := []string{"id", "resource_name", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path", "memo_id"}
if find.GetBlob { if find.GetBlob {
fields = append(fields, "blob") fields = append(fields, "blob")
} }
@ -70,6 +73,7 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
var memoID sql.NullInt32 var memoID sql.NullInt32
dests := []any{ dests := []any{
&resource.ID, &resource.ID,
&resource.ResourceName,
&resource.Filename, &resource.Filename,
&resource.ExternalLink, &resource.ExternalLink,
&resource.Type, &resource.Type,
@ -102,6 +106,9 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) { func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) {
set, args := []string{}, []any{} set, args := []string{}, []any{}
if v := update.ResourceName; v != nil {
set, args = append(set, "resource_name = "+placeholder(len(args)+1)), append(args, *v)
}
if v := update.UpdatedTs; v != nil { if v := update.UpdatedTs; v != nil {
set, args = append(set, "updated_ts = "+placeholder(len(args)+1)), append(args, *v) set, args = append(set, "updated_ts = "+placeholder(len(args)+1)), append(args, *v)
} }
@ -118,16 +125,13 @@ func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (
set, args = append(set, "blob = "+placeholder(len(args)+1)), append(args, v) set, args = append(set, "blob = "+placeholder(len(args)+1)), append(args, v)
} }
fields := []string{"id", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path"} fields := []string{"id", "resource_name", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path"}
stmt := ` stmt := `UPDATE resource SET ` + strings.Join(set, ", ") + ` WHERE id = ` + placeholder(len(args)+1) + ` RETURNING ` + strings.Join(fields, ", ")
UPDATE resource
SET ` + strings.Join(set, ", ") + `
WHERE id = ` + placeholder(len(args)+1) + `
RETURNING ` + strings.Join(fields, ", ")
args = append(args, update.ID) args = append(args, update.ID)
resource := store.Resource{} resource := store.Resource{}
dests := []any{ dests := []any{
&resource.ID, &resource.ID,
&resource.ResourceName,
&resource.Filename, &resource.Filename,
&resource.ExternalLink, &resource.ExternalLink,
&resource.Type, &resource.Type,
@ -157,11 +161,7 @@ func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) e
} }
func vacuumResource(ctx context.Context, tx *sql.Tx) error { func vacuumResource(ctx context.Context, tx *sql.Tx) error {
stmt := ` stmt := `DELETE FROM resource WHERE creator_id NOT IN (SELECT id FROM "user")`
DELETE FROM
resource
WHERE
creator_id NOT IN (SELECT id FROM "user")`
_, err := tx.ExecContext(ctx, stmt) _, err := tx.ExecContext(ctx, stmt)
if err != nil { if err != nil {
return err return err

View File

@ -1,19 +1,3 @@
-- drop all tables first
DROP TABLE IF EXISTS migration_history;
DROP TABLE IF EXISTS system_setting;
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS user_setting;
DROP TABLE IF EXISTS memo;
DROP TABLE IF EXISTS memo_organizer;
DROP TABLE IF EXISTS memo_relation;
DROP TABLE IF EXISTS resource;
DROP TABLE IF EXISTS tag;
DROP TABLE IF EXISTS activity;
DROP TABLE IF EXISTS storage;
DROP TABLE IF EXISTS idp;
DROP TABLE IF EXISTS inbox;
DROP TABLE IF EXISTS webhook;
-- migration_history -- migration_history
CREATE TABLE migration_history ( CREATE TABLE migration_history (
version TEXT NOT NULL PRIMARY KEY, version TEXT NOT NULL PRIMARY KEY,
@ -87,6 +71,7 @@ CREATE TABLE memo_relation (
-- resource -- resource
CREATE TABLE resource ( CREATE TABLE resource (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
resource_name TEXT NOT NULL UNIQUE,
creator_id INTEGER NOT NULL, creator_id INTEGER NOT NULL,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')), updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),

View File

@ -10,9 +10,9 @@ import (
) )
func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) { func (d *DB) CreateResource(ctx context.Context, create *store.Resource) (*store.Resource, error) {
fields := []string{"`filename`", "`blob`", "`external_link`", "`type`", "`size`", "`creator_id`", "`internal_path`", "`memo_id`"} fields := []string{"`resource_name`", "`filename`", "`blob`", "`external_link`", "`type`", "`size`", "`creator_id`", "`internal_path`", "`memo_id`"}
placeholder := []string{"?", "?", "?", "?", "?", "?", "?", "?"} placeholder := []string{"?", "?", "?", "?", "?", "?", "?", "?", "?"}
args := []any{create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.MemoID} args := []any{create.ResourceName, create.Filename, create.Blob, create.ExternalLink, create.Type, create.Size, create.CreatorID, create.InternalPath, create.MemoID}
stmt := "INSERT INTO `resource` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ") RETURNING `id`, `created_ts`, `updated_ts`" stmt := "INSERT INTO `resource` (" + strings.Join(fields, ", ") + ") VALUES (" + strings.Join(placeholder, ", ") + ") RETURNING `id`, `created_ts`, `updated_ts`"
if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(&create.ID, &create.CreatedTs, &create.UpdatedTs); err != nil { if err := d.db.QueryRowContext(ctx, stmt, args...).Scan(&create.ID, &create.CreatedTs, &create.UpdatedTs); err != nil {
@ -26,33 +26,30 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
where, args := []string{"1 = 1"}, []any{} where, args := []string{"1 = 1"}, []any{}
if v := find.ID; v != nil { if v := find.ID; v != nil {
where, args = append(where, "id = ?"), append(args, *v) where, args = append(where, "`id` = ?"), append(args, *v)
}
if v := find.ResourceName; v != nil {
where, args = append(where, "`resource_name` = ?"), append(args, *v)
} }
if v := find.CreatorID; v != nil { if v := find.CreatorID; v != nil {
where, args = append(where, "creator_id = ?"), append(args, *v) where, args = append(where, "`creator_id` = ?"), append(args, *v)
} }
if v := find.Filename; v != nil { if v := find.Filename; v != nil {
where, args = append(where, "filename = ?"), append(args, *v) where, args = append(where, "`filename` = ?"), append(args, *v)
} }
if v := find.MemoID; v != nil { if v := find.MemoID; v != nil {
where, args = append(where, "memo_id = ?"), append(args, *v) where, args = append(where, "`memo_id` = ?"), append(args, *v)
} }
if find.HasRelatedMemo { if find.HasRelatedMemo {
where = append(where, "memo_id IS NOT NULL") where = append(where, "`memo_id` IS NOT NULL")
} }
fields := []string{"id", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path", "memo_id"} fields := []string{"`id`", "`resource_name`", "`filename`", "`external_link`", "`type`", "`size`", "`creator_id`", "`created_ts`", "`updated_ts`", "`internal_path`", "`memo_id`"}
if find.GetBlob { if find.GetBlob {
fields = append(fields, "blob") fields = append(fields, "`blob`")
} }
query := fmt.Sprintf(` query := fmt.Sprintf("SELECT %s FROM `resource` WHERE %s ORDER BY `updated_ts` DESC, `created_ts` DESC", strings.Join(fields, ", "), strings.Join(where, " AND "))
SELECT
%s
FROM resource
WHERE %s
ORDER BY updated_ts DESC, created_ts DESC
`, strings.Join(fields, ", "), strings.Join(where, " AND "))
if find.Limit != nil { if find.Limit != nil {
query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit) query = fmt.Sprintf("%s LIMIT %d", query, *find.Limit)
if find.Offset != nil { if find.Offset != nil {
@ -72,6 +69,7 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
var memoID sql.NullInt32 var memoID sql.NullInt32
dests := []any{ dests := []any{
&resource.ID, &resource.ID,
&resource.ResourceName,
&resource.Filename, &resource.Filename,
&resource.ExternalLink, &resource.ExternalLink,
&resource.Type, &resource.Type,
@ -104,32 +102,32 @@ func (d *DB) ListResources(ctx context.Context, find *store.FindResource) ([]*st
func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) { func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (*store.Resource, error) {
set, args := []string{}, []any{} set, args := []string{}, []any{}
if v := update.ResourceName; v != nil {
set, args = append(set, "`resource_name` = ?"), append(args, *v)
}
if v := update.UpdatedTs; v != nil { if v := update.UpdatedTs; v != nil {
set, args = append(set, "updated_ts = ?"), append(args, *v) set, args = append(set, "`updated_ts` = ?"), append(args, *v)
} }
if v := update.Filename; v != nil { if v := update.Filename; v != nil {
set, args = append(set, "filename = ?"), append(args, *v) set, args = append(set, "`filename` = ?"), append(args, *v)
} }
if v := update.InternalPath; v != nil { if v := update.InternalPath; v != nil {
set, args = append(set, "internal_path = ?"), append(args, *v) set, args = append(set, "`internal_path` = ?"), append(args, *v)
} }
if v := update.MemoID; v != nil { if v := update.MemoID; v != nil {
set, args = append(set, "memo_id = ?"), append(args, *v) set, args = append(set, "`memo_id` = ?"), append(args, *v)
} }
if v := update.Blob; v != nil { if v := update.Blob; v != nil {
set, args = append(set, "blob = ?"), append(args, v) set, args = append(set, "`blob` = ?"), append(args, v)
} }
args = append(args, update.ID) args = append(args, update.ID)
fields := []string{"id", "filename", "external_link", "type", "size", "creator_id", "created_ts", "updated_ts", "internal_path"} fields := []string{"`id`", "`resource_name`", "`filename`", "`external_link`", "`type`", "`size`", "`creator_id`", "`created_ts`", "`updated_ts`", "`internal_path`"}
stmt := ` stmt := "UPDATE `resource` SET " + strings.Join(set, ", ") + " WHERE `id` = ? RETURNING " + strings.Join(fields, ", ")
UPDATE resource
SET ` + strings.Join(set, ", ") + `
WHERE id = ?
RETURNING ` + strings.Join(fields, ", ")
resource := store.Resource{} resource := store.Resource{}
dests := []any{ dests := []any{
&resource.ID, &resource.ID,
&resource.ResourceName,
&resource.Filename, &resource.Filename,
&resource.ExternalLink, &resource.ExternalLink,
&resource.Type, &resource.Type,
@ -147,10 +145,7 @@ func (d *DB) UpdateResource(ctx context.Context, update *store.UpdateResource) (
} }
func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error { func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) error {
stmt := ` stmt := "DELETE FROM `resource` WHERE `id` = ?"
DELETE FROM resource
WHERE id = ?
`
result, err := d.db.ExecContext(ctx, stmt, delete.ID) result, err := d.db.ExecContext(ctx, stmt, delete.ID)
if err != nil { if err != nil {
return err return err
@ -168,16 +163,7 @@ func (d *DB) DeleteResource(ctx context.Context, delete *store.DeleteResource) e
} }
func vacuumResource(ctx context.Context, tx *sql.Tx) error { func vacuumResource(ctx context.Context, tx *sql.Tx) error {
stmt := ` stmt := "DELETE FROM `resource` WHERE `creator_id` NOT IN (SELECT `id` FROM `user`)"
DELETE FROM
resource
WHERE
creator_id NOT IN (
SELECT
id
FROM
user
)`
_, err := tx.ExecContext(ctx, stmt) _, err := tx.ExecContext(ctx, stmt)
if err != nil { if err != nil {
return err return err

View File

@ -17,7 +17,8 @@ const (
) )
type Resource struct { type Resource struct {
ID int32 ID int32
ResourceName string
// Standard fields // Standard fields
CreatorID int32 CreatorID int32
@ -37,6 +38,7 @@ type Resource struct {
type FindResource struct { type FindResource struct {
GetBlob bool GetBlob bool
ID *int32 ID *int32
ResourceName *string
CreatorID *int32 CreatorID *int32
Filename *string Filename *string
MemoID *int32 MemoID *int32
@ -47,6 +49,7 @@ type FindResource struct {
type UpdateResource struct { type UpdateResource struct {
ID int32 ID int32
ResourceName *string
UpdatedTs *int64 UpdatedTs *int64
Filename *string Filename *string
InternalPath *string InternalPath *string
@ -60,6 +63,9 @@ type DeleteResource struct {
} }
func (s *Store) CreateResource(ctx context.Context, create *Resource) (*Resource, error) { func (s *Store) CreateResource(ctx context.Context, create *Resource) (*Resource, error) {
if !util.ResourceNameMatcher.MatchString(create.ResourceName) {
return nil, errors.New("invalid resource name")
}
return s.driver.CreateResource(ctx, create) return s.driver.CreateResource(ctx, create)
} }
@ -81,6 +87,9 @@ func (s *Store) GetResource(ctx context.Context, find *FindResource) (*Resource,
} }
func (s *Store) UpdateResource(ctx context.Context, update *UpdateResource) (*Resource, error) { func (s *Store) UpdateResource(ctx context.Context, update *UpdateResource) (*Resource, error) {
if update.ResourceName != nil && !util.ResourceNameMatcher.MatchString(*update.ResourceName) {
return nil, errors.New("invalid resource name")
}
return s.driver.UpdateResource(ctx, update) return s.driver.UpdateResource(ctx, update)
} }
@ -101,6 +110,7 @@ func (s *Store) DeleteResource(ctx context.Context, delete *DeleteResource) erro
} }
_ = os.Remove(resourcePath) _ = os.Remove(resourcePath)
} }
// Delete the thumbnail. // Delete the thumbnail.
if util.HasPrefixes(resource.Type, "image/png", "image/jpeg") { if util.HasPrefixes(resource.Type, "image/png", "image/jpeg") {
ext := filepath.Ext(resource.Filename) ext := filepath.Ext(resource.Filename)

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/lithammer/shortuuid/v4"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
@ -33,6 +34,7 @@ func TestMigrateResourceInternalPath(t *testing.T) {
for _, testCase := range testCases { for _, testCase := range testCases {
for input, expectedOutput := range testCase { for input, expectedOutput := range testCase {
resourceCreate := &store.Resource{ resourceCreate := &store.Resource{
ResourceName: shortuuid.New(),
CreatorID: user.ID, CreatorID: user.ID,
InternalPath: input, InternalPath: input,
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/lithammer/shortuuid/v4"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
@ -13,6 +14,7 @@ func TestResourceStore(t *testing.T) {
ctx := context.Background() ctx := context.Background()
ts := NewTestingStore(ctx, t) ts := NewTestingStore(ctx, t)
_, err := ts.CreateResource(ctx, &store.Resource{ _, err := ts.CreateResource(ctx, &store.Resource{
ResourceName: shortuuid.New(),
CreatorID: 101, CreatorID: 101,
Filename: "test.epub", Filename: "test.epub",
Blob: []byte("test"), Blob: []byte("test"),