@@ -25,6 +25,7 @@ if (process.platform === 'win32') {
2525
2626class SyncToDrive {
2727 constructor ( ) {
28+ this . validateRsync ( ) ;
2829 this . config = this . fetchConfig ( ) ;
2930 if ( ! this . config . synctodrive . enabled ) {
3031 error ( 'Sync to drive is disabled.' ) ;
@@ -47,6 +48,15 @@ class SyncToDrive {
4748 this . start ( ) ;
4849 }
4950
51+ validateRsync ( ) {
52+ try {
53+ execSync ( 'command -v rsync' , { stdio : 'ignore' } ) ;
54+ } catch ( err ) {
55+ log ( err . message ) ;
56+ error ( 'Error: rsync is not installed. Please install it and try again.' ) ;
57+ }
58+ }
59+
5060 start ( ) {
5161 try {
5262 const device = this . findDevice ( this . driveName ) ;
@@ -82,31 +92,51 @@ class SyncToDrive {
8292 throw new Error ( 'Folder ' + this . source + ' does not exist!' ) ;
8393 }
8494
85- log ( 'Starting sync to USB drive ...' ) ;
86- const distinationPath = path . join ( device . mountpoint , this . destination ) ;
87- if ( ! fs . existsSync ( distinationPath ) ) {
88- log ( 'Creating target directory' + distinationPath ) ;
89- fs . mkdirSync ( distinationPath , { recursive : true } ) ;
95+ if ( ! device . mountpoint ) {
96+ throw new Error ( 'Error: USB device is not properly mounted.' ) ;
9097 }
9198
92- log ( 'Source data folder ' + this . source ) ;
93- log ( 'Syncing to drive ' + device . path + ' -> ' + distinationPath ) ;
99+ const destinationPath = path . join ( device . mountpoint , this . destination ) ;
100+ if ( ! fs . existsSync ( destinationPath ) ) {
101+ log ( `Creating target directory ${ destinationPath } ` ) ;
102+ try {
103+ fs . mkdirSync ( destinationPath , { recursive : true } ) ;
104+ } catch ( err ) {
105+ throw new Error ( `Error: Failed to create directory ${ destinationPath } - ${ err . message } ` ) ;
106+ }
107+ }
108+
109+ if ( ! this . isWritable ( destinationPath ) ) {
110+ throw new Error ( `Error: Destination ${ destinationPath } is not writable.` ) ;
111+ }
94112
95113 const command = [
96114 'rsync' ,
97115 '-a' ,
98- '--delete-before' ,
99116 '-b' ,
117+ '--delete-before' ,
100118 '--backup-dir=' + path . join ( device . mountpoint , 'deleted' ) ,
101119 '--ignore-existing' ,
102- '--include=\'*.\' {jpg,chk,gif,mp4}' ,
120+ '--include=\'*.{jpg,chk,gif,mp4}\' ' ,
103121 '--include=\'*/\'' ,
104122 '--exclude=\'*\'' ,
105123 '--prune-empty-dirs' ,
106124 this . source ,
107- path . join ( device . mountpoint , this . destination )
125+ destinationPath
108126 ] . join ( ' ' ) ;
109- log ( 'Executing command "' + command + '"' ) ;
127+
128+ log ( 'Validating rsync command...' ) ;
129+ try {
130+ execSync ( command + ' --dry-run' , { stdio : 'ignore' } ) ;
131+ // eslint-disable-next-line no-unused-vars
132+ } catch ( err ) {
133+ throw new Error ( 'Error: Rsync validation failed. Check permissions and paths.' ) ;
134+ }
135+
136+ log ( 'Starting sync to USB drive ...' ) ;
137+ log ( 'Source data folder ' + this . source ) ;
138+ log ( 'Syncing to drive ' + device . path + ' -> ' + destinationPath ) ;
139+ log ( `Executing command: "${ command } "` ) ;
110140
111141 this . rsyncProcess = spawn ( command , {
112142 shell : '/bin/bash'
@@ -118,8 +148,7 @@ class SyncToDrive {
118148 } ) ;
119149 this . rsyncProcess . on ( 'exit' , ( ) => {
120150 this . rsyncProcess = null ;
121- log ( 'Sync finished' ) ;
122- log ( 'Next run in ' + this . intervalInSeconds + 's' ) ;
151+ log ( 'Sync finished. Next run in ' + this . intervalInSeconds + 's' ) ;
123152 setTimeout ( ( ) => {
124153 this . start ( ) ;
125154 } , this . intervalInMilliseconds ) ;
@@ -228,6 +257,16 @@ class SyncToDrive {
228257 this . stop ( ) ;
229258 }
230259
260+ isWritable ( directory ) {
261+ try {
262+ fs . accessSync ( directory , fs . constants . W_OK ) ;
263+ return true ;
264+ // eslint-disable-next-line no-unused-vars
265+ } catch ( err ) {
266+ return false ;
267+ }
268+ }
269+
231270 fetchConfig ( ) {
232271 try {
233272 const cmd = 'bin/photobooth photobooth:config:list json' ;
0 commit comments