db-data-gd-uploader 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +276 -0
- package/config/service-account.json +13 -0
- package/package.json +66 -0
- package/src/app.js +69 -0
- package/src/config/database.js +38 -0
- package/src/config/environment.js +55 -0
- package/src/index.js +14 -0
- package/src/routes/authRoutes.js +15 -0
- package/src/routes/backupRoutes.js +38 -0
- package/src/scripts/backup.js +44 -0
- package/src/scripts/scheduler.js +15 -0
- package/src/server.js +54 -0
- package/src/services/backupScheduler.js +142 -0
- package/src/services/backupService.js +85 -0
- package/src/services/googleAuth.js +12 -0
- package/src/services/googleDrive.js +245 -0
- package/src/utils/dataExtract.js +175 -0
- package/src/utils/excelFormatter.js +153 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Saud Ahmad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# GD Uploader š¦
|
|
2
|
+
|
|
3
|
+
**Professional Database Backup System with Google Drive Integration**
|
|
4
|
+
|
|
5
|
+
Export MongoDB/any database to Excel and upload to Google Drive automatically - no manual intervention required!
|
|
6
|
+
|
|
7
|
+
[](https://npmjs.org/package/db-data-gd-uploader)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
[](https://github.com/Saud-web-dev/GD-module)
|
|
11
|
+
|
|
12
|
+
## ⨠Features
|
|
13
|
+
|
|
14
|
+
- šļø **Multi-Database Support** - Extract from MongoDB, any database with Node drivers
|
|
15
|
+
- š **Excel Export** - Professional formatted Excel files with headers, styling, borders
|
|
16
|
+
- āļø **Google Drive Upload** - Automatic upload using Service Account (no OAuth needed)
|
|
17
|
+
- š **Secure** - Service Account authentication, no personal credentials exposed
|
|
18
|
+
- šÆ **Zero Config** - Works out of the box with minimal setup
|
|
19
|
+
- ā” **Fast** - Streams large datasets efficiently
|
|
20
|
+
- š
**Scheduled Backups** - Built-in scheduler for automated backups
|
|
21
|
+
|
|
22
|
+
## š Quick Start
|
|
23
|
+
|
|
24
|
+
### Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install db-data-gd-uploader
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Setup (One-time)
|
|
31
|
+
|
|
32
|
+
1. **Create Google Cloud Service Account**
|
|
33
|
+
```bash
|
|
34
|
+
# Go to Google Cloud Console
|
|
35
|
+
# Create service account ā Generate JSON key
|
|
36
|
+
# Save to: config/service-account.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
2. **Share Folder with Service Account**
|
|
40
|
+
- Go to Google Drive
|
|
41
|
+
- Create folder for backups
|
|
42
|
+
- Right-click ā Share
|
|
43
|
+
- Add Service Account email: `your-account@project.iam.gserviceaccount.com`
|
|
44
|
+
- Give Editor permission
|
|
45
|
+
|
|
46
|
+
3. **Set Environment Variables**
|
|
47
|
+
```bash
|
|
48
|
+
cp .env.example .env
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```env
|
|
52
|
+
MONGODB_URI=mongodb://localhost:27017/your-db
|
|
53
|
+
GOOGLE_DRIVE_FOLDER_ID=your-folder-id
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Usage
|
|
57
|
+
|
|
58
|
+
#### Basic Backup
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
import { createAndUploadCompleteBackup } from 'db-data-gd-uploader'
|
|
62
|
+
|
|
63
|
+
const result = await createAndUploadCompleteBackup({
|
|
64
|
+
uploadToDrive: true,
|
|
65
|
+
workbookName: 'my-backup'
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
console.log(result)
|
|
69
|
+
// Output:
|
|
70
|
+
// {
|
|
71
|
+
// uploaded: true,
|
|
72
|
+
// fileName: 'my-backup.xlsx',
|
|
73
|
+
// databases: ['stc_db', 'test'],
|
|
74
|
+
// collections: [...],
|
|
75
|
+
// totalRecords: 1000,
|
|
76
|
+
// driveFile: { id: '...', webViewLink: '...' }
|
|
77
|
+
// }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### Upload Excel to Drive
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
import { uploadExcelToGoogleDrive } from 'db-data-gd-uploader/drive'
|
|
84
|
+
|
|
85
|
+
const result = await uploadExcelToGoogleDrive({
|
|
86
|
+
buffer: excelBuffer,
|
|
87
|
+
fileName: 'data-export.xlsx',
|
|
88
|
+
folderId: 'optional-folder-id'
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Generate Excel
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
import { createExcelWorkbook } from 'db-data-gd-uploader/excel'
|
|
96
|
+
|
|
97
|
+
const workbook = await createExcelWorkbook([
|
|
98
|
+
{
|
|
99
|
+
modelName: 'users',
|
|
100
|
+
collectionName: 'users',
|
|
101
|
+
database: 'mydb',
|
|
102
|
+
data: [
|
|
103
|
+
{ _id: 1, name: 'John', email: 'john@example.com' },
|
|
104
|
+
{ _id: 2, name: 'Jane', email: 'jane@example.com' }
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
], {
|
|
108
|
+
workbookName: 'users-export'
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Extract Data from Database
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
import { extractAllDatabasesCollections } from 'db-data-gd-uploader/extract'
|
|
116
|
+
|
|
117
|
+
const data = await extractAllDatabasesCollections(
|
|
118
|
+
'mongodb://localhost:27017/mydb',
|
|
119
|
+
['admin', 'config', 'local'] // Excluded databases
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
console.log(data)
|
|
123
|
+
// [
|
|
124
|
+
// { database: 'mydb', collectionName: 'users', data: [...] },
|
|
125
|
+
// { database: 'mydb', collectionName: 'products', data: [...] }
|
|
126
|
+
// ]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## š API Reference
|
|
130
|
+
|
|
131
|
+
### `createAndUploadCompleteBackup(options)`
|
|
132
|
+
|
|
133
|
+
Main function - extracts all databases, creates Excel, uploads to Drive.
|
|
134
|
+
|
|
135
|
+
**Options:**
|
|
136
|
+
- `uploadToDrive` (boolean) - Upload to Google Drive? Default: `true`
|
|
137
|
+
- `workbookName` (string) - Excel file name. Default: `complete-backup-YYYY-MM-DD`
|
|
138
|
+
- `folderId` (string) - Google Drive folder ID. Default: from `.env`
|
|
139
|
+
- `excludedDatabases` (array) - Skip these databases. Default: `['admin', 'config', 'local']`
|
|
140
|
+
|
|
141
|
+
**Returns:** Promise<Object>
|
|
142
|
+
- `uploaded` (boolean)
|
|
143
|
+
- `fileName` (string)
|
|
144
|
+
- `databases` (array)
|
|
145
|
+
- `collections` (array)
|
|
146
|
+
- `totalRecords` (number)
|
|
147
|
+
- `driveFile` (object) - Google Drive file info
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### `uploadExcelToGoogleDrive(options)`
|
|
152
|
+
|
|
153
|
+
Upload Excel file to Google Drive.
|
|
154
|
+
|
|
155
|
+
**Options:**
|
|
156
|
+
- `buffer` (Buffer) - Excel file buffer **[required]**
|
|
157
|
+
- `fileName` (string) - File name **[required]**
|
|
158
|
+
- `contentType` (string) - MIME type. Default: Excel
|
|
159
|
+
- `folderId` (string) - Google Drive folder ID
|
|
160
|
+
|
|
161
|
+
**Returns:** Promise<Object>
|
|
162
|
+
- `id` (string) - Google Drive file ID
|
|
163
|
+
- `webViewLink` (string) - Shareable link
|
|
164
|
+
- `status` (string) - 'uploaded' or 'local_only'
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### `createExcelWorkbook(data, options)`
|
|
169
|
+
|
|
170
|
+
Create formatted Excel workbook.
|
|
171
|
+
|
|
172
|
+
**Parameters:**
|
|
173
|
+
- `data` (array) - Collection data with structure: `{ modelName, collectionName, database, data }`
|
|
174
|
+
- `options.workbookName` (string) - Workbook name
|
|
175
|
+
|
|
176
|
+
**Returns:** Promise<Object>
|
|
177
|
+
- `buffer` (Buffer) - Excel file binary
|
|
178
|
+
- `fileName` (string)
|
|
179
|
+
- `contentType` (string)
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### `extractAllDatabasesCollections(mongoUri, excludedDatabases)`
|
|
184
|
+
|
|
185
|
+
Extract all collections from all databases.
|
|
186
|
+
|
|
187
|
+
**Parameters:**
|
|
188
|
+
- `mongoUri` (string) - MongoDB connection string
|
|
189
|
+
- `excludedDatabases` (array) - Databases to skip
|
|
190
|
+
|
|
191
|
+
**Returns:** Promise<Array>
|
|
192
|
+
- `[{ database, collectionName, documentCount, data }]`
|
|
193
|
+
|
|
194
|
+
## š§ Environment Variables
|
|
195
|
+
|
|
196
|
+
```env
|
|
197
|
+
# MongoDB
|
|
198
|
+
MONGODB_URI=mongodb://localhost:27017/mydb
|
|
199
|
+
|
|
200
|
+
# Google Drive
|
|
201
|
+
GOOGLE_DRIVE_FOLDER_ID=your-folder-id
|
|
202
|
+
|
|
203
|
+
# Service Account (or use config/service-account.json)
|
|
204
|
+
GOOGLE_SERVICE_ACCOUNT_JSON={"type":"service_account",...}
|
|
205
|
+
|
|
206
|
+
# Optional: Scheduler settings
|
|
207
|
+
BACKUP_SCHEDULE_TIME=02:00 # 2 AM daily
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## š Project Structure
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
gd-uploader/
|
|
214
|
+
āāā src/
|
|
215
|
+
ā āāā index.js # Main entry point
|
|
216
|
+
ā āāā services/
|
|
217
|
+
ā ā āāā backupService.js # Main backup logic
|
|
218
|
+
ā ā āāā googleDrive.js # Google Drive operations
|
|
219
|
+
ā ā āāā googleAuth.js # Authentication
|
|
220
|
+
ā āāā utils/
|
|
221
|
+
ā ā āāā excelFormatter.js # Excel generation
|
|
222
|
+
ā ā āāā dataExtract.js # Database extraction
|
|
223
|
+
ā āāā scripts/
|
|
224
|
+
ā āāā backup.js # CLI script
|
|
225
|
+
āāā config/
|
|
226
|
+
ā āāā service-account.json # Google Service Account
|
|
227
|
+
āāā .env.example
|
|
228
|
+
āāā package.json
|
|
229
|
+
āāā README.md
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## š Security
|
|
233
|
+
|
|
234
|
+
- ā
No personal Google credentials stored
|
|
235
|
+
- ā
Service Account uses read-only for data extraction
|
|
236
|
+
- ā
All sensitive data in `.env` (not committed)
|
|
237
|
+
- ā
No hardcoded credentials
|
|
238
|
+
|
|
239
|
+
## š Troubleshooting
|
|
240
|
+
|
|
241
|
+
### "Service Account has no storage quota"
|
|
242
|
+
**Solution:** Use Shared Drive or share personal folder with Service Account email
|
|
243
|
+
|
|
244
|
+
### "MONGODB_URI is required"
|
|
245
|
+
**Solution:** Set `MONGODB_URI` in `.env` or environment variables
|
|
246
|
+
|
|
247
|
+
### "Invalid GOOGLE_SERVICE_ACCOUNT_JSON"
|
|
248
|
+
**Solution:** Place valid JSON in `config/service-account.json` or `.env`
|
|
249
|
+
|
|
250
|
+
## š¦ Publishing to npm
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# 1. Login to npm
|
|
254
|
+
npm login
|
|
255
|
+
|
|
256
|
+
# 2. Update version in package.json
|
|
257
|
+
npm version patch # or minor/major
|
|
258
|
+
|
|
259
|
+
# 3. Publish
|
|
260
|
+
npm publish
|
|
261
|
+
|
|
262
|
+
# 4. Check
|
|
263
|
+
npm info gd-uploader
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## š License
|
|
267
|
+
|
|
268
|
+
MIT Ā© [Saud Ahmad]
|
|
269
|
+
|
|
270
|
+
## š¤ Contributing
|
|
271
|
+
|
|
272
|
+
Contributions welcome! Please feel free to submit a Pull Request.
|
|
273
|
+
|
|
274
|
+
## š§ Support
|
|
275
|
+
|
|
276
|
+
For issues and questions: https://github.com/Saud-web-dev/GD-module/issues
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "service_account",
|
|
3
|
+
"project_id": "gd-uploader-saud",
|
|
4
|
+
"private_key_id": "f4b306f4f39ddea962be2c18f62754210a881a47",
|
|
5
|
+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCw6mu1O1DwPkUL\nzgiK/BXB3UbZRqNzERnp5LdeY1TBWknnvm0GnWoNQjFSVKq38KjSG0ekJ4eIYy+N\nqLFd+o460LuYkORBJQSN3OxXQym/aV3e12fcgWyncAKxyF+4KJtMWI7ir2ih9BcS\nkQ+bDX82LGZeG9uCu51FzG5jKFJ/JueTjSG0DZamFvaXZU4P+fYe+m5r4cFMt8yL\nLFbkoh+Pn8yEY2tKKyTdmA4oX74g0cB8JaNo6Y72TgPsX7aVU7MWlB3LCykFRcj3\nkVvsPJZLRiWGPSjM/F4YWE/MltJ4zrvk1rPr8HHpxGwaV3+8ZRj7D1S8f5Mfk6MW\ngSadzunxAgMBAAECggEAA1uGgBpftzqygX57FAZOp4h2O2Mx+UBfzQa6e8L/TSZc\nv2/fCkshoZ4NNeiwtWf1IQ1EFhmvaymU3HzzRD9+CKi37aSbpBdlH/bpSGitmbfd\noqJD1xu2GHr+WhjupS77kalSVBUUd8YcS7hGuGBuXUFV3CWymOMQTBK3csvWipGo\nLsZj3n+FWuasqbHstE7RR8rac+EgMq4PrYfOFlu3on4542HRAiQDUNjbyt6Mt+xZ\ns8B8NoyNlZxD5/26olQrqBQMo5rt87tT+oEE7doxaxqiUyd7eyZEQ7UoQ1Cv+U6/\nxMJVYze7xBJ5qqexJhTl4ANRxT9Qd2dJJcOJzQiKbwKBgQDkfts7Z83krHWld6cV\n9UexJJkqiovE4QRk2mfFq5KrqzBDM+EYlugH2OzNPyrqKyj/Cmjk2hwSxbnkyO38\nZCeylUFVfd/v36nkYUzZ3gR8B9en7OaaEMS4VIgpyPwxrvNlmS0O51Jt0s6k2eib\natCApfXF/eMCdjwF7IchxGx5LwKBgQDGNh6u+WDXx+j+OTFPgANqXGa3ls3BEqsw\nAmGrv2xBIGTkZBbNInV7LaW8cvbSZN+k8Zel30fpHt/JbzjmexqQi8/7qFl7MNun\n45jXjghkJhjvtib/Xt9FCCLXhH7wmGRYzhElJCqF9wjnxBoatdXybV/wnXE+Hxd2\nCHURuBnG3wKBgQDDopmyUz5F/CL+eWPluQyY7uz1L4pcFwzcDOOsqyn5Makw4Mpp\nc0tW7LRTRzJHLIz9ULxetSA0MnbnG+InptkWXhSCTjPd/XJIx8Y1A5JzX4OVk5Ad\neR31dOrgW+UR5Okgo/WgPQ6mWUf0fV5bMykx/OZhju8+vFzH9av9jWASoQKBgQCE\ndR5EJ/sDuGCHo4jEa/PcXCt4fJq7b9JenvjOYXnie9dwmUJzi5ee3X5oVRuEtkyO\nWbTR8EiKDUvXvS/1NkcmWYh/0J0bqibgJ0S4p/4LjzoS11NwTOI/q5Q9GhoKhoxo\nnCzw+R5BsDiPSYl4WBPFkjoO/efzb6uqATTaxoDTWQKBgQC7BMJ4Bq2IQtpZzXp4\nkafOraq47O8xXE5V9jg6wlLEw2zWWK9wRGy+ByC7Cx8H5dGRq8z87lUCAIE4SJnr\n8YUWkWhxcOJ/PIj+qkWZUq3HewjzeZRFFFlIwCYK7qM9yPkrMZWmx6ZIY6ILkgMW\nRux3wxiq+cjEs55QGtxqBgmciA==\n-----END PRIVATE KEY-----\n",
|
|
6
|
+
"client_email": "gd-uploader@gd-uploader-saud.iam.gserviceaccount.com",
|
|
7
|
+
"client_id": "111930094607475043849",
|
|
8
|
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
9
|
+
"token_uri": "https://oauth2.googleapis.com/token",
|
|
10
|
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
11
|
+
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/gd-uploader%40gd-uploader-saud.iam.gserviceaccount.com",
|
|
12
|
+
"universe_domain": "googleapis.com"
|
|
13
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "db-data-gd-uploader",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Professional Database Backup System with Google Drive Integration. Export MongoDB/any database to Excel and upload to Google Drive automatically.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"database",
|
|
7
|
+
"backup",
|
|
8
|
+
"google-drive",
|
|
9
|
+
"mongodb",
|
|
10
|
+
"excel",
|
|
11
|
+
"export",
|
|
12
|
+
"cloud-storage",
|
|
13
|
+
"db-export",
|
|
14
|
+
"data-backup"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Saud Ahmad",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/Saud-web-dev/GD-module.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/Saud-web-dev/GD-module/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/Saud-web-dev/GD-module#readme",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "src/index.js",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": "./src/index.js",
|
|
30
|
+
"./backup": "./src/services/backupService.js",
|
|
31
|
+
"./drive": "./src/services/googleDrive.js",
|
|
32
|
+
"./excel": "./src/utils/excelFormatter.js",
|
|
33
|
+
"./extract": "./src/utils/dataExtract.js"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=16.0.0"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"start": "node src/server.js",
|
|
40
|
+
"dev": "nodemon src/server.js",
|
|
41
|
+
"backup": "node src/scripts/backup.js",
|
|
42
|
+
"scheduler": "node src/scripts/scheduler.js",
|
|
43
|
+
"lint": "node --check src/index.js src/services/*.js src/utils/*.js",
|
|
44
|
+
"test": "node --check src/server.js && node --check src/app.js && node --check src/config/database.js && node --check src/services/backupService.js",
|
|
45
|
+
"prepublishOnly": "npm run lint && npm run test"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"cookie-parser": "^1.4.7",
|
|
49
|
+
"cors": "^2.8.6",
|
|
50
|
+
"dotenv": "^17.4.2",
|
|
51
|
+
"exceljs": "^4.4.0",
|
|
52
|
+
"express": "^5.2.1",
|
|
53
|
+
"googleapis": "^180.0.0",
|
|
54
|
+
"jsonwebtoken": "^9.0.3",
|
|
55
|
+
"mongoose": "^9.10.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"nodemon": "^3.1.14"
|
|
59
|
+
},
|
|
60
|
+
"files": [
|
|
61
|
+
"src/",
|
|
62
|
+
"config/",
|
|
63
|
+
"README.md",
|
|
64
|
+
"LICENSE"
|
|
65
|
+
]
|
|
66
|
+
}
|
package/src/app.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import cors from 'cors'
|
|
2
|
+
import express from 'express'
|
|
3
|
+
import mongoose from 'mongoose'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import config from './config/environment.js'
|
|
7
|
+
import authRoutes from './routes/authRoutes.js'
|
|
8
|
+
import backupRoutes from './routes/backupRoutes.js'
|
|
9
|
+
import { getDatabaseStatus } from './config/database.js'
|
|
10
|
+
|
|
11
|
+
const __dirname = join(fileURLToPath(import.meta.url), '..')
|
|
12
|
+
|
|
13
|
+
export function createApp() {
|
|
14
|
+
const app = express()
|
|
15
|
+
|
|
16
|
+
// Middleware
|
|
17
|
+
app.use(cors({ origin: config.frontendUrl }))
|
|
18
|
+
app.use(express.json())
|
|
19
|
+
|
|
20
|
+
// Health check endpoint
|
|
21
|
+
app.get('/api/health', (_request, response) => {
|
|
22
|
+
response.json({
|
|
23
|
+
ok: true,
|
|
24
|
+
database: getDatabaseStatus(),
|
|
25
|
+
environment: config.nodeEnv,
|
|
26
|
+
timestamp: new Date().toISOString(),
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// Routes
|
|
31
|
+
app.use('/auth', authRoutes)
|
|
32
|
+
app.use('/api', backupRoutes)
|
|
33
|
+
|
|
34
|
+
// Download endpoint
|
|
35
|
+
app.get('/download/:fileName', (request, response) => {
|
|
36
|
+
const { fileName } = request.params
|
|
37
|
+
const filePath = join(__dirname, '..', 'exports', fileName)
|
|
38
|
+
|
|
39
|
+
// Security: prevent path traversal
|
|
40
|
+
if (!filePath.startsWith(join(__dirname, '..', 'exports'))) {
|
|
41
|
+
return response.status(400).json({ message: 'Invalid file' })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
response.download(filePath, fileName, (err) => {
|
|
45
|
+
if (err) {
|
|
46
|
+
console.error('Download error:', err)
|
|
47
|
+
response.status(500).json({ message: 'Download failed' })
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// 404 handler
|
|
53
|
+
app.use((_request, response) => {
|
|
54
|
+
response.status(404).json({ message: 'Route not found' })
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// Error handler
|
|
58
|
+
app.use((error, _request, response, _next) => {
|
|
59
|
+
console.error('Server error:', error)
|
|
60
|
+
response.status(500).json({
|
|
61
|
+
message: 'Internal server error',
|
|
62
|
+
error: config.nodeEnv === 'development' ? error.message : undefined,
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
return app
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default createApp
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import mongoose from 'mongoose'
|
|
2
|
+
|
|
3
|
+
export async function connectDatabase() {
|
|
4
|
+
if (!process.env.MONGODB_URI) {
|
|
5
|
+
throw new Error('MONGODB_URI is required')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
await mongoose.connect(process.env.MONGODB_URI, {
|
|
10
|
+
maxPoolSize: 10,
|
|
11
|
+
serverSelectionTimeoutMS: 5000,
|
|
12
|
+
})
|
|
13
|
+
console.log('ā
MongoDB connected successfully')
|
|
14
|
+
return mongoose.connection
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error('ā MongoDB connection failed:', error.message)
|
|
17
|
+
throw error
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function disconnectDatabase() {
|
|
22
|
+
try {
|
|
23
|
+
await mongoose.disconnect()
|
|
24
|
+
console.log('ā
MongoDB disconnected')
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('ā Disconnect error:', error.message)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getDatabaseStatus() {
|
|
31
|
+
const states = {
|
|
32
|
+
0: 'disconnected',
|
|
33
|
+
1: 'connected',
|
|
34
|
+
2: 'connecting',
|
|
35
|
+
3: 'disconnecting',
|
|
36
|
+
}
|
|
37
|
+
return states[mongoose.connection.readyState] || 'unknown'
|
|
38
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import dotenv from 'dotenv'
|
|
2
|
+
|
|
3
|
+
dotenv.config()
|
|
4
|
+
|
|
5
|
+
export const config = {
|
|
6
|
+
port: Number(process.env.PORT || 5000),
|
|
7
|
+
nodeEnv: process.env.NODE_ENV || 'development',
|
|
8
|
+
mongoUri: process.env.MONGODB_URI,
|
|
9
|
+
frontendUrl: process.env.FRONTEND_URL || 'http://localhost:5173',
|
|
10
|
+
|
|
11
|
+
// Google OAuth
|
|
12
|
+
googleOAuth: {
|
|
13
|
+
clientId: process.env.GOOGLE_OAUTH_CLIENT_ID,
|
|
14
|
+
clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET,
|
|
15
|
+
redirectUri: process.env.GOOGLE_OAUTH_REDIRECT_URI,
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
// Google Drive
|
|
19
|
+
googleDrive: {
|
|
20
|
+
folderId: process.env.GOOGLE_DRIVE_FOLDER_ID,
|
|
21
|
+
tokensPath: process.env.GOOGLE_TOKENS_PATH || './google-tokens.json',
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
// Backup Configuration
|
|
25
|
+
backup: {
|
|
26
|
+
enabled: process.env.ENABLE_AUTO_BACKUP === 'true',
|
|
27
|
+
intervalMinutes: parseInt(process.env.BACKUP_INTERVAL_MINUTES || '60'),
|
|
28
|
+
excludedDatabases: ['admin', 'config', 'local'],
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Validate required configuration
|
|
33
|
+
function validateConfig() {
|
|
34
|
+
const required = ['mongoUri', 'googleOAuth.clientId', 'googleOAuth.clientSecret']
|
|
35
|
+
const missing = []
|
|
36
|
+
|
|
37
|
+
for (const key of required) {
|
|
38
|
+
const keys = key.split('.')
|
|
39
|
+
let value = config
|
|
40
|
+
for (const k of keys) {
|
|
41
|
+
value = value?.[k]
|
|
42
|
+
}
|
|
43
|
+
if (!value) {
|
|
44
|
+
missing.push(key)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (missing.length > 0) {
|
|
49
|
+
console.warn(`ā ļø Missing configuration: ${missing.join(', ')}`)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
validateConfig()
|
|
54
|
+
|
|
55
|
+
export default config
|
package/src/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GD Uploader - Google Drive Backup & Export Module
|
|
3
|
+
* Complete database backup, Excel generation, and Google Drive upload
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { createAndUploadCompleteBackup } from './services/backupService.js'
|
|
7
|
+
export { uploadExcelToGoogleDrive, listDriveFiles, deleteOldBackups } from './services/googleDrive.js'
|
|
8
|
+
export { createExcelWorkbook } from './utils/excelFormatter.js'
|
|
9
|
+
export { extractAllDatabasesCollections, extractSpecificDatabases } from './utils/dataExtract.js'
|
|
10
|
+
export { getAuthStatus } from './services/googleAuth.js'
|
|
11
|
+
|
|
12
|
+
// Version info
|
|
13
|
+
export const version = '1.0.0'
|
|
14
|
+
export const name = 'gd-uploader'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
|
|
3
|
+
const router = express.Router()
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Health check endpoint
|
|
7
|
+
*/
|
|
8
|
+
router.get('/status', (_request, response) => {
|
|
9
|
+
response.json({
|
|
10
|
+
message: 'Service Account authentication enabled',
|
|
11
|
+
authenticated: true
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export default router
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import mongoose from 'mongoose'
|
|
3
|
+
import { createAndUploadCompleteBackup } from '../services/backupService.js'
|
|
4
|
+
import { getBackupStatus } from '../services/backupScheduler.js'
|
|
5
|
+
|
|
6
|
+
const router = express.Router()
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Complete backup - Extract ALL databases and collections
|
|
10
|
+
* Direct upload to Google Drive (Service Account)
|
|
11
|
+
*/
|
|
12
|
+
router.post('/exports', async (request, response) => {
|
|
13
|
+
try {
|
|
14
|
+
if (mongoose.connection.readyState !== 1) {
|
|
15
|
+
return response.status(503).json({ message: 'Database is not connected' })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const result = await createAndUploadCompleteBackup({
|
|
19
|
+
uploadToDrive: true,
|
|
20
|
+
workbookName: request.body?.workbookName,
|
|
21
|
+
folderId: request.body?.folderId,
|
|
22
|
+
excludedDatabases: request.body?.excludedDatabases || ['admin', 'config', 'local'],
|
|
23
|
+
})
|
|
24
|
+
return response.status(201).json({ message: 'Backup created and uploaded successfully', ...result })
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Backup failed:', error)
|
|
27
|
+
return response.status(500).json({ message: error.message || 'Backup failed' })
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get backup status
|
|
33
|
+
*/
|
|
34
|
+
router.get('/backup/status', (_request, response) => {
|
|
35
|
+
response.json(getBackupStatus())
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export default router
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import '../config/environment.js'
|
|
2
|
+
import { connectDatabase, disconnectDatabase } from '../config/database.js'
|
|
3
|
+
import { createAndUploadCompleteBackup } from '../services/backupService.js'
|
|
4
|
+
|
|
5
|
+
async function runBackup() {
|
|
6
|
+
try {
|
|
7
|
+
console.log('š Starting manual backup...')
|
|
8
|
+
console.log(`ā° ${new Date().toLocaleString()}`)
|
|
9
|
+
console.log('ā'.repeat(50))
|
|
10
|
+
|
|
11
|
+
await connectDatabase()
|
|
12
|
+
|
|
13
|
+
const result = await createAndUploadCompleteBackup({
|
|
14
|
+
uploadToDrive: true,
|
|
15
|
+
excludedDatabases: ['admin', 'config', 'local'],
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
console.log('ā'.repeat(50))
|
|
19
|
+
console.log('\nš BACKUP SUMMARY:')
|
|
20
|
+
console.log(` Databases: ${result.databases.join(', ')}`)
|
|
21
|
+
console.log(` Collections: ${result.collections.length}`)
|
|
22
|
+
console.log(` Total Records: ${result.totalRecords}`)
|
|
23
|
+
console.log(` File Name: ${result.fileName}`)
|
|
24
|
+
console.log(` Upload Status: ${result.uploadStatus}`)
|
|
25
|
+
|
|
26
|
+
if (result.uploaded && result.driveFile) {
|
|
27
|
+
console.log(`\nā
Successfully uploaded to Google Drive!`)
|
|
28
|
+
console.log(` Link: ${result.driveFile.webViewLink}`)
|
|
29
|
+
} else if (result.driveFile?.localPath) {
|
|
30
|
+
console.log(`\nā ļø File saved locally (Drive upload failed)`)
|
|
31
|
+
console.log(` Path: ${result.driveFile.localPath}`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log('\nā
Backup completed successfully!')
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('\nā Backup failed:')
|
|
37
|
+
console.error(` Error: ${error.message}`)
|
|
38
|
+
process.exit(1)
|
|
39
|
+
} finally {
|
|
40
|
+
await disconnectDatabase()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
runBackup()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { connectDatabase } from '../config/database.js'
|
|
2
|
+
import config from '../config/environment.js'
|
|
3
|
+
import { startBackupScheduler } from '../services/backupScheduler.js'
|
|
4
|
+
|
|
5
|
+
async function startScheduler() {
|
|
6
|
+
try {
|
|
7
|
+
await connectDatabase()
|
|
8
|
+
startBackupScheduler(config.backup.intervalMinutes)
|
|
9
|
+
} catch (error) {
|
|
10
|
+
console.error('ā Scheduler startup failed:', error.message)
|
|
11
|
+
process.exit(1)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
startScheduler()
|