Remove obsolete gRPC client/server implementations and migrate to remote package
This commit is contained in:
236
grpc/client.go
236
grpc/client.go
@ -1,236 +0,0 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"gitea.unprism.ru/KRBL/Agate/store"
|
||||
)
|
||||
|
||||
// SnapshotClient implements the client for connecting to a remote snapshot server
|
||||
type SnapshotClient struct {
|
||||
conn *grpc.ClientConn
|
||||
client SnapshotServiceClient
|
||||
}
|
||||
|
||||
// NewSnapshotClient creates a new client connected to the specified address
|
||||
func NewSnapshotClient(address string) (*SnapshotClient, error) {
|
||||
// Connect to the server with insecure credentials (for simplicity)
|
||||
conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to server at %s: %w", address, err)
|
||||
}
|
||||
|
||||
// Create the gRPC client
|
||||
client := NewSnapshotServiceClient(conn)
|
||||
|
||||
return &SnapshotClient{
|
||||
conn: conn,
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the connection to the server
|
||||
func (c *SnapshotClient) Close() error {
|
||||
if c.conn != nil {
|
||||
return c.conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListSnapshots retrieves a list of snapshots from the remote server
|
||||
func (c *SnapshotClient) ListSnapshots(ctx context.Context) ([]store.SnapshotInfo, error) {
|
||||
response, err := c.client.ListSnapshots(ctx, &ListSnapshotsRequest{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list snapshots: %w", err)
|
||||
}
|
||||
|
||||
// Convert gRPC snapshot info to store.SnapshotInfo
|
||||
snapshots := make([]store.SnapshotInfo, 0, len(response.Snapshots))
|
||||
for _, snapshot := range response.Snapshots {
|
||||
snapshots = append(snapshots, store.SnapshotInfo{
|
||||
ID: snapshot.Id,
|
||||
Name: snapshot.Name,
|
||||
ParentID: snapshot.ParentId,
|
||||
CreationTime: snapshot.CreationTime.AsTime(),
|
||||
})
|
||||
}
|
||||
|
||||
return snapshots, nil
|
||||
}
|
||||
|
||||
// FetchSnapshotDetails retrieves detailed information about a specific snapshot
|
||||
func (c *SnapshotClient) FetchSnapshotDetails(ctx context.Context, snapshotID string) (*store.Snapshot, error) {
|
||||
response, err := c.client.GetSnapshotDetails(ctx, &GetSnapshotDetailsRequest{
|
||||
SnapshotId: snapshotID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get snapshot details: %w", err)
|
||||
}
|
||||
|
||||
// Convert gRPC snapshot details to store.Snapshot
|
||||
snapshot := &store.Snapshot{
|
||||
ID: response.Info.Id,
|
||||
Name: response.Info.Name,
|
||||
ParentID: response.Info.ParentId,
|
||||
CreationTime: response.Info.CreationTime.AsTime(),
|
||||
Files: make([]store.FileInfo, 0, len(response.Files)),
|
||||
}
|
||||
|
||||
// Convert file info
|
||||
for _, file := range response.Files {
|
||||
snapshot.Files = append(snapshot.Files, store.FileInfo{
|
||||
Path: file.Path,
|
||||
Size: file.SizeBytes,
|
||||
IsDir: file.IsDir,
|
||||
SHA256: file.Sha256Hash,
|
||||
})
|
||||
}
|
||||
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
// DownloadSnapshot downloads a snapshot from the server
|
||||
// This implementation downloads each file individually to optimize bandwidth usage
|
||||
func (c *SnapshotClient) DownloadSnapshot(ctx context.Context, snapshotID string, targetDir string, localParentID string) error {
|
||||
// Get snapshot details
|
||||
snapshot, err := c.FetchSnapshotDetails(ctx, snapshotID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get snapshot details: %w", err)
|
||||
}
|
||||
|
||||
// Create target directory if it doesn't exist
|
||||
if err := os.MkdirAll(targetDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create target directory: %w", err)
|
||||
}
|
||||
|
||||
// If a local parent is specified, get its details to compare files
|
||||
var localParentFiles map[string]store.FileInfo
|
||||
if localParentID != "" {
|
||||
localParent, err := c.FetchSnapshotDetails(ctx, localParentID)
|
||||
if err == nil {
|
||||
// Create a map of file paths to file info for quick lookup
|
||||
localParentFiles = make(map[string]store.FileInfo, len(localParent.Files))
|
||||
for _, file := range localParent.Files {
|
||||
localParentFiles[file.Path] = file
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Download each file
|
||||
for _, file := range snapshot.Files {
|
||||
// Skip directories, we'll create them when needed
|
||||
if file.IsDir {
|
||||
// Create directory
|
||||
dirPath := filepath.Join(targetDir, file.Path)
|
||||
if err := os.MkdirAll(dirPath, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create directory %s: %w", dirPath, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if we can skip downloading this file
|
||||
if localParentFiles != nil {
|
||||
if parentFile, exists := localParentFiles[file.Path]; exists && parentFile.SHA256 == file.SHA256 {
|
||||
// File exists in parent with same hash, copy it instead of downloading
|
||||
parentFilePath := filepath.Join(targetDir, "..", localParentID, file.Path)
|
||||
targetFilePath := filepath.Join(targetDir, file.Path)
|
||||
|
||||
// Ensure parent directory exists
|
||||
if err := os.MkdirAll(filepath.Dir(targetFilePath), 0755); err != nil {
|
||||
return fmt.Errorf("failed to create directory for %s: %w", targetFilePath, err)
|
||||
}
|
||||
|
||||
// Copy the file
|
||||
if err := copyFile(parentFilePath, targetFilePath); err != nil {
|
||||
// If copy fails, fall back to downloading
|
||||
fmt.Printf("Failed to copy file %s, will download instead: %v\n", file.Path, err)
|
||||
} else {
|
||||
// Skip to next file
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Download the file
|
||||
if err := c.downloadFile(ctx, snapshotID, file.Path, filepath.Join(targetDir, file.Path)); err != nil {
|
||||
return fmt.Errorf("failed to download file %s: %w", file.Path, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// downloadFile downloads a single file from the server
|
||||
func (c *SnapshotClient) downloadFile(ctx context.Context, snapshotID, filePath, targetPath string) error {
|
||||
// Create the request
|
||||
req := &DownloadFileRequest{
|
||||
SnapshotId: snapshotID,
|
||||
FilePath: filePath,
|
||||
}
|
||||
|
||||
// Start streaming the file
|
||||
stream, err := c.client.DownloadFile(ctx, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start file download: %w", err)
|
||||
}
|
||||
|
||||
// Ensure the target directory exists
|
||||
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
|
||||
return fmt.Errorf("failed to create directory for %s: %w", targetPath, err)
|
||||
}
|
||||
|
||||
// Create the target file
|
||||
file, err := os.Create(targetPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file %s: %w", targetPath, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Receive and write chunks
|
||||
for {
|
||||
resp, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error receiving file chunk: %w", err)
|
||||
}
|
||||
|
||||
// Write the chunk to the file
|
||||
if _, err := file.Write(resp.ChunkData); err != nil {
|
||||
return fmt.Errorf("error writing to file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper function to copy a file
|
||||
func copyFile(src, dst string) error {
|
||||
sourceFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
destFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
_, err = io.Copy(destFile, sourceFile)
|
||||
return err
|
||||
}
|
||||
|
||||
// ConnectToServer creates a new client connected to the specified address
|
||||
func ConnectToServer(address string) (*SnapshotClient, error) {
|
||||
return NewSnapshotClient(address)
|
||||
}
|
158
grpc/server.go
158
grpc/server.go
@ -1,158 +0,0 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"gitea.unprism.ru/KRBL/Agate/interfaces"
|
||||
"gitea.unprism.ru/KRBL/Agate/store"
|
||||
)
|
||||
|
||||
// SnapshotServer implements the gRPC server for snapshots
|
||||
type SnapshotServer struct {
|
||||
UnimplementedSnapshotServiceServer
|
||||
manager interfaces.SnapshotManager
|
||||
server *grpc.Server
|
||||
}
|
||||
|
||||
// NewSnapshotServer creates a new snapshot server
|
||||
func NewSnapshotServer(manager interfaces.SnapshotManager) *SnapshotServer {
|
||||
return &SnapshotServer{
|
||||
manager: manager,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the gRPC server on the specified address
|
||||
func (s *SnapshotServer) Start(ctx context.Context, address string) error {
|
||||
lis, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen on %s: %w", address, err)
|
||||
}
|
||||
|
||||
s.server = grpc.NewServer()
|
||||
RegisterSnapshotServiceServer(s.server, s)
|
||||
|
||||
go func() {
|
||||
if err := s.server.Serve(lis); err != nil {
|
||||
fmt.Printf("Server error: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
fmt.Printf("Server started on %s\n", address)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop gracefully stops the server
|
||||
func (s *SnapshotServer) Stop(ctx context.Context) error {
|
||||
if s.server != nil {
|
||||
s.server.GracefulStop()
|
||||
fmt.Println("Server stopped")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListSnapshots implements the gRPC ListSnapshots method
|
||||
func (s *SnapshotServer) ListSnapshots(ctx context.Context, req *ListSnapshotsRequest) (*ListSnapshotsResponse, error) {
|
||||
// Create empty ListOptions since the proto doesn't have active filter/pagination fields yet
|
||||
opts := store.ListOptions{}
|
||||
|
||||
// Call manager with the required ListOptions parameter
|
||||
snapshots, err := s.manager.ListSnapshots(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list snapshots: %w", err)
|
||||
}
|
||||
|
||||
response := &ListSnapshotsResponse{
|
||||
Snapshots: make([]*SnapshotInfo, 0, len(snapshots)),
|
||||
}
|
||||
|
||||
for _, snapshot := range snapshots {
|
||||
response.Snapshots = append(response.Snapshots, convertToGrpcSnapshotInfo(snapshot))
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetSnapshotDetails implements the gRPC GetSnapshotDetails method
|
||||
func (s *SnapshotServer) GetSnapshotDetails(ctx context.Context, req *GetSnapshotDetailsRequest) (*SnapshotDetails, error) {
|
||||
snapshot, err := s.manager.GetSnapshotDetails(ctx, req.SnapshotId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get snapshot details: %w", err)
|
||||
}
|
||||
|
||||
response := &SnapshotDetails{
|
||||
Info: convertToGrpcSnapshotInfo(store.SnapshotInfo{
|
||||
ID: snapshot.ID,
|
||||
Name: snapshot.Name,
|
||||
ParentID: snapshot.ParentID,
|
||||
CreationTime: snapshot.CreationTime,
|
||||
}),
|
||||
Files: make([]*FileInfo, 0, len(snapshot.Files)),
|
||||
}
|
||||
|
||||
for _, file := range snapshot.Files {
|
||||
response.Files = append(response.Files, &FileInfo{
|
||||
Path: file.Path,
|
||||
SizeBytes: file.Size,
|
||||
Sha256Hash: file.SHA256,
|
||||
IsDir: file.IsDir,
|
||||
})
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// DownloadFile implements the gRPC DownloadFile method
|
||||
func (s *SnapshotServer) DownloadFile(req *DownloadFileRequest, stream grpc.ServerStreamingServer[DownloadFileResponse]) error {
|
||||
// Open the file from the snapshot
|
||||
fileReader, err := s.manager.OpenFile(context.Background(), req.SnapshotId, req.FilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
defer fileReader.Close()
|
||||
|
||||
// Read the file in chunks and send them to the client
|
||||
buffer := make([]byte, 64*1024) // 64KB chunks
|
||||
for {
|
||||
n, err := fileReader.Read(buffer)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
|
||||
// Send the chunk to the client
|
||||
if err := stream.Send(&DownloadFileResponse{
|
||||
ChunkData: buffer[:n],
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to send chunk: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper function to convert store.SnapshotInfo to grpc.SnapshotInfo
|
||||
func convertToGrpcSnapshotInfo(info store.SnapshotInfo) *SnapshotInfo {
|
||||
return &SnapshotInfo{
|
||||
Id: info.ID,
|
||||
Name: info.Name,
|
||||
ParentId: info.ParentID,
|
||||
CreationTime: timestamppb.New(info.CreationTime),
|
||||
}
|
||||
}
|
||||
|
||||
// RunServer is a helper function to create and start a snapshot server
|
||||
func RunServer(ctx context.Context, manager interfaces.SnapshotManager, address string) (*SnapshotServer, error) {
|
||||
server := NewSnapshotServer(manager)
|
||||
if err := server.Start(ctx, address); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return server, nil
|
||||
}
|
@ -26,10 +26,10 @@ const (
|
||||
// Метаданные файла внутри снапшота
|
||||
type FileInfo struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` // Относительный путь файла внутри снапшота
|
||||
SizeBytes int64 `protobuf:"varint,2,opt,name=size_bytes,json=sizeBytes,proto3" json:"size_bytes,omitempty"` // Размер файла в байтах
|
||||
Sha256Hash string `protobuf:"bytes,3,opt,name=sha256_hash,json=sha256Hash,proto3" json:"sha256_hash,omitempty"` // Хеш-сумма файла (SHA256)
|
||||
IsDir bool `protobuf:"varint,4,opt,name=is_dir,json=isDir,proto3" json:"is_dir,omitempty"` // Является ли запись директорией
|
||||
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
|
||||
SizeBytes int64 `protobuf:"varint,2,opt,name=size_bytes,json=sizeBytes,proto3" json:"size_bytes,omitempty"`
|
||||
Sha256Hash string `protobuf:"bytes,3,opt,name=sha256_hash,json=sha256Hash,proto3" json:"sha256_hash,omitempty"`
|
||||
IsDir bool `protobuf:"varint,4,opt,name=is_dir,json=isDir,proto3" json:"is_dir,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -95,10 +95,10 @@ func (x *FileInfo) GetIsDir() bool {
|
||||
// Краткая информация о снапшоте
|
||||
type SnapshotInfo struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Уникальный ID снапшота (UUID)
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Имя снапшота
|
||||
ParentId string `protobuf:"bytes,3,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` // ID родительского снапшота (может быть пустым)
|
||||
CreationTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"` // Время создания
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||
ParentId string `protobuf:"bytes,3,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"`
|
||||
CreationTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -164,8 +164,8 @@ func (x *SnapshotInfo) GetCreationTime() *timestamppb.Timestamp {
|
||||
// Детальная информация о снапшоте
|
||||
type SnapshotDetails struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Info *SnapshotInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"` // Краткая информация
|
||||
Files []*FileInfo `protobuf:"bytes,2,rep,name=files,proto3" json:"files,omitempty"` // Список файлов в снапшоте
|
||||
Info *SnapshotInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"`
|
||||
Files []*FileInfo `protobuf:"bytes,2,rep,name=files,proto3" json:"files,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -214,7 +214,7 @@ func (x *SnapshotDetails) GetFiles() []*FileInfo {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Запрос на получение списка снапшотов (можно добавить фильтры/пагинацию)
|
||||
// Запрос на получение списка снапшотов
|
||||
type ListSnapshotsRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
@ -254,7 +254,7 @@ func (*ListSnapshotsRequest) Descriptor() ([]byte, []int) {
|
||||
// Ответ со списком снапшотов
|
||||
type ListSnapshotsResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Snapshots []*SnapshotInfo `protobuf:"bytes,1,rep,name=snapshots,proto3" json:"snapshots,omitempty"` // string next_page_token = 2;
|
||||
Snapshots []*SnapshotInfo `protobuf:"bytes,1,rep,name=snapshots,proto3" json:"snapshots,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -299,7 +299,7 @@ func (x *ListSnapshotsResponse) GetSnapshots() []*SnapshotInfo {
|
||||
// Запрос на получение деталей снапшота
|
||||
type GetSnapshotDetailsRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
SnapshotId string `protobuf:"bytes,1,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"` // ID нужного снапшота
|
||||
SnapshotId string `protobuf:"bytes,1,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -344,8 +344,8 @@ func (x *GetSnapshotDetailsRequest) GetSnapshotId() string {
|
||||
// Запрос на скачивание файла
|
||||
type DownloadFileRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
SnapshotId string `protobuf:"bytes,1,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"` // ID снапшота
|
||||
FilePath string `protobuf:"bytes,2,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` // Путь к файлу внутри снапшота
|
||||
SnapshotId string `protobuf:"bytes,1,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"`
|
||||
FilePath string `protobuf:"bytes,2,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -397,7 +397,7 @@ func (x *DownloadFileRequest) GetFilePath() string {
|
||||
// Ответ (часть файла) при скачивании
|
||||
type DownloadFileResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
ChunkData []byte `protobuf:"bytes,1,opt,name=chunk_data,json=chunkData,proto3" json:"chunk_data,omitempty"` // Кусочек данных файла
|
||||
ChunkData []byte `protobuf:"bytes,1,opt,name=chunk_data,json=chunkData,proto3" json:"chunk_data,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -439,6 +439,67 @@ func (x *DownloadFileResponse) GetChunkData() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Запрос на скачивание разницы между снапшотами
|
||||
type DownloadSnapshotDiffRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
SnapshotId string `protobuf:"bytes,1,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"` // ID целевого снапшота
|
||||
LocalParentId string `protobuf:"bytes,2,opt,name=local_parent_id,json=localParentId,proto3" json:"local_parent_id,omitempty"` // ID снапшота, который уже есть у клиента
|
||||
Offset int64 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` // Смещение в байтах для докачки
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *DownloadSnapshotDiffRequest) Reset() {
|
||||
*x = DownloadSnapshotDiffRequest{}
|
||||
mi := &file_snapshot_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *DownloadSnapshotDiffRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DownloadSnapshotDiffRequest) ProtoMessage() {}
|
||||
|
||||
func (x *DownloadSnapshotDiffRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_snapshot_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DownloadSnapshotDiffRequest.ProtoReflect.Descriptor instead.
|
||||
func (*DownloadSnapshotDiffRequest) Descriptor() ([]byte, []int) {
|
||||
return file_snapshot_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *DownloadSnapshotDiffRequest) GetSnapshotId() string {
|
||||
if x != nil {
|
||||
return x.SnapshotId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DownloadSnapshotDiffRequest) GetLocalParentId() string {
|
||||
if x != nil {
|
||||
return x.LocalParentId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *DownloadSnapshotDiffRequest) GetOffset() int64 {
|
||||
if x != nil {
|
||||
return x.Offset
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_snapshot_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_snapshot_proto_rawDesc = "" +
|
||||
@ -472,11 +533,17 @@ const file_snapshot_proto_rawDesc = "" +
|
||||
"\tfile_path\x18\x02 \x01(\tR\bfilePath\"5\n" +
|
||||
"\x14DownloadFileResponse\x12\x1d\n" +
|
||||
"\n" +
|
||||
"chunk_data\x18\x01 \x01(\fR\tchunkData2\x8a\x03\n" +
|
||||
"chunk_data\x18\x01 \x01(\fR\tchunkData\"~\n" +
|
||||
"\x1bDownloadSnapshotDiffRequest\x12\x1f\n" +
|
||||
"\vsnapshot_id\x18\x01 \x01(\tR\n" +
|
||||
"snapshotId\x12&\n" +
|
||||
"\x0flocal_parent_id\x18\x02 \x01(\tR\rlocalParentId\x12\x16\n" +
|
||||
"\x06offset\x18\x03 \x01(\x03R\x06offset2\xf1\x03\n" +
|
||||
"\x0fSnapshotService\x12k\n" +
|
||||
"\rListSnapshots\x12 .agate.grpc.ListSnapshotsRequest\x1a!.agate.grpc.ListSnapshotsResponse\"\x15\x82\xd3\xe4\x93\x02\x0f\x12\r/v1/snapshots\x12}\n" +
|
||||
"\x12GetSnapshotDetails\x12%.agate.grpc.GetSnapshotDetailsRequest\x1a\x1b.agate.grpc.SnapshotDetails\"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/v1/snapshots/{snapshot_id}\x12\x8a\x01\n" +
|
||||
"\fDownloadFile\x12\x1f.agate.grpc.DownloadFileRequest\x1a .agate.grpc.DownloadFileResponse\"5\x82\xd3\xe4\x93\x02/\x12-/v1/snapshots/{snapshot_id}/files/{file_path}0\x01B\"Z gitea.unprism.ru/KRBL/Agate/grpcb\x06proto3"
|
||||
"\fDownloadFile\x12\x1f.agate.grpc.DownloadFileRequest\x1a .agate.grpc.DownloadFileResponse\"5\x82\xd3\xe4\x93\x02/\x12-/v1/snapshots/{snapshot_id}/files/{file_path}0\x01\x12e\n" +
|
||||
"\x14DownloadSnapshotDiff\x12'.agate.grpc.DownloadSnapshotDiffRequest\x1a .agate.grpc.DownloadFileResponse\"\x000\x01B\"Z gitea.unprism.ru/KRBL/Agate/grpcb\x06proto3"
|
||||
|
||||
var (
|
||||
file_snapshot_proto_rawDescOnce sync.Once
|
||||
@ -490,31 +557,34 @@ func file_snapshot_proto_rawDescGZIP() []byte {
|
||||
return file_snapshot_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_snapshot_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_snapshot_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
|
||||
var file_snapshot_proto_goTypes = []any{
|
||||
(*FileInfo)(nil), // 0: agate.grpc.FileInfo
|
||||
(*SnapshotInfo)(nil), // 1: agate.grpc.SnapshotInfo
|
||||
(*SnapshotDetails)(nil), // 2: agate.grpc.SnapshotDetails
|
||||
(*ListSnapshotsRequest)(nil), // 3: agate.grpc.ListSnapshotsRequest
|
||||
(*ListSnapshotsResponse)(nil), // 4: agate.grpc.ListSnapshotsResponse
|
||||
(*GetSnapshotDetailsRequest)(nil), // 5: agate.grpc.GetSnapshotDetailsRequest
|
||||
(*DownloadFileRequest)(nil), // 6: agate.grpc.DownloadFileRequest
|
||||
(*DownloadFileResponse)(nil), // 7: agate.grpc.DownloadFileResponse
|
||||
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
|
||||
(*FileInfo)(nil), // 0: agate.grpc.FileInfo
|
||||
(*SnapshotInfo)(nil), // 1: agate.grpc.SnapshotInfo
|
||||
(*SnapshotDetails)(nil), // 2: agate.grpc.SnapshotDetails
|
||||
(*ListSnapshotsRequest)(nil), // 3: agate.grpc.ListSnapshotsRequest
|
||||
(*ListSnapshotsResponse)(nil), // 4: agate.grpc.ListSnapshotsResponse
|
||||
(*GetSnapshotDetailsRequest)(nil), // 5: agate.grpc.GetSnapshotDetailsRequest
|
||||
(*DownloadFileRequest)(nil), // 6: agate.grpc.DownloadFileRequest
|
||||
(*DownloadFileResponse)(nil), // 7: agate.grpc.DownloadFileResponse
|
||||
(*DownloadSnapshotDiffRequest)(nil), // 8: agate.grpc.DownloadSnapshotDiffRequest
|
||||
(*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp
|
||||
}
|
||||
var file_snapshot_proto_depIdxs = []int32{
|
||||
8, // 0: agate.grpc.SnapshotInfo.creation_time:type_name -> google.protobuf.Timestamp
|
||||
9, // 0: agate.grpc.SnapshotInfo.creation_time:type_name -> google.protobuf.Timestamp
|
||||
1, // 1: agate.grpc.SnapshotDetails.info:type_name -> agate.grpc.SnapshotInfo
|
||||
0, // 2: agate.grpc.SnapshotDetails.files:type_name -> agate.grpc.FileInfo
|
||||
1, // 3: agate.grpc.ListSnapshotsResponse.snapshots:type_name -> agate.grpc.SnapshotInfo
|
||||
3, // 4: agate.grpc.SnapshotService.ListSnapshots:input_type -> agate.grpc.ListSnapshotsRequest
|
||||
5, // 5: agate.grpc.SnapshotService.GetSnapshotDetails:input_type -> agate.grpc.GetSnapshotDetailsRequest
|
||||
6, // 6: agate.grpc.SnapshotService.DownloadFile:input_type -> agate.grpc.DownloadFileRequest
|
||||
4, // 7: agate.grpc.SnapshotService.ListSnapshots:output_type -> agate.grpc.ListSnapshotsResponse
|
||||
2, // 8: agate.grpc.SnapshotService.GetSnapshotDetails:output_type -> agate.grpc.SnapshotDetails
|
||||
7, // 9: agate.grpc.SnapshotService.DownloadFile:output_type -> agate.grpc.DownloadFileResponse
|
||||
7, // [7:10] is the sub-list for method output_type
|
||||
4, // [4:7] is the sub-list for method input_type
|
||||
8, // 7: agate.grpc.SnapshotService.DownloadSnapshotDiff:input_type -> agate.grpc.DownloadSnapshotDiffRequest
|
||||
4, // 8: agate.grpc.SnapshotService.ListSnapshots:output_type -> agate.grpc.ListSnapshotsResponse
|
||||
2, // 9: agate.grpc.SnapshotService.GetSnapshotDetails:output_type -> agate.grpc.SnapshotDetails
|
||||
7, // 10: agate.grpc.SnapshotService.DownloadFile:output_type -> agate.grpc.DownloadFileResponse
|
||||
7, // 11: agate.grpc.SnapshotService.DownloadSnapshotDiff:output_type -> agate.grpc.DownloadFileResponse
|
||||
8, // [8:12] is the sub-list for method output_type
|
||||
4, // [4:8] is the sub-list for method input_type
|
||||
4, // [4:4] is the sub-list for extension type_name
|
||||
4, // [4:4] is the sub-list for extension extendee
|
||||
0, // [0:4] is the sub-list for field type_name
|
||||
@ -531,7 +601,7 @@ func file_snapshot_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_snapshot_proto_rawDesc), len(file_snapshot_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 8,
|
||||
NumMessages: 9,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ syntax = "proto3";
|
||||
package agate.grpc;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/api/annotations.proto"; // Добавлено для HTTP mapping
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
option go_package = "gitea.unprism.ru/KRBL/Agate/grpc";
|
||||
|
||||
@ -30,77 +30,59 @@ service SnapshotService {
|
||||
};
|
||||
}
|
||||
|
||||
// --- Методы для управления (опционально, можно не включать в публичный API клиента) ---
|
||||
// Создать новый снапшот из директории (если серверу позволено инициировать)
|
||||
// rpc CreateSnapshot(CreateSnapshotRequest) returns (Snapshot);
|
||||
// Удалить снапшот (если требуется)
|
||||
// rpc DeleteSnapshot(DeleteSnapshotRequest) returns (DeleteSnapshotResponse);
|
||||
// Скачать архив, содержащий только разницу между двумя снапшотами
|
||||
rpc DownloadSnapshotDiff(DownloadSnapshotDiffRequest) returns (stream DownloadFileResponse) {}
|
||||
}
|
||||
|
||||
// Метаданные файла внутри снапшота
|
||||
message FileInfo {
|
||||
string path = 1; // Относительный путь файла внутри снапшота
|
||||
int64 size_bytes = 2; // Размер файла в байтах
|
||||
string sha256_hash = 3; // Хеш-сумма файла (SHA256)
|
||||
bool is_dir = 4; // Является ли запись директорией
|
||||
string path = 1;
|
||||
int64 size_bytes = 2;
|
||||
string sha256_hash = 3;
|
||||
bool is_dir = 4;
|
||||
}
|
||||
|
||||
// Краткая информация о снапшоте
|
||||
message SnapshotInfo {
|
||||
string id = 1; // Уникальный ID снапшота (UUID)
|
||||
string name = 2; // Имя снапшота
|
||||
string parent_id = 3; // ID родительского снапшота (может быть пустым)
|
||||
google.protobuf.Timestamp creation_time = 4; // Время создания
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string parent_id = 3;
|
||||
google.protobuf.Timestamp creation_time = 4;
|
||||
}
|
||||
|
||||
// Детальная информация о снапшоте
|
||||
message SnapshotDetails {
|
||||
SnapshotInfo info = 1; // Краткая информация
|
||||
repeated FileInfo files = 2; // Список файлов в снапшоте
|
||||
SnapshotInfo info = 1;
|
||||
repeated FileInfo files = 2;
|
||||
}
|
||||
|
||||
// Запрос на получение списка снапшотов (можно добавить фильтры/пагинацию)
|
||||
message ListSnapshotsRequest {
|
||||
// string filter_by_name = 1;
|
||||
// int32 page_size = 2;
|
||||
// string page_token = 3;
|
||||
}
|
||||
// Запрос на получение списка снапшотов
|
||||
message ListSnapshotsRequest {}
|
||||
|
||||
// Ответ со списком снапшотов
|
||||
message ListSnapshotsResponse {
|
||||
repeated SnapshotInfo snapshots = 1;
|
||||
// string next_page_token = 2;
|
||||
}
|
||||
|
||||
// Запрос на получение деталей снапшота
|
||||
message GetSnapshotDetailsRequest {
|
||||
string snapshot_id = 1; // ID нужного снапшота
|
||||
string snapshot_id = 1;
|
||||
}
|
||||
|
||||
// Запрос на скачивание файла
|
||||
message DownloadFileRequest {
|
||||
string snapshot_id = 1; // ID снапшота
|
||||
string file_path = 2; // Путь к файлу внутри снапшота
|
||||
string snapshot_id = 1;
|
||||
string file_path = 2;
|
||||
}
|
||||
|
||||
// Ответ (часть файла) при скачивании
|
||||
message DownloadFileResponse {
|
||||
bytes chunk_data = 1; // Кусочек данных файла
|
||||
bytes chunk_data = 1;
|
||||
}
|
||||
|
||||
// --- Сообщения для опциональных методов управления ---
|
||||
/*
|
||||
message CreateSnapshotRequest {
|
||||
string source_path = 1; // Путь к директории на сервере
|
||||
string name = 2;
|
||||
string parent_id = 3; // Опционально
|
||||
// Запрос на скачивание разницы между снапшотами
|
||||
message DownloadSnapshotDiffRequest {
|
||||
string snapshot_id = 1; // ID целевого снапшота
|
||||
string local_parent_id = 2; // ID снапшота, который уже есть у клиента
|
||||
int64 offset = 3; // Смещение в байтах для докачки
|
||||
}
|
||||
|
||||
message DeleteSnapshotRequest {
|
||||
string snapshot_id = 1;
|
||||
}
|
||||
|
||||
message DeleteSnapshotResponse {
|
||||
bool success = 1;
|
||||
}
|
||||
*/
|
@ -19,9 +19,10 @@ import (
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
SnapshotService_ListSnapshots_FullMethodName = "/agate.grpc.SnapshotService/ListSnapshots"
|
||||
SnapshotService_GetSnapshotDetails_FullMethodName = "/agate.grpc.SnapshotService/GetSnapshotDetails"
|
||||
SnapshotService_DownloadFile_FullMethodName = "/agate.grpc.SnapshotService/DownloadFile"
|
||||
SnapshotService_ListSnapshots_FullMethodName = "/agate.grpc.SnapshotService/ListSnapshots"
|
||||
SnapshotService_GetSnapshotDetails_FullMethodName = "/agate.grpc.SnapshotService/GetSnapshotDetails"
|
||||
SnapshotService_DownloadFile_FullMethodName = "/agate.grpc.SnapshotService/DownloadFile"
|
||||
SnapshotService_DownloadSnapshotDiff_FullMethodName = "/agate.grpc.SnapshotService/DownloadSnapshotDiff"
|
||||
)
|
||||
|
||||
// SnapshotServiceClient is the client API for SnapshotService service.
|
||||
@ -36,6 +37,8 @@ type SnapshotServiceClient interface {
|
||||
GetSnapshotDetails(ctx context.Context, in *GetSnapshotDetailsRequest, opts ...grpc.CallOption) (*SnapshotDetails, error)
|
||||
// Скачать конкретный файл из снапшота (потоковая передача)
|
||||
DownloadFile(ctx context.Context, in *DownloadFileRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DownloadFileResponse], error)
|
||||
// Скачать архив, содержащий только разницу между двумя снапшотами
|
||||
DownloadSnapshotDiff(ctx context.Context, in *DownloadSnapshotDiffRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DownloadFileResponse], error)
|
||||
}
|
||||
|
||||
type snapshotServiceClient struct {
|
||||
@ -85,6 +88,25 @@ func (c *snapshotServiceClient) DownloadFile(ctx context.Context, in *DownloadFi
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type SnapshotService_DownloadFileClient = grpc.ServerStreamingClient[DownloadFileResponse]
|
||||
|
||||
func (c *snapshotServiceClient) DownloadSnapshotDiff(ctx context.Context, in *DownloadSnapshotDiffRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DownloadFileResponse], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &SnapshotService_ServiceDesc.Streams[1], SnapshotService_DownloadSnapshotDiff_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[DownloadSnapshotDiffRequest, DownloadFileResponse]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type SnapshotService_DownloadSnapshotDiffClient = grpc.ServerStreamingClient[DownloadFileResponse]
|
||||
|
||||
// SnapshotServiceServer is the server API for SnapshotService service.
|
||||
// All implementations must embed UnimplementedSnapshotServiceServer
|
||||
// for forward compatibility.
|
||||
@ -97,6 +119,8 @@ type SnapshotServiceServer interface {
|
||||
GetSnapshotDetails(context.Context, *GetSnapshotDetailsRequest) (*SnapshotDetails, error)
|
||||
// Скачать конкретный файл из снапшота (потоковая передача)
|
||||
DownloadFile(*DownloadFileRequest, grpc.ServerStreamingServer[DownloadFileResponse]) error
|
||||
// Скачать архив, содержащий только разницу между двумя снапшотами
|
||||
DownloadSnapshotDiff(*DownloadSnapshotDiffRequest, grpc.ServerStreamingServer[DownloadFileResponse]) error
|
||||
mustEmbedUnimplementedSnapshotServiceServer()
|
||||
}
|
||||
|
||||
@ -116,6 +140,9 @@ func (UnimplementedSnapshotServiceServer) GetSnapshotDetails(context.Context, *G
|
||||
func (UnimplementedSnapshotServiceServer) DownloadFile(*DownloadFileRequest, grpc.ServerStreamingServer[DownloadFileResponse]) error {
|
||||
return status.Errorf(codes.Unimplemented, "method DownloadFile not implemented")
|
||||
}
|
||||
func (UnimplementedSnapshotServiceServer) DownloadSnapshotDiff(*DownloadSnapshotDiffRequest, grpc.ServerStreamingServer[DownloadFileResponse]) error {
|
||||
return status.Errorf(codes.Unimplemented, "method DownloadSnapshotDiff not implemented")
|
||||
}
|
||||
func (UnimplementedSnapshotServiceServer) mustEmbedUnimplementedSnapshotServiceServer() {}
|
||||
func (UnimplementedSnapshotServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@ -184,6 +211,17 @@ func _SnapshotService_DownloadFile_Handler(srv interface{}, stream grpc.ServerSt
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type SnapshotService_DownloadFileServer = grpc.ServerStreamingServer[DownloadFileResponse]
|
||||
|
||||
func _SnapshotService_DownloadSnapshotDiff_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(DownloadSnapshotDiffRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(SnapshotServiceServer).DownloadSnapshotDiff(m, &grpc.GenericServerStream[DownloadSnapshotDiffRequest, DownloadFileResponse]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type SnapshotService_DownloadSnapshotDiffServer = grpc.ServerStreamingServer[DownloadFileResponse]
|
||||
|
||||
// SnapshotService_ServiceDesc is the grpc.ServiceDesc for SnapshotService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -206,6 +244,11 @@ var SnapshotService_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _SnapshotService_DownloadFile_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "DownloadSnapshotDiff",
|
||||
Handler: _SnapshotService_DownloadSnapshotDiff_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "snapshot.proto",
|
||||
}
|
||||
|
Reference in New Issue
Block a user