@@ -14,6 +14,7 @@ import type {ILocalData, LocalStorage, Logger} from '@verdaccio/types';
1414 path : string ;
1515 logger : Logger ;
1616 data : LocalStorage ;
17+ locked : boolean ;
1718
1819 /**
1920 * Load an parse the local json database.
@@ -22,34 +23,84 @@ import type {ILocalData, LocalStorage, Logger} from '@verdaccio/types';
2223 constructor ( path : string , logger : Logger ) {
2324 this . path = path ;
2425 this . logger = logger ;
26+ this . locked = false ;
27+ this . data = this . _fetchLocalPackages ( ) ;
28+ }
29+
30+ /**
31+ * Fetch local packages.
32+ * @private
33+ * @return {Object }
34+ */
35+ _fetchLocalPackages ( ) {
36+ const emptyDatabase = { list : [ ] } ;
37+
2538 try {
26- this . data = JSON . parse ( fs . readFileSync ( this . path , 'utf8' ) ) ;
27- } catch ( _ ) {
28- this . data = { list : [ ] } ;
39+ const dbFile = fs . readFileSync ( this . path , 'utf8' ) ;
40+
41+ if ( ! dbFile ) { // readFileSync is platform specific, FreeBSD might return null
42+ return emptyDatabase ;
43+ }
44+
45+ const db = this . _parseDatabase ( dbFile ) ;
46+
47+ if ( ! db ) {
48+ return emptyDatabase ;
49+ }
50+
51+ return db ;
52+ } catch ( err ) {
53+ // readFileSync is platform specific, macOS, Linux and Windows thrown an error
54+ // Only recreate if file not found to prevent data loss
55+ if ( err . code !== 'ENOENT' ) {
56+ this . locked = true ;
57+ this . logger . error (
58+ 'Failed to read package database file, please check the error printed below:\n' ,
59+ `File Path: ${ this . path } \n\n ${ err . message } `
60+ ) ;
61+ }
62+ return emptyDatabase ;
63+ }
64+ }
65+
66+ /**
67+ * Parse the local database.
68+ * @param {Object } dbFile
69+ * @private
70+ * @return {Object }
71+ */
72+ _parseDatabase ( dbFile : any ) {
73+ try {
74+ return JSON . parse ( dbFile ) ;
75+ } catch ( err ) {
76+ this . logger . error ( `Package database file corrupted (invalid JSON), please check the error printed below.\nFile Path: ${ this . path } ` , err ) ;
77+ this . locked = true ;
2978 }
3079 }
3180
3281 /**
3382 * Add a new element.
3483 * @param {* } name
84+ * @return {Error|* }
3585 */
3686 add ( name : string ) {
3787 if ( this . data . list . indexOf ( name ) === - 1 ) {
3888 this . data . list . push ( name ) ;
39- this . sync ( ) ;
89+ return this . sync ( ) ;
4090 }
4191 }
4292
4393 /**
4494 * Remove an element from the database.
4595 * @param {* } name
96+ * @return {Error|* }
4697 */
4798 remove ( name : string ) {
4899 const i = this . data . list . indexOf ( name ) ;
49100 if ( i !== - 1 ) {
50101 this . data . list . splice ( i , 1 ) ;
51102 }
52- this . sync ( ) ;
103+ return this . sync ( ) ;
53104 }
54105
55106 /**
@@ -62,16 +113,26 @@ import type {ILocalData, LocalStorage, Logger} from '@verdaccio/types';
62113
63114 /**
64115 * Syncronize {create} database whether does not exist.
116+ * @return {Error|* }
65117 */
66118 sync ( ) {
119+ if ( this . locked ) {
120+ this . logger . error ( 'Database is locked, please check error message printed during startup to prevent data loss.' ) ;
121+ return new Error ( 'Verdaccio database is locked, please contact your administrator to checkout logs during verdaccio startup.' ) ;
122+ }
67123 // Uses sync to prevent ugly race condition
68124 try {
69125 mkdirp . sync ( Path . dirname ( this . path ) ) ;
70126 } catch ( err ) {
71127 // perhaps a logger instance?
72128 /* eslint no-empty:off */
73129 }
74- fs . writeFileSync ( this . path , JSON . stringify ( this . data ) ) ;
130+
131+ try {
132+ fs . writeFileSync ( this . path , JSON . stringify ( this . data ) ) ;
133+ } catch ( err ) {
134+ return err ;
135+ }
75136 }
76137
77138}
0 commit comments