re organizer project

This commit is contained in:
m
2026-07-12 13:44:15 +02:00
parent 999fbaf7fa
commit 6b8c64bd40
20 changed files with 529 additions and 479 deletions
-3
View File
@@ -7,9 +7,7 @@ import (
func CreateSHA256Hash(data []byte) []byte {
hasher := sha256.New()
hasher.Write(data)
return hasher.Sum(nil)
}
@@ -17,6 +15,5 @@ func CompareHash(x, y []byte) bool {
if len(x) != len(y) {
return false
}
return subtle.ConstantTimeCompare(x, y) == 1
}
+155
View File
@@ -0,0 +1,155 @@
package service
import (
"context"
"encoding/hex"
"fmt"
"mime/multipart"
"os"
"path/filepath"
"github.com/google/uuid"
"github.com/vaultdrop/backend/internal/config"
"github.com/vaultdrop/backend/internal/db"
"github.com/vaultdrop/backend/internal/model"
)
type FileService struct {
queries *db.Queries
cfg *config.Config
}
func NewFileService(queries *db.Queries, cfg *config.Config) *FileService {
return &FileService{queries: queries, cfg: cfg}
}
func (s *FileService) Upload(file *multipart.FileHeader) (*model.UploadResult, error) {
dst := filepath.Join(s.cfg.UploadDir, uuid.New().String()+filepath.Ext(file.Filename))
if err := os.MkdirAll(s.cfg.UploadDir, 0o755); err != nil {
return nil, fmt.Errorf("create upload dir: %w", err)
}
if err := saveUploadedFile(file, dst); err != nil {
return nil, fmt.Errorf("save file: %w", err)
}
data, err := os.ReadFile(dst)
if err != nil {
return nil, fmt.Errorf("read saved file: %w", err)
}
info, err := os.Stat(dst)
if err != nil {
return nil, fmt.Errorf("stat file: %w", err)
}
id := uuid.New().String()
checksum := hex.EncodeToString(CreateSHA256Hash(data))
dbFile, err := s.queries.CreateFile(context.Background(), db.CreateFileParams{
ID: id,
Name: file.Filename,
MimeType: file.Header.Get("Content-Type"),
Size: info.Size(),
StorageKey: dst,
Checksum: checksum,
})
if err != nil {
return nil, fmt.Errorf("create file in db: %w", err)
}
return &model.UploadResult{
ID: dbFile.ID,
Name: dbFile.Name,
}, nil
}
func (s *FileService) List() ([]model.File, error) {
dbFiles, err := s.queries.ListFiles(context.Background())
if err != nil {
return nil, fmt.Errorf("list files: %w", err)
}
files := make([]model.File, len(dbFiles))
for i, f := range dbFiles {
files[i] = dbToModel(f)
}
return files, nil
}
func (s *FileService) Get(id string) (*model.File, error) {
f, err := s.queries.GetFile(context.Background(), id)
if err != nil {
return nil, fmt.Errorf("get file: %w", err)
}
m := dbToModel(f)
return &m, nil
}
func (s *FileService) Delete(id string) error {
return s.queries.DeleteFile(context.Background(), id)
}
func (s *FileService) GetStoragePath(id string) (string, error) {
f, err := s.queries.GetFile(context.Background(), id)
if err != nil {
return "", fmt.Errorf("get file: %w", err)
}
return f.StorageKey, nil
}
func (s *FileService) UpdateOCRText(id, text string) error {
f, err := s.queries.GetFile(context.Background(), id)
if err != nil {
return fmt.Errorf("get file: %w", err)
}
return s.queries.UpdateFile(context.Background(), db.UpdateFileParams{
Name: f.Name,
MimeType: f.MimeType,
OcrText: text,
ID: id,
})
}
func dbToModel(f db.File) model.File {
return model.File{
ID: f.ID,
Name: f.Name,
MimeType: f.MimeType,
Size: f.Size,
StorageKey: f.StorageKey,
Checksum: f.Checksum,
OcrText: f.OcrText,
CreatedAt: f.CreatedAt.String(),
UpdatedAt: f.UpdatedAt.String(),
}
}
func saveUploadedFile(file *multipart.FileHeader, dst string) error {
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
buf := make([]byte, 32*1024)
for {
n, readErr := src.Read(buf)
if n > 0 {
if _, writeErr := out.Write(buf[:n]); writeErr != nil {
return writeErr
}
}
if readErr != nil {
break
}
}
return nil
}
+44
View File
@@ -0,0 +1,44 @@
package service
import (
"fmt"
"os"
"strings"
"github.com/vaultdrop/backend/internal/config"
"github.com/vaultdrop/backend/internal/ocr"
)
type OCRService struct {
client *ocr.Client
}
func NewOCRService(cfg *config.Config) *OCRService {
return &OCRService{
client: ocr.NewClient(cfg.OCREndpoint),
}
}
func (s *OCRService) RecognizeFromFile(filePath string) ([]ocr.TextBlock, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return nil, fmt.Errorf("read file: %w", err)
}
return s.client.Recognize(data)
}
func (s *OCRService) RecognizeFromBytes(data []byte) ([]ocr.TextBlock, error) {
return s.client.Recognize(data)
}
func (s *OCRService) FlattenResults(blocks []ocr.TextBlock) string {
var texts []string
for _, b := range blocks {
texts = append(texts, b.Text)
}
return strings.Join(texts, "\n")
}
func (s *OCRService) HealthCheck() error {
return s.client.HealthCheck()
}
-41
View File
@@ -1,41 +0,0 @@
package service
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"time"
)
const secret = "thisismyrandomstring"
func sign(fileID string, expires int64, secret string) string {
data := fmt.Sprintf("%s:%d", fileID, expires)
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(data))
return hex.EncodeToString(mac.Sum(nil))
}
func GenerateFileDownloadUrl(fileID string) string {
expires := time.Now().Add(10 * time.Minute).Unix()
sig := sign(fileID, expires, secret)
url := fmt.Sprintf(
"http://192.168.1.17:8080/api/v1/files/%s?expires=%d&sig=%s",
fileID,
expires,
sig,
)
return url
}
func Validate(fileID, sig string, expires int64) bool {
if time.Now().Unix() > expires {
return false
}
expected := sign(fileID, expires, secret)
return hmac.Equal([]byte(sig), []byte(expected))
}
+46
View File
@@ -0,0 +1,46 @@
package service
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"time"
)
type URLService struct {
secret string
serverHost string
}
func NewURLService(secret, serverHost string) *URLService {
return &URLService{secret: secret, serverHost: serverHost}
}
func (s *URLService) sign(fileID string, expires int64) string {
data := fmt.Sprintf("%s:%d", fileID, expires)
mac := hmac.New(sha256.New, []byte(s.secret))
mac.Write([]byte(data))
return hex.EncodeToString(mac.Sum(nil))
}
func (s *URLService) GenerateDownloadURL(fileUUID string) string {
expires := time.Now().Add(10 * time.Minute).Unix()
sig := s.sign(fileUUID, expires)
return fmt.Sprintf(
"%s/api/v1/files/%s?expires=%d&sig=%s",
s.serverHost,
fileUUID,
expires,
sig,
)
}
func (s *URLService) Validate(fileID, sig string, expires int64) bool {
if time.Now().Unix() > expires {
return false
}
expected := s.sign(fileID, expires)
return hmac.Equal([]byte(sig), []byte(expected))
}