Skip to content

Commit 62ff396

Browse files
FEATURE (storages): Add NAS storage
1 parent 34afe9a commit 62ff396

27 files changed

+855
-4
lines changed

backend/.env.development.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ TEST_POSTGRES_16_PORT=5004
2424
TEST_POSTGRES_17_PORT=5005
2525
# testing S3
2626
TEST_MINIO_PORT=9000
27-
TEST_MINIO_CONSOLE_PORT=9001
27+
TEST_MINIO_CONSOLE_PORT=9001
28+
# testing NAS
29+
TEST_NAS_PORT=5006

backend/docker-compose.yml.example

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,19 @@ services:
8686
- POSTGRES_PASSWORD=testpassword
8787
container_name: test-postgres-17
8888
shm_size: 1gb
89+
90+
# Test NAS server (Samba)
91+
test-nas:
92+
image: dperson/samba:latest
93+
ports:
94+
- "${TEST_NAS_PORT:-445}:445"
95+
environment:
96+
- USERID=1000
97+
- GROUPID=1000
98+
volumes:
99+
- ./temp/nas:/shared
100+
command: >
101+
-u "testuser;testpassword"
102+
-s "backups;/shared;yes;no;no;testuser"
103+
-p
104+
container_name: test-nas

backend/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ require (
2929
cloud.google.com/go/auth v0.16.2 // indirect
3030
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
3131
cloud.google.com/go/compute/metadata v0.7.0 // indirect
32+
github.com/geoffgarside/ber v1.1.0 // indirect
3233
github.com/google/s2a-go v0.1.9 // indirect
3334
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
3435
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
36+
github.com/hirochachacha/go-smb2 v1.1.0
3537
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
3638
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
3739
google.golang.org/grpc v1.73.0 // indirect

backend/go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
3535
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
3636
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
3737
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
38+
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
39+
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
3840
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
3941
github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
4042
github.com/gin-contrib/gzip v1.2.3 h1:dAhT722RuEG330ce2agAs75z7yB+NKvX/ZM1r8w0u2U=
@@ -91,6 +93,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU
9193
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
9294
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
9395
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
96+
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
97+
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
9498
github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4=
9599
github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk=
96100
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
@@ -210,12 +214,14 @@ go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2
210214
golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU=
211215
golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
212216
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
217+
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
213218
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
214219
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
215220
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
216221
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
217222
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
218223
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
224+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
219225
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
220226
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
221227
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
@@ -230,6 +236,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
230236
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
231237
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
232238
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
239+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
233240
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
234241
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
235242
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

backend/internal/config/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ type EnvVariables struct {
4141

4242
TestMinioPort string `env:"TEST_MINIO_PORT"`
4343
TestMinioConsolePort string `env:"TEST_MINIO_CONSOLE_PORT"`
44+
45+
TestNASPort string `env:"TEST_NAS_PORT"`
4446
}
4547

4648
var (
@@ -161,6 +163,11 @@ func loadEnvVariables() {
161163
log.Error("TEST_MINIO_CONSOLE_PORT is empty")
162164
os.Exit(1)
163165
}
166+
167+
if env.TestNASPort == "" {
168+
log.Error("TEST_NAS_PORT is empty")
169+
os.Exit(1)
170+
}
164171
}
165172

166173
log.Info("Environment variables loaded successfully!")

backend/internal/features/storages/enums.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ const (
66
StorageTypeLocal StorageType = "LOCAL"
77
StorageTypeS3 StorageType = "S3"
88
StorageTypeGoogleDrive StorageType = "GOOGLE_DRIVE"
9+
StorageTypeNAS StorageType = "NAS"
910
)

backend/internal/features/storages/model.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"log/slog"
77
google_drive_storage "postgresus-backend/internal/features/storages/models/google_drive"
88
local_storage "postgresus-backend/internal/features/storages/models/local"
9+
nas_storage "postgresus-backend/internal/features/storages/models/nas"
910
s3_storage "postgresus-backend/internal/features/storages/models/s3"
1011

1112
"github.com/google/uuid"
@@ -22,6 +23,7 @@ type Storage struct {
2223
LocalStorage *local_storage.LocalStorage `json:"localStorage" gorm:"foreignKey:StorageID"`
2324
S3Storage *s3_storage.S3Storage `json:"s3Storage" gorm:"foreignKey:StorageID"`
2425
GoogleDriveStorage *google_drive_storage.GoogleDriveStorage `json:"googleDriveStorage" gorm:"foreignKey:StorageID"`
26+
NASStorage *nas_storage.NASStorage `json:"nasStorage" gorm:"foreignKey:StorageID"`
2527
}
2628

2729
func (s *Storage) SaveFile(logger *slog.Logger, fileID uuid.UUID, file io.Reader) error {
@@ -69,6 +71,8 @@ func (s *Storage) getSpecificStorage() StorageFileSaver {
6971
return s.S3Storage
7072
case StorageTypeGoogleDrive:
7173
return s.GoogleDriveStorage
74+
case StorageTypeNAS:
75+
return s.NASStorage
7276
default:
7377
panic("invalid storage type: " + string(s.Type))
7478
}

backend/internal/features/storages/model_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import (
1010
"postgresus-backend/internal/config"
1111
google_drive_storage "postgresus-backend/internal/features/storages/models/google_drive"
1212
local_storage "postgresus-backend/internal/features/storages/models/local"
13+
nas_storage "postgresus-backend/internal/features/storages/models/nas"
1314
s3_storage "postgresus-backend/internal/features/storages/models/s3"
1415
"postgresus-backend/internal/util/logger"
16+
"strconv"
1517
"testing"
1618
"time"
1719

@@ -44,6 +46,14 @@ func Test_Storage_BasicOperations(t *testing.T) {
4446
require.NoError(t, err, "Failed to setup test file")
4547
defer os.Remove(testFilePath)
4648

49+
// Setup NAS port
50+
nasPort := 445
51+
if portStr := config.GetEnv().TestNASPort; portStr != "" {
52+
if port, err := strconv.Atoi(portStr); err == nil {
53+
nasPort = port
54+
}
55+
}
56+
4757
// Run tests
4858
testCases := []struct {
4959
name string
@@ -73,6 +83,20 @@ func Test_Storage_BasicOperations(t *testing.T) {
7383
TokenJSON: config.GetEnv().TestGoogleDriveTokenJSON,
7484
},
7585
},
86+
{
87+
name: "NASStorage",
88+
storage: &nas_storage.NASStorage{
89+
StorageID: uuid.New(),
90+
Host: "localhost",
91+
Port: nasPort,
92+
Share: "backups",
93+
Username: "testuser",
94+
Password: "testpassword",
95+
UseSSL: false,
96+
Domain: "",
97+
Path: "test-files",
98+
},
99+
},
76100
}
77101

78102
for _, tc := range testCases {
@@ -201,4 +225,5 @@ func validateEnvVariables(t *testing.T) {
201225
assert.NotEmpty(t, env.TestGoogleDriveClientSecret, "TEST_GOOGLE_DRIVE_CLIENT_SECRET is empty")
202226
assert.NotEmpty(t, env.TestGoogleDriveTokenJSON, "TEST_GOOGLE_DRIVE_TOKEN_JSON is empty")
203227
assert.NotEmpty(t, env.TestMinioPort, "TEST_MINIO_PORT is empty")
228+
assert.NotEmpty(t, env.TestNASPort, "TEST_NAS_PORT is empty")
204229
}

0 commit comments

Comments
 (0)