mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-02-14 23:48:30 -06:00
[client] WIP: fix actions
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import 'package:http/http.dart';
|
||||
import 'package:offtheline/offtheline.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/libphylum/phylum_api_types.dart';
|
||||
import 'package:phylum/libphylum/responses/responses.dart';
|
||||
|
||||
@@ -6,12 +8,14 @@ abstract class ResourceAction extends PhylumAction {
|
||||
String _resourceId;
|
||||
String get resourceId => _resourceId;
|
||||
|
||||
@override
|
||||
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, ResourceResponse.fromResponse);
|
||||
|
||||
ResourceAction({required String resourceId}) : _resourceId = resourceId;
|
||||
|
||||
void setResourceId(String resourceId) {
|
||||
_resourceId = resourceId;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
|
||||
return parseJsonMapResponse(response, ResourceResponse.fromResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:http/http.dart';
|
||||
import 'package:offtheline/offtheline.dart';
|
||||
import 'package:phylum/libphylum/actions/action_resource_bookmark_remove.dart';
|
||||
import 'package:phylum/libphylum/actions/action_resource_create.dart';
|
||||
import 'package:phylum/libphylum/db/db.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/libphylum/phylum_api_types.dart';
|
||||
import 'package:phylum/libphylum/responses/responses.dart';
|
||||
|
||||
@@ -24,9 +26,6 @@ class ResourceBookmarkAddAction extends ResourceAction with JsonApiAction {
|
||||
'name': bookmarkName,
|
||||
};
|
||||
|
||||
@override
|
||||
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, BookmarkResponse.fromResponse);
|
||||
|
||||
@override
|
||||
List<LocalChange> get localChanges =>
|
||||
[AddBookmarkChange(resourceId, bookmarkName: bookmarkName, dir: dir, createdMillis: createdMillis)];
|
||||
@@ -62,6 +61,11 @@ class ResourceBookmarkAddAction extends ResourceAction with JsonApiAction {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
|
||||
return parseJsonMapResponse(response, BookmarkResponse.fromResponse);
|
||||
}
|
||||
|
||||
@override
|
||||
bool dependsOn(PhylumAction action) =>
|
||||
action is ResourceAction &&
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:http/src/streamed_response.dart';
|
||||
import 'package:offtheline/offtheline.dart';
|
||||
import 'package:phylum/libphylum/db/db.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/libphylum/phylum_api_types.dart';
|
||||
import 'package:phylum/libphylum/responses/responses.dart';
|
||||
|
||||
@@ -24,9 +26,6 @@ class ResourceBookmarkRemoveAction extends ResourceAction with JsonApiAction {
|
||||
'id': resourceId,
|
||||
};
|
||||
|
||||
@override
|
||||
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, EmptyResponse.fromResponse);
|
||||
|
||||
@override
|
||||
List<LocalChange> get localChanges => [RemoveBookmarkChange(resourceId)];
|
||||
|
||||
@@ -50,6 +49,11 @@ class ResourceBookmarkRemoveAction extends ResourceAction with JsonApiAction {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
|
||||
return parseJsonMapResponse(response, EmptyResponse.fromResponse);
|
||||
}
|
||||
|
||||
@override
|
||||
bool dependsOn(PhylumAction action) =>
|
||||
action is ResourceAction &&
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:http/http.dart';
|
||||
import 'package:offtheline/offtheline.dart';
|
||||
import 'package:phylum/libphylum/actions/changes/delete_resource_change.dart';
|
||||
import 'package:phylum/libphylum/actions/changes/update_resource_deleted_change.dart';
|
||||
import 'package:phylum/libphylum/actions/changes/update_resource_modified_change.dart';
|
||||
import 'package:phylum/libphylum/actions/changes/update_resource_trashed_change.dart';
|
||||
import 'package:phylum/libphylum/db/db.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/libphylum/phylum_api_types.dart';
|
||||
import 'package:phylum/libphylum/responses/responses.dart';
|
||||
|
||||
@@ -89,6 +91,7 @@ class ResourceDeleteAction extends ResourceAction with JsonApiAction {
|
||||
bool dependsOn(PhylumAction action) => action is ResourceAction && action.resourceId == resourceId;
|
||||
|
||||
@override
|
||||
ResponseParser get parseResponse =>
|
||||
permanent ? (_, response) => parseJsonMapResponse(response, EmptyResponse.fromResponse) : super.parseResponse;
|
||||
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
|
||||
return permanent ? parseJsonMapResponse(response, EmptyResponse.fromResponse) : super.parseResponse(response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:http/src/streamed_response.dart';
|
||||
import 'package:offtheline/offtheline.dart';
|
||||
import 'package:phylum/libphylum/actions/action_resource_create.dart';
|
||||
import 'package:phylum/libphylum/actions/changes/create_publink_change.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/libphylum/phylum_api_types.dart';
|
||||
import 'package:phylum/libphylum/responses/responses.dart';
|
||||
|
||||
@@ -26,9 +28,6 @@ class ResourcePublinkCreateAction extends ResourceAction with JsonApiAction {
|
||||
if (accessLimit != 0) 'access_limit': accessLimit,
|
||||
};
|
||||
|
||||
@override
|
||||
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, PublinkResponse.fromResponse);
|
||||
|
||||
@override
|
||||
List<LocalChange> get localChanges => [
|
||||
CreatePublinkChange(
|
||||
@@ -82,6 +81,11 @@ class ResourcePublinkCreateAction extends ResourceAction with JsonApiAction {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
|
||||
return parseJsonMapResponse(response, PublinkResponse.fromResponse);
|
||||
}
|
||||
|
||||
@override
|
||||
bool dependsOn(PhylumAction action) =>
|
||||
action is ResourceAction &&
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'dart:nativewrappers/_internal/vm/lib/math_patch.dart';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
import 'package:offtheline/offtheline.dart';
|
||||
import 'package:phylum/libphylum/actions/action_resource.dart';
|
||||
import 'package:phylum/libphylum/actions/action_resource_create.dart';
|
||||
import 'package:phylum/libphylum/actions/changes/create_resource_change.dart';
|
||||
import 'package:phylum/libphylum/actions/changes/create_resource_version_change.dart';
|
||||
import 'package:phylum/libphylum/actions/changes/update_resource_deleted_change.dart';
|
||||
import 'package:phylum/libphylum/phylum_api_types.dart';
|
||||
import 'package:phylum/libphylum/responses/responses.dart';
|
||||
import 'package:phylum/libphylum/util/uuid.dart';
|
||||
import 'package:phylum/util/permissions.dart';
|
||||
|
||||
@@ -187,7 +187,7 @@ class ResourceUploadAction extends ResourceCreateAction {
|
||||
// 'sha256': '', // TODO: compute and send
|
||||
};
|
||||
final request = MultipartRequest('PUT', uriBuilder.build());
|
||||
final length = min(chunkSize, data.length - uploaded);
|
||||
final length = chunkSize < data.length - uploaded ? chunkSize : data.length - uploaded;
|
||||
request.files.add(MultipartFile(
|
||||
'contents',
|
||||
ByteStream(data.getStream(start: uploaded, end: uploaded + length)),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:http/src/streamed_response.dart';
|
||||
import 'package:offtheline/offtheline.dart';
|
||||
import 'package:phylum/libphylum/actions/action_resource_share.dart';
|
||||
import 'package:phylum/libphylum/actions/changes/create_user_change.dart';
|
||||
import 'package:phylum/libphylum/phylum_account.dart';
|
||||
import 'package:phylum/libphylum/phylum_api_types.dart';
|
||||
import 'package:phylum/libphylum/responses/responses.dart';
|
||||
|
||||
@@ -58,7 +60,9 @@ class UserInviteAction extends PhylumAction with JsonApiAction {
|
||||
bool dependsOn(PhylumAction action) => false;
|
||||
|
||||
@override
|
||||
ResponseParser get parseResponse => (_, response) => parseJsonMapResponse(response, UserResponse.fromResponse);
|
||||
Future<ApiResponse<PhylumAccount>> parseResponse(StreamedResponse response) {
|
||||
return parseJsonMapResponse(response, UserResponse.fromResponse);
|
||||
}
|
||||
|
||||
@override
|
||||
bool processResponse(ApiResponse response) {
|
||||
|
||||
@@ -30,7 +30,7 @@ func SetupRoutes(r *gin.RouterGroup) {
|
||||
group.PUT("/upload", handleUploadRequest)
|
||||
|
||||
partialUploadsGroup := group.Group("/partial_uploads")
|
||||
partialUploadsGroup.POST("/start", handlePartialUploadsCreateRequest)
|
||||
partialUploadsGroup.POST("/start", handlePartialUploadsStartRequest)
|
||||
partialUploadsGroup.GET("/query", handlePartialUploadsQueryRequest)
|
||||
partialUploadsGroup.PUT("/upload", handlePartialUploadsUploadRequest)
|
||||
partialUploadsGroup.POST("/finalize", handlePartialUploadsFinalizeRequest)
|
||||
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type partialUploadsCreateParams struct {
|
||||
type partialUploadsStartParams struct {
|
||||
ID string `json:"id" form:"id" binding:"omitempty,uuid"`
|
||||
Size int64 `json:"size" form:"size" binding:"required"`
|
||||
SHA256 string `json:"sha256" form:"sha256"`
|
||||
}
|
||||
@@ -36,8 +37,8 @@ type partialUploadsFinalizeParams struct {
|
||||
Conflict core.ResourceBindConflictResolution `json:"conflict" form:"conflict"`
|
||||
}
|
||||
|
||||
func handlePartialUploadsCreateRequest(c *gin.Context) {
|
||||
var params partialUploadsCreateParams
|
||||
func handlePartialUploadsStartRequest(c *gin.Context) {
|
||||
var params partialUploadsStartParams
|
||||
err := c.ShouldBind(¶ms)
|
||||
if err != nil {
|
||||
err = c.BindQuery(¶ms)
|
||||
@@ -48,13 +49,23 @@ func handlePartialUploadsCreateRequest(c *gin.Context) {
|
||||
|
||||
userID := authenticator.GetAuth(c).UserID()
|
||||
f := authenticator.GetFileSystem(c)
|
||||
uploadID, err := f.CreatePartialUpload(userID, params.Size, params.SHA256)
|
||||
|
||||
var id uuid.UUID
|
||||
if params.ID != "" {
|
||||
id, err = uuid.Parse(params.ID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
id, _ = uuid.NewV7()
|
||||
}
|
||||
err = f.PartialUploadCreate(userID, id, params.Size, params.SHA256)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"upload_id": uploadID,
|
||||
"id": id,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -75,7 +86,7 @@ func handlePartialUploadsQueryRequest(c *gin.Context) {
|
||||
|
||||
userID := authenticator.GetAuth(c).UserID()
|
||||
f := authenticator.GetFileSystem(c)
|
||||
size, uploaded, err := f.QueryPartialUpload(userID, id)
|
||||
size, uploaded, err := f.PartialUploadQuery(userID, id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -102,6 +113,7 @@ func handlePartialUploadsUploadRequest(c *gin.Context) {
|
||||
}
|
||||
|
||||
f := authenticator.GetFileSystem(c)
|
||||
offset := params.Offset
|
||||
err = func() error {
|
||||
file, err := c.FormFile("contents")
|
||||
if err != nil {
|
||||
@@ -117,15 +129,16 @@ func handlePartialUploadsUploadRequest(c *gin.Context) {
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
out, err := f.OpenPartialUpload(authenticator.GetAuth(c).UserID(), id, params.Offset)
|
||||
out, err := f.PartialUploadOpenWrite(authenticator.GetAuth(c).UserID(), id, offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(out, src); err != nil {
|
||||
if written, err := io.Copy(out, src); err != nil {
|
||||
out.Close()
|
||||
return err
|
||||
} else {
|
||||
offset += written
|
||||
return out.Close()
|
||||
}
|
||||
}()
|
||||
@@ -133,7 +146,9 @@ func handlePartialUploadsUploadRequest(c *gin.Context) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Status(200)
|
||||
c.JSON(200, gin.H{
|
||||
"offset": offset,
|
||||
})
|
||||
}
|
||||
|
||||
func handlePartialUploadsFinalizeRequest(c *gin.Context) {
|
||||
@@ -168,7 +183,7 @@ func handlePartialUploadsFinalizeRequest(c *gin.Context) {
|
||||
f := authenticator.GetFileSystem(c)
|
||||
err = func() error {
|
||||
// TODO: #perf disk I/O in tx
|
||||
src, err := f.ReadPartialUpload(authenticator.GetAuth(c).UserID(), uploadID)
|
||||
src, err := f.PartialUploadOpenRead(authenticator.GetAuth(c).UserID(), uploadID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -58,4 +58,5 @@ var (
|
||||
ErrUploadChecksumMismatch = NewError(http.StatusBadRequest, "upload_checksum_mismatch", "Upload Checksum Mismatch")
|
||||
ErrUploadOffsetExceeded = NewError(http.StatusBadRequest, "upload_offset_exceeded", "Upload Offset Exceeded")
|
||||
ErrUploadIncomplete = NewError(http.StatusBadRequest, "upload_incomplete", "Upload Incomplete")
|
||||
ErrUploadNotFound = NewError(http.StatusBadRequest, "upload_not_found", "Upload Not Found")
|
||||
)
|
||||
|
||||
@@ -16,11 +16,9 @@ import (
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
func (f *FileSystem) CreatePartialUpload(user int32, size int64, checksum string) (uuid.UUID, error) {
|
||||
id, _ := uuid.NewV7()
|
||||
|
||||
func (f *FileSystem) PartialUploadCreate(user int32, id uuid.UUID, size int64, checksum string) error {
|
||||
path := uploadFilePath(user, id)
|
||||
return id, f.db.RunInTx(func(db db.TxHandler) error {
|
||||
return f.db.RunInTx(func(db db.TxHandler) error {
|
||||
if err := insertPartialUpload(db, id, user, size, checksum); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -32,13 +30,16 @@ func (f *FileSystem) CreatePartialUpload(user int32, size int64, checksum string
|
||||
})
|
||||
}
|
||||
|
||||
func (f *FileSystem) OpenPartialUpload(user int32, id uuid.UUID, offset int64) (io.WriteCloser, error) {
|
||||
func (f *FileSystem) PartialUploadOpenWrite(user int32, id uuid.UUID, offset int64) (io.WriteCloser, error) {
|
||||
owner, size, uploaded, checksum, err := queryPartialUpload(f.db, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
err = ErrUploadNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if user != owner {
|
||||
return nil, ErrResourceNotFound
|
||||
return nil, ErrUploadNotFound
|
||||
}
|
||||
|
||||
path := uploadFilePath(user, id)
|
||||
@@ -71,14 +72,17 @@ func (f *FileSystem) OpenPartialUpload(user int32, id uuid.UUID, offset int64) (
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (f *FileSystem) QueryPartialUpload(user int32, id uuid.UUID) (size, uploaded int64, err error) {
|
||||
func (f *FileSystem) PartialUploadQuery(user int32, id uuid.UUID) (size, uploaded int64, err error) {
|
||||
var owner int32
|
||||
owner, size, uploaded, _, err = queryPartialUpload(f.db, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
err = ErrUploadNotFound
|
||||
}
|
||||
return
|
||||
}
|
||||
if user != owner {
|
||||
err = ErrResourceNotFound
|
||||
err = ErrUploadNotFound
|
||||
return
|
||||
}
|
||||
|
||||
@@ -99,13 +103,16 @@ func (f *FileSystem) QueryPartialUpload(user int32, id uuid.UUID) (size, uploade
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FileSystem) ReadPartialUpload(user int32, id uuid.UUID) (io.ReadCloser, error) {
|
||||
func (f *FileSystem) PartialUploadOpenRead(user int32, id uuid.UUID) (io.ReadCloser, error) {
|
||||
owner, size, uploaded, _, err := queryPartialUpload(f.db, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrUploadNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if user != owner {
|
||||
return nil, ErrResourceNotFound
|
||||
return nil, ErrUploadNotFound
|
||||
}
|
||||
|
||||
path := uploadFilePath(user, id)
|
||||
|
||||
Reference in New Issue
Block a user