Skip to content
6 changes: 5 additions & 1 deletion backend/db/init/01-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ CREATE TABLE file_metadata(
-- owner UUID NOT NULL REFERENCES users(id)
);

-- INDEX FOR FILE METADATA PAGINATION
CREATE INDEX CONCURRENTLY idx_file_owner_modified_desc
ON file_metadata (owner, modified_at DESC, id DESC);

-- DIRECTORY METADATA
CREATE TABLE dir_metadata(
id UUID PRIMARY KEY NOT NULL,
Expand All @@ -68,4 +72,4 @@ CREATE TABLE file_metadata_access(
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
file_id UUID NOT NULL REFERENCES file_metadata(id) ON DELETE CASCADE,
PRIMARY KEY (user_id, file_id)
);
);
13 changes: 13 additions & 0 deletions backend/src/core/files/dto/file_request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dto

import "github.com/google/uuid"

type GetAllFilesRequest struct {
UserID uuid.UUID `json:"user_id"`
Index uuid.UUID `json:"index"`
}

type GetFileRequest struct {
UserID uuid.UUID `json:"user_id"`
FileID uuid.UUID `json:"file_id"`
}
41 changes: 41 additions & 0 deletions backend/src/core/files/dto/file_response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dto

import (
files "backend/src/core/files/model"
"time"

"github.com/google/uuid"
)

type FileDTO struct {
ID uuid.UUID `json:"uuid"`
FileName string `json:"file_name"`
Path string `json:"path"`
Size uint64 `json:"size"`
FileType string `json:"file_type"`
//Mode fs.FileMode
//IsDir bool
ModifiedAt time.Time `json:"modified_at"`
CreatedAt time.Time `json:"created_at"`
Owner uuid.UUID `json:"owner_id"`
AccessTo []uuid.UUID `json:"access_to"`
Group []uuid.UUID `json:"group_id"`
//Links *uint64
Version time.Time `json:"version"`
}

func MapToResponse(m files.MetaData) FileDTO {
return FileDTO{
ID: m.ID,
FileName: m.FileName,
Path: m.Path,
Size: m.Size,
FileType: m.FileType,
CreatedAt: m.CreatedAt,
ModifiedAt: m.ModifiedAt,
Owner: m.Owner,
AccessTo: m.AccessTo,
Group: m.Group,
Version: m.Version,
}
}
20 changes: 14 additions & 6 deletions backend/src/core/files/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package files

import (
service "backend/src/core/files/service"
"encoding/json"
"backend/src/internal/api/message"
"log"
"net/http"
)
Expand All @@ -28,12 +28,20 @@ func (h *Handler) Upload(w http.ResponseWriter, r *http.Request) {
http.Error(w, "could not save file", http.StatusInternalServerError)
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
err := json.NewEncoder(w).Encode(map[string]string{
"status": "uploaded",
})
err := message.Response(w, "uploaded")
if err != nil {
log.Print(err)
return
}
}

func (h *Handler) GetAll(w http.ResponseWriter, r *http.Request) {
svc := h.svc

userUUID := r.URL.Query().Get("user_uuid")
files, err := svc.GetAll(userUUID, r.Context())
if err != nil {
log.Printf("couldn't get all user's files: %v", err)
http.Error(w, "unable to get user's files", http.StatusInternalServerError)
}
}
65 changes: 65 additions & 0 deletions backend/src/core/files/repository/repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package files

import (
"backend/src/core/files/dto"
files "backend/src/core/files/model"
"backend/src/internal/metadb"
"context"
Expand Down Expand Up @@ -84,3 +85,67 @@ func (repo *Repository) SaveFileData(

return nil
}

func (repo *Repository) GetAllFiles(ctx context.Context, req dto.GetAllFilesRequest) ([]files.MetaData, error) {
var db = repo.db.Pool

const query = `
SELECT
id,
file_name,
path,
size,
file_type,
modified_at,
uploaded_at,
version,
checksum,
owner
FROM file_metadata
WHERE owner = $1
AND (modified_at, id) < ($2, $3)
ORDER BY modified_at, id
LIMIT 20;
`

var result []files.MetaData

rows, err := db.Query(
ctx,
query,
req.UserID,
req.Cursor.ModifiedAt,
req.Cursor.ID,
)
if err != nil {
return nil, err
}
defer rows.Close()

for rows.Next() {
var model files.MetaData
if err := rows.Scan(
&model.ID,
&model.FileName,
&model.Path,
&model.Size,
&model.FileType,
&model.ModifiedAt,
&model.CreatedAt,
&model.Owner,
&model.AccessTo,
&model.Group,
&model.Version,
); err != nil {
return nil, err
}

result = append(result, model)
}

if err := rows.Err(); err != nil {
return nil, err
}

return result, nil
}
19 changes: 19 additions & 0 deletions backend/src/core/files/service/service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package files

// TODO: Finish test, verify upload works correctly.
//func UploadTest_TestFileUpload(t *testing.T) {
// fileContents := []byte("This is a test file")
// fileName := "testfile.txt"
//
// var requestBody bytes.Buffer
// writer := multipart.NewWriter(&requestBody)
//
// part, err := writer.CreateFormFile("file", fileName)
// if err != nil {
// t.Fatalf("failed to create form file: %v", err)
// }
//
// Writer.Close()
//
// req := httptest.NewRequest(rest.MethodPut, "files/upload", &requestBody)
//}
51 changes: 51 additions & 0 deletions backend/src/internal/api/message/dto_mapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package message

import (
"log"
"reflect"
)

func mapFields[T any](src any) (T, error) {
var response T

dstValue := reflect.ValueOf(&response).Elem()
srcValue := reflect.Indirect(reflect.ValueOf(src))
dstType := dstValue.Type()

for i := 0; i < dstType.NumField(); i++ {
df := dstType.Field(i)
dv := dstValue.Field(i)

if !dv.CanSet() {
log.Printf("could not map value: %v, is it unexported or read-only?", dv)
continue
}

name := df.Tag.Get("map")
if name == "" {
name = df.Name
}

sf := srcValue.FieldByName(name)
if !sf.IsValid() || sf.Type() != dv.Type() {
log.Printf("src field invalid %v", sf)
continue
}

dv.Set(sf)
}

return response, nil
}

func ToRequest[T any](src any) (T, error) {
return mapFields[T](src)
}

func ToResponse[T any](src any) (T, error) {
return mapFields[T](src)
}

func ToModel[T any](src any) (T, error) {
return mapFields[T](src)
}
3 changes: 3 additions & 0 deletions backend/src/internal/api/message/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package message

func Response()
55 changes: 55 additions & 0 deletions backend/src/internal/api/rest/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package rest

import (
"backend/src/internal/app"
"bytes"
"encoding/json"
"net/http"
)

type AppConfig struct {
Config *app.Config
}

func (app AppConfig) Post(endpointURL string, payload any) (*http.Response, error) {
body, err := json.Marshal(payload)
if err != nil {
return nil, err
}

url := app.buildURL(endpointURL)

req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(body))
if err != nil {
return nil, err
}

req.Header.Set("Content-Type", "application/json")
client := app.Config.HTTPClient

return client.Do(req)
}

func (app AppConfig) Get() (http.Response, error) {
panic("Not implemented.")
}

func (app AppConfig) Delete() (http.Response, error) {
panic("Not implemented.")
}

func (app AppConfig) Put() (http.Response, error) {
panic("Not implemented.")
}

func (app AppConfig) Trace() (http.Response, error) {
panic("Not implemented.")
}

func (app AppConfig) Patch() (http.Response, error) {
panic("Not implemented.")
}

func (app AppConfig) buildURL(endpointUrl string) string {
return app.Config.BaseURL + endpointUrl
}
8 changes: 8 additions & 0 deletions backend/src/internal/api/rest/http_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package rest

//func TestHTTP_Post(t *testing.T) {
// var gotMethod string
// var gotContentType string
// var gotBody map[string]any
//
//}
Loading
Loading