From f56e3e7bffe1f53570d98ff953b4ab3885de33d4 Mon Sep 17 00:00:00 2001 From: = <torjus@usit.uio.no> Date: Sat, 4 Dec 2021 11:30:42 +0100 Subject: [PATCH] Add list --- cmd/ezshare.go | 40 +++++ pb/ezshare.pb.go | 393 ++++++++++++++++++++++++++++++++++++++---- pb/ezshare_grpc.pb.go | 72 ++++++++ protos/ezshare.proto | 18 ++ server/grpc.go | 16 ++ store/bolt.go | 23 +++ store/filesystem.go | 35 ++++ store/memory.go | 13 ++ store/store.go | 1 + store/store_test.go | 13 ++ 10 files changed, 589 insertions(+), 35 deletions(-) diff --git a/cmd/ezshare.go b/cmd/ezshare.go index b1d2a19..a7ccaab 100644 --- a/cmd/ezshare.go +++ b/cmd/ezshare.go @@ -82,6 +82,11 @@ func main() { ArgsUsage: "PATH [PATH]..", Action: ActionClientUpload, }, + { + Name: "list", + Usage: "List files", + Action: ActionClientList, + }, { Name: "config-init", Usage: "Initialize default config", @@ -332,6 +337,41 @@ func ActionClientUpload(c *cli.Context) error { return nil } +func ActionClientList(c *cli.Context) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + + addr := cfg.Client.DefaultServer + if c.IsSet("addr") { + addr = c.String("addr") + } + + clientCreds, err := cfg.Client.Creds() + if err != nil { + return err + } + conn, err := grpc.DialContext(c.Context, addr, grpc.WithTransportCredentials(clientCreds)) + + if err != nil { + return err + } + defer conn.Close() + + client := pb.NewFileServiceClient(conn) + + resp, err := client.ListFiles(c.Context, &pb.ListFilesRequest{}) + if err != nil { + return err + } + + for _, elem := range resp.Files { + fmt.Println(elem.FileId) + } + return nil +} + func ActionGencerts(c *cli.Context) error { outDir := "." if c.IsSet("out-dir") { diff --git a/pb/ezshare.pb.go b/pb/ezshare.pb.go index f081cb1..6b33c3a 100644 --- a/pb/ezshare.pb.go +++ b/pb/ezshare.pb.go @@ -299,6 +299,178 @@ func (x *GetFileResponse) GetFile() *File { return nil } +// Delete +type DeleteFileRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteFileRequest) Reset() { + *x = DeleteFileRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteFileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteFileRequest) ProtoMessage() {} + +func (x *DeleteFileRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteFileRequest.ProtoReflect.Descriptor instead. +func (*DeleteFileRequest) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{5} +} + +func (x *DeleteFileRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DeleteFileResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteFileResponse) Reset() { + *x = DeleteFileResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteFileResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteFileResponse) ProtoMessage() {} + +func (x *DeleteFileResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteFileResponse.ProtoReflect.Descriptor instead. +func (*DeleteFileResponse) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{6} +} + +// List +type ListFilesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListFilesRequest) Reset() { + *x = ListFilesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListFilesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListFilesRequest) ProtoMessage() {} + +func (x *ListFilesRequest) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListFilesRequest.ProtoReflect.Descriptor instead. +func (*ListFilesRequest) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{7} +} + +type ListFilesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Files []*ListFilesResponse_ListFileInfo `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"` +} + +func (x *ListFilesResponse) Reset() { + *x = ListFilesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListFilesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListFilesResponse) ProtoMessage() {} + +func (x *ListFilesResponse) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListFilesResponse.ProtoReflect.Descriptor instead. +func (*ListFilesResponse) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{8} +} + +func (x *ListFilesResponse) GetFiles() []*ListFilesResponse_ListFileInfo { + if x != nil { + return x.Files + } + return nil +} + type File_Metadata struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -312,7 +484,7 @@ type File_Metadata struct { func (x *File_Metadata) Reset() { *x = File_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_protos_ezshare_proto_msgTypes[5] + mi := &file_protos_ezshare_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -325,7 +497,7 @@ func (x *File_Metadata) String() string { func (*File_Metadata) ProtoMessage() {} func (x *File_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_protos_ezshare_proto_msgTypes[5] + mi := &file_protos_ezshare_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -362,6 +534,61 @@ func (x *File_Metadata) GetOriginalFilename() string { return "" } +type ListFilesResponse_ListFileInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FileId string `protobuf:"bytes,1,opt,name=file_id,json=fileId,proto3" json:"file_id,omitempty"` + Metadata *File_Metadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` +} + +func (x *ListFilesResponse_ListFileInfo) Reset() { + *x = ListFilesResponse_ListFileInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_protos_ezshare_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListFilesResponse_ListFileInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListFilesResponse_ListFileInfo) ProtoMessage() {} + +func (x *ListFilesResponse_ListFileInfo) ProtoReflect() protoreflect.Message { + mi := &file_protos_ezshare_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListFilesResponse_ListFileInfo.ProtoReflect.Descriptor instead. +func (*ListFilesResponse_ListFileInfo) Descriptor() ([]byte, []int) { + return file_protos_ezshare_proto_rawDescGZIP(), []int{8, 0} +} + +func (x *ListFilesResponse_ListFileInfo) GetFileId() string { + if x != nil { + return x.FileId + } + return "" +} + +func (x *ListFilesResponse_ListFileInfo) GetMetadata() *File_Metadata { + if x != nil { + return x.Metadata + } + return nil +} + var File_protos_ezshare_proto protoreflect.FileDescriptor var file_protos_ezshare_proto_rawDesc = []byte{ @@ -405,16 +632,41 @@ var file_protos_ezshare_proto_rawDesc = []byte{ 0x64, 0x22, 0x34, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x46, 0x69, 0x6c, - 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x32, 0x96, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x65, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x55, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, - 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1b, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x6c, 0x6f, - 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x17, 0x2e, 0x65, 0x7a, - 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x47, - 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x14, 0x0a, 0x12, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xaf, 0x01, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x46, + 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x05, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x7a, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x1a, 0x5b, 0x0a, 0x0c, 0x4c, + 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x66, + 0x69, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, + 0x6c, 0x65, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, + 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x32, 0xa5, 0x02, 0x0a, 0x0b, 0x46, 0x69, 0x6c, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x0a, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, + 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x17, 0x2e, 0x65, + 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, + 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, + 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x65, 0x7a, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x09, 0x4c, 0x69, + 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, + 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x23, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x65, 0x61, 0x2e, 0x62, 0x65, 0x6e, 0x6e, 0x79, 0x2e, 0x64, 0x6f, 0x67, 0x2f, 0x74, 0x6f, 0x72, 0x6a, 0x75, 0x73, 0x2f, 0x65, 0x7a, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, @@ -432,31 +684,42 @@ func file_protos_ezshare_proto_rawDescGZIP() []byte { return file_protos_ezshare_proto_rawDescData } -var file_protos_ezshare_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_protos_ezshare_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_protos_ezshare_proto_goTypes = []interface{}{ - (*File)(nil), // 0: ezshare.File - (*UploadFileRequest)(nil), // 1: ezshare.UploadFileRequest - (*UploadFileResponse)(nil), // 2: ezshare.UploadFileResponse - (*GetFileRequest)(nil), // 3: ezshare.GetFileRequest - (*GetFileResponse)(nil), // 4: ezshare.GetFileResponse - (*File_Metadata)(nil), // 5: ezshare.File.Metadata - (*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp + (*File)(nil), // 0: ezshare.File + (*UploadFileRequest)(nil), // 1: ezshare.UploadFileRequest + (*UploadFileResponse)(nil), // 2: ezshare.UploadFileResponse + (*GetFileRequest)(nil), // 3: ezshare.GetFileRequest + (*GetFileResponse)(nil), // 4: ezshare.GetFileResponse + (*DeleteFileRequest)(nil), // 5: ezshare.DeleteFileRequest + (*DeleteFileResponse)(nil), // 6: ezshare.DeleteFileResponse + (*ListFilesRequest)(nil), // 7: ezshare.ListFilesRequest + (*ListFilesResponse)(nil), // 8: ezshare.ListFilesResponse + (*File_Metadata)(nil), // 9: ezshare.File.Metadata + (*ListFilesResponse_ListFileInfo)(nil), // 10: ezshare.ListFilesResponse.ListFileInfo + (*timestamppb.Timestamp)(nil), // 11: google.protobuf.Timestamp } var file_protos_ezshare_proto_depIdxs = []int32{ - 5, // 0: ezshare.File.metadata:type_name -> ezshare.File.Metadata - 6, // 1: ezshare.UploadFileRequest.expires_on:type_name -> google.protobuf.Timestamp - 0, // 2: ezshare.GetFileResponse.file:type_name -> ezshare.File - 6, // 3: ezshare.File.Metadata.uploaded_on:type_name -> google.protobuf.Timestamp - 6, // 4: ezshare.File.Metadata.expires_on:type_name -> google.protobuf.Timestamp - 1, // 5: ezshare.FileService.UploadFile:input_type -> ezshare.UploadFileRequest - 3, // 6: ezshare.FileService.GetFile:input_type -> ezshare.GetFileRequest - 2, // 7: ezshare.FileService.UploadFile:output_type -> ezshare.UploadFileResponse - 4, // 8: ezshare.FileService.GetFile:output_type -> ezshare.GetFileResponse - 7, // [7:9] is the sub-list for method output_type - 5, // [5:7] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 9, // 0: ezshare.File.metadata:type_name -> ezshare.File.Metadata + 11, // 1: ezshare.UploadFileRequest.expires_on:type_name -> google.protobuf.Timestamp + 0, // 2: ezshare.GetFileResponse.file:type_name -> ezshare.File + 10, // 3: ezshare.ListFilesResponse.files:type_name -> ezshare.ListFilesResponse.ListFileInfo + 11, // 4: ezshare.File.Metadata.uploaded_on:type_name -> google.protobuf.Timestamp + 11, // 5: ezshare.File.Metadata.expires_on:type_name -> google.protobuf.Timestamp + 9, // 6: ezshare.ListFilesResponse.ListFileInfo.metadata:type_name -> ezshare.File.Metadata + 1, // 7: ezshare.FileService.UploadFile:input_type -> ezshare.UploadFileRequest + 3, // 8: ezshare.FileService.GetFile:input_type -> ezshare.GetFileRequest + 5, // 9: ezshare.FileService.DeleteFile:input_type -> ezshare.DeleteFileRequest + 7, // 10: ezshare.FileService.ListFiles:input_type -> ezshare.ListFilesRequest + 2, // 11: ezshare.FileService.UploadFile:output_type -> ezshare.UploadFileResponse + 4, // 12: ezshare.FileService.GetFile:output_type -> ezshare.GetFileResponse + 6, // 13: ezshare.FileService.DeleteFile:output_type -> ezshare.DeleteFileResponse + 8, // 14: ezshare.FileService.ListFiles:output_type -> ezshare.ListFilesResponse + 11, // [11:15] is the sub-list for method output_type + 7, // [7:11] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_protos_ezshare_proto_init() } @@ -526,6 +789,54 @@ func file_protos_ezshare_proto_init() { } } file_protos_ezshare_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteFileRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteFileResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListFilesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListFilesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_protos_ezshare_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*File_Metadata); i { case 0: return &v.state @@ -537,6 +848,18 @@ func file_protos_ezshare_proto_init() { return nil } } + file_protos_ezshare_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListFilesResponse_ListFileInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -544,7 +867,7 @@ func file_protos_ezshare_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_protos_ezshare_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 11, NumExtensions: 0, NumServices: 1, }, diff --git a/pb/ezshare_grpc.pb.go b/pb/ezshare_grpc.pb.go index ea5cd41..6dad9ba 100644 --- a/pb/ezshare_grpc.pb.go +++ b/pb/ezshare_grpc.pb.go @@ -20,6 +20,8 @@ const _ = grpc.SupportPackageIsVersion7 type FileServiceClient interface { UploadFile(ctx context.Context, in *UploadFileRequest, opts ...grpc.CallOption) (*UploadFileResponse, error) GetFile(ctx context.Context, in *GetFileRequest, opts ...grpc.CallOption) (*GetFileResponse, error) + DeleteFile(ctx context.Context, in *DeleteFileRequest, opts ...grpc.CallOption) (*DeleteFileResponse, error) + ListFiles(ctx context.Context, in *ListFilesRequest, opts ...grpc.CallOption) (*ListFilesResponse, error) } type fileServiceClient struct { @@ -48,12 +50,32 @@ func (c *fileServiceClient) GetFile(ctx context.Context, in *GetFileRequest, opt return out, nil } +func (c *fileServiceClient) DeleteFile(ctx context.Context, in *DeleteFileRequest, opts ...grpc.CallOption) (*DeleteFileResponse, error) { + out := new(DeleteFileResponse) + err := c.cc.Invoke(ctx, "/ezshare.FileService/DeleteFile", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fileServiceClient) ListFiles(ctx context.Context, in *ListFilesRequest, opts ...grpc.CallOption) (*ListFilesResponse, error) { + out := new(ListFilesResponse) + err := c.cc.Invoke(ctx, "/ezshare.FileService/ListFiles", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // FileServiceServer is the server API for FileService service. // All implementations must embed UnimplementedFileServiceServer // for forward compatibility type FileServiceServer interface { UploadFile(context.Context, *UploadFileRequest) (*UploadFileResponse, error) GetFile(context.Context, *GetFileRequest) (*GetFileResponse, error) + DeleteFile(context.Context, *DeleteFileRequest) (*DeleteFileResponse, error) + ListFiles(context.Context, *ListFilesRequest) (*ListFilesResponse, error) mustEmbedUnimplementedFileServiceServer() } @@ -67,6 +89,12 @@ func (UnimplementedFileServiceServer) UploadFile(context.Context, *UploadFileReq func (UnimplementedFileServiceServer) GetFile(context.Context, *GetFileRequest) (*GetFileResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetFile not implemented") } +func (UnimplementedFileServiceServer) DeleteFile(context.Context, *DeleteFileRequest) (*DeleteFileResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteFile not implemented") +} +func (UnimplementedFileServiceServer) ListFiles(context.Context, *ListFilesRequest) (*ListFilesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListFiles not implemented") +} func (UnimplementedFileServiceServer) mustEmbedUnimplementedFileServiceServer() {} // UnsafeFileServiceServer may be embedded to opt out of forward compatibility for this service. @@ -116,6 +144,42 @@ func _FileService_GetFile_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } +func _FileService_DeleteFile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteFileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FileServiceServer).DeleteFile(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ezshare.FileService/DeleteFile", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FileServiceServer).DeleteFile(ctx, req.(*DeleteFileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FileService_ListFiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListFilesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FileServiceServer).ListFiles(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ezshare.FileService/ListFiles", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FileServiceServer).ListFiles(ctx, req.(*ListFilesRequest)) + } + return interceptor(ctx, in, info, handler) +} + // FileService_ServiceDesc is the grpc.ServiceDesc for FileService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -131,6 +195,14 @@ var FileService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetFile", Handler: _FileService_GetFile_Handler, }, + { + MethodName: "DeleteFile", + Handler: _FileService_DeleteFile_Handler, + }, + { + MethodName: "ListFiles", + Handler: _FileService_ListFiles_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "protos/ezshare.proto", diff --git a/protos/ezshare.proto b/protos/ezshare.proto index 88b046d..35de631 100644 --- a/protos/ezshare.proto +++ b/protos/ezshare.proto @@ -40,8 +40,26 @@ message GetFileResponse { File file = 1; } +// Delete +message DeleteFileRequest { + string id = 1; +} +message DeleteFileResponse { +} + +// List +message ListFilesRequest {} +message ListFilesResponse { + message ListFileInfo { + string file_id = 1; + File.Metadata metadata = 2; + } + repeated ListFileInfo files = 1; +} service FileService { rpc UploadFile(UploadFileRequest) returns (UploadFileResponse) {} rpc GetFile(GetFileRequest) returns (GetFileResponse) {} + rpc DeleteFile(DeleteFileRequest) returns (DeleteFileResponse) {} + rpc ListFiles(ListFilesRequest) returns (ListFilesResponse) {} } \ No newline at end of file diff --git a/server/grpc.go b/server/grpc.go index d7184e7..ba56fa7 100644 --- a/server/grpc.go +++ b/server/grpc.go @@ -37,3 +37,19 @@ func (s *GRPCFileServiceServer) GetFile(ctx context.Context, req *pb.GetFileRequ return &pb.GetFileResponse{File: f}, nil } + +func (s *GRPCFileServiceServer) DeleteFile(ctx context.Context, req *pb.DeleteFileRequest) (*pb.DeleteFileResponse, error) { + err := s.store.DeleteFile(req.Id) + return &pb.DeleteFileResponse{}, err +} + +func (s *GRPCFileServiceServer) ListFiles(ctx context.Context, req *pb.ListFilesRequest) (*pb.ListFilesResponse, error) { + infos, err := s.store.ListFiles() + if err != nil { + return nil, err + } + + return &pb.ListFilesResponse{ + Files: infos, + }, nil +} diff --git a/store/bolt.go b/store/bolt.go index cad319c..e490b96 100644 --- a/store/bolt.go +++ b/store/bolt.go @@ -9,6 +9,8 @@ import ( "google.golang.org/protobuf/proto" ) +var _ FileStore = &BoltStore{} + type BoltStore struct { db *bolt.DB } @@ -88,3 +90,24 @@ func (s *BoltStore) DeleteFile(id string) error { return bkt.Delete([]byte(id)) }) } + +func (s *BoltStore) ListFiles() ([]*pb.ListFilesResponse_ListFileInfo, error) { + var response []*pb.ListFilesResponse_ListFileInfo + err := s.db.View(func(t *bolt.Tx) error { + bkt := t.Bucket(bktKey) + return bkt.ForEach(func(k, v []byte) error { + var f pb.File + err := proto.Unmarshal(v, &f) + if err != nil { + return err + } + + response = append(response, &pb.ListFilesResponse_ListFileInfo{FileId: f.FileId, Metadata: f.Metadata}) + return nil + }) + }) + if err != nil { + return nil, err + } + return response, nil +} diff --git a/store/filesystem.go b/store/filesystem.go index 2906d39..6401207 100644 --- a/store/filesystem.go +++ b/store/filesystem.go @@ -3,6 +3,7 @@ package store import ( "encoding/json" "fmt" + "io/fs" "io/ioutil" "os" "path/filepath" @@ -92,3 +93,37 @@ func (s *FileSystemStore) DeleteFile(id string) error { return nil } + +func (s *FileSystemStore) ListFiles() ([]*pb.ListFilesResponse_ListFileInfo, error) { + root := os.DirFS(s.dir) + var response []*pb.ListFilesResponse_ListFileInfo + err := fs.WalkDir(root, ".", func(path string, d fs.DirEntry, err error) error { + id := filepath.Base(path) + + // Check that it matches length of uuid + if len(id) != 36 { + return nil + } + + if _, err := uuid.Parse(id); err == nil { + // Is valid uuid, try to open metadata-file + f, err := root.Open(fmt.Sprintf("%s.metadata", path)) + if err != nil { + return err + } + defer f.Close() + + var fm pb.File_Metadata + decoder := json.NewDecoder(f) + if err := decoder.Decode(&fm); err != nil { + return err + } + response = append(response, &pb.ListFilesResponse_ListFileInfo{FileId: id, Metadata: &fm}) + } + return nil + }) + if err != nil { + return nil, err + } + return response, nil +} diff --git a/store/memory.go b/store/memory.go index 1e9c72c..b571b33 100644 --- a/store/memory.go +++ b/store/memory.go @@ -51,3 +51,16 @@ func (s *MemoryFileStore) DeleteFile(id string) error { delete(s.files, id) return nil } + +func (s *MemoryFileStore) ListFiles() ([]*pb.ListFilesResponse_ListFileInfo, error) { + s.filesLock.RLock() + defer s.filesLock.RUnlock() + + var response []*pb.ListFilesResponse_ListFileInfo + + for _, f := range s.files { + response = append(response, &pb.ListFilesResponse_ListFileInfo{FileId: f.FileId, Metadata: f.Metadata}) + } + + return response, nil +} diff --git a/store/store.go b/store/store.go index 7c3610c..5fab14b 100644 --- a/store/store.go +++ b/store/store.go @@ -12,4 +12,5 @@ type FileStore interface { GetFile(id string) (*pb.File, error) StoreFile(file *pb.File) (string, error) DeleteFile(id string) error + ListFiles() ([]*pb.ListFilesResponse_ListFileInfo, error) } diff --git a/store/store_test.go b/store/store_test.go index d111888..656ee7f 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -26,6 +26,19 @@ func doFileStoreTest(s store.FileStore, t *testing.T) { t.Fatalf("Unable to store file: %s", err) } + // List + list, err := s.ListFiles() + if err != nil { + t.Fatalf("error listing files: %s", err) + } + if len(list) != 1 { + t.Fatalf("List returned unexpected amount. Got %d want %d", len(list), 1) + } + + if list[0].FileId != id { + t.Fatalf("List contains wrong id") + } + retrieved, err := s.GetFile(id) if err != nil { t.Fatalf("Unable to get file: %s", err)