@@ -10,6 +10,7 @@ dotenv.config();
1010const app = express ( ) ;
1111let client ;
1212let db ;
13+ let isConnected = false ;
1314
1415app . use ( express . json ( { limit : '50mb' } ) ) ;
1516app . use ( express . urlencoded ( { limit : '50mb' , extended : true } ) ) ;
@@ -45,14 +46,35 @@ app.use((req, res, next) => {
4546const MAX_FILE_SIZE = 10 * 1024 * 1024 ;
4647const MAX_TOTAL_ATTACHMENTS_SIZE = 50 * 1024 * 1024 ;
4748
48- async function setupDatabase ( ) {
49+ async function connectToDatabase ( ) {
4950 try {
50- client = new MongoClient ( process . env . MONGODB_URI , {
51- serverApi : ServerApiVersion . v1 ,
52- } ) ;
51+ if ( ! client ) {
52+ client = new MongoClient ( process . env . MONGODB_URI , {
53+ serverApi : ServerApiVersion . v1 ,
54+ maxPoolSize : 50 ,
55+ connectTimeoutMS : 5000 ,
56+ socketTimeoutMS : 45000 ,
57+ retryWrites : true ,
58+ retryReads : true
59+ } ) ;
60+ }
61+
62+ if ( ! isConnected ) {
63+ await client . connect ( ) ;
64+ db = client . db ( 'notes-sync' ) ;
65+ isConnected = true ;
66+ console . log ( 'Connected to MongoDB' ) ;
67+ }
68+ } catch ( error ) {
69+ isConnected = false ;
70+ console . error ( 'MongoDB connection failed:' , error ) ;
71+ throw error ;
72+ }
73+ }
5374
54- await client . connect ( ) ;
55- db = client . db ( 'notes-sync' ) ;
75+ async function setupDatabase ( ) {
76+ try {
77+ await connectToDatabase ( ) ;
5678
5779 await db . collection ( 'notes' ) . createIndexes ( [
5880 { key : { public_key : 1 } } ,
@@ -65,76 +87,83 @@ async function setupDatabase() {
6587 { expireAfterSeconds : 180 * 24 * 60 * 60 }
6688 ) ;
6789
68- console . log ( 'Connected to MongoDB' ) ;
6990 } catch ( error ) {
7091 console . error ( 'Database setup failed:' , error ) ;
7192 process . exit ( 1 ) ;
7293 }
7394}
7495
96+ async function ensureConnection ( ) {
97+ if ( ! isConnected ) {
98+ await connectToDatabase ( ) ;
99+ }
100+ }
101+
75102app . post ( '/api/sync' , async ( req , res ) => {
76- console . log ( 'Received sync request:' , {
77- hasPublicKey : ! ! req . body . public_key ,
78- notesCount : req . body . notes ?. length ,
79- clientVersion : req . body . client_version ,
80- contentType : req . headers [ 'content-type' ]
81- } ) ;
103+ try {
104+ await ensureConnection ( ) ;
105+
106+ console . log ( 'Received sync request:' , {
107+ hasPublicKey : ! ! req . body . public_key ,
108+ notesCount : req . body . notes ?. length ,
109+ clientVersion : req . body . client_version ,
110+ contentType : req . headers [ 'content-type' ]
111+ } ) ;
82112
83- const { public_key, notes, client_version } = req . body ;
113+ const { public_key, notes, client_version } = req . body ;
84114
85- if ( ! public_key || ! Array . isArray ( notes ) ) {
86- console . log ( 'Invalid request format:' , {
87- hasPublicKey : ! ! public_key ,
88- hasNotes : ! ! notes ,
89- isNotesArray : Array . isArray ( notes )
90- } ) ;
91- return res . status ( 400 ) . json ( {
92- error : 'Invalid request format' ,
93- details : {
94- hasPublicKey : ! ! public_key ,
115+ if ( ! public_key || ! Array . isArray ( notes ) ) {
116+ console . log ( 'Invalid request format:' , {
117+ hasPublicKey : ! ! public_key ,
95118 hasNotes : ! ! notes ,
96119 isNotesArray : Array . isArray ( notes )
97- }
98- } ) ;
99- }
120+ } ) ;
121+ return res . status ( 400 ) . json ( {
122+ error : 'Invalid request format' ,
123+ details : {
124+ hasPublicKey : ! ! public_key ,
125+ hasNotes : ! ! notes ,
126+ isNotesArray : Array . isArray ( notes )
127+ }
128+ } ) ;
129+ }
100130
101- const MIN_CLIENT_VERSION = '0.1.0' ;
102- if ( client_version < MIN_CLIENT_VERSION ) {
103- return res . status ( 400 ) . json ( {
104- error : 'Please update your client to the latest version'
105- } ) ;
106- }
131+ const MIN_CLIENT_VERSION = '0.1.0' ;
132+ if ( client_version < MIN_CLIENT_VERSION ) {
133+ return res . status ( 400 ) . json ( {
134+ error : 'Please update your client to the latest version'
135+ } ) ;
136+ }
107137
108- for ( const note of notes ) {
109- if ( note . attachments ) {
110- let totalSize = 0 ;
111- for ( const attachment of note . attachments ) {
112- const fileSize = Buffer . from ( attachment . data , 'base64' ) . length ;
113- if ( fileSize > MAX_FILE_SIZE ) {
138+ for ( const note of notes ) {
139+ if ( note . attachments ) {
140+ let totalSize = 0 ;
141+ for ( const attachment of note . attachments ) {
142+ const fileSize = Buffer . from ( attachment . data , 'base64' ) . length ;
143+ if ( fileSize > MAX_FILE_SIZE ) {
144+ return res . status ( 400 ) . json ( {
145+ error : 'File size exceeds limit' ,
146+ details : {
147+ fileName : attachment . name ,
148+ size : fileSize ,
149+ limit : MAX_FILE_SIZE
150+ }
151+ } ) ;
152+ }
153+ totalSize += fileSize ;
154+ }
155+ if ( totalSize > MAX_TOTAL_ATTACHMENTS_SIZE ) {
114156 return res . status ( 400 ) . json ( {
115- error : 'File size exceeds limit' ,
157+ error : 'Total attachments size exceeds limit' ,
116158 details : {
117- fileName : attachment . name ,
118- size : fileSize ,
119- limit : MAX_FILE_SIZE
159+ totalSize,
160+ limit : MAX_TOTAL_ATTACHMENTS_SIZE
120161 }
121162 } ) ;
122163 }
123- totalSize += fileSize ;
124- }
125- if ( totalSize > MAX_TOTAL_ATTACHMENTS_SIZE ) {
126- return res . status ( 400 ) . json ( {
127- error : 'Total attachments size exceeds limit' ,
128- details : {
129- totalSize,
130- limit : MAX_TOTAL_ATTACHMENTS_SIZE
131- }
132- } ) ;
133164 }
134165 }
135- }
136166
137- try {
138167 await db . collection ( 'users' ) . updateOne (
139168 { public_key } ,
140169 {
@@ -167,13 +196,14 @@ app.post('/api/sync', async (req, res) => {
167196 res . json ( results ) ;
168197 } catch ( error ) {
169198 console . error ( 'Sync error:' , error ) ;
199+ isConnected = false ;
170200
171201 await db . collection ( 'error_logs' ) . insertOne ( {
172- public_key,
202+ public_key : req . body ?. public_key ,
173203 timestamp : new Date ( ) ,
174204 error : error instanceof Error ? error . message : 'Unknown error' ,
175205 stack : error instanceof Error ? error . stack : undefined ,
176- } ) ;
206+ } ) . catch ( console . error ) ;
177207
178208 res . status ( 500 ) . json ( {
179209 error : 'Internal server error' ,
@@ -259,6 +289,7 @@ async function processNotes(public_key, incoming_notes) {
259289
260290app . get ( '/api/health' , async ( req , res ) => {
261291 try {
292+ await ensureConnection ( ) ;
262293 await db . command ( { ping : 1 } ) ;
263294 res . json ( {
264295 status : 'healthy' ,
@@ -267,6 +298,7 @@ app.get('/api/health', async (req, res) => {
267298 timestamp : new Date ( ) . toISOString ( )
268299 } ) ;
269300 } catch ( error ) {
301+ isConnected = false ;
270302 console . error ( 'Health check failed:' , error ) ;
271303 res . status ( 500 ) . json ( {
272304 status : 'unhealthy' ,
@@ -316,4 +348,34 @@ process.on('SIGINT', async () => {
316348 console . error ( 'Error during shutdown:' , error ) ;
317349 process . exit ( 1 ) ;
318350 }
351+ } ) ;
352+
353+ client . on ( 'connectionClosed' , ( ) => {
354+ console . log ( 'MongoDB connection closed' ) ;
355+ isConnected = false ;
356+ } ) ;
357+
358+ client . on ( 'error' , ( error ) => {
359+ console . error ( 'MongoDB error:' , error ) ;
360+ isConnected = false ;
361+ } ) ;
362+
363+ process . on ( 'uncaughtException' , async ( error ) => {
364+ console . error ( 'Uncaught exception:' , error ) ;
365+ try {
366+ await client ?. close ( ) ;
367+ } catch ( err ) {
368+ console . error ( 'Error while closing MongoDB connection:' , err ) ;
369+ }
370+ process . exit ( 1 ) ;
371+ } ) ;
372+
373+ process . on ( 'unhandledRejection' , async ( reason , promise ) => {
374+ console . error ( 'Unhandled Rejection at:' , promise , 'reason:' , reason ) ;
375+ try {
376+ await client ?. close ( ) ;
377+ } catch ( err ) {
378+ console . error ( 'Error while closing MongoDB connection:' , err ) ;
379+ }
380+ process . exit ( 1 ) ;
319381} ) ;
0 commit comments