@ecopex/ecopex-framework 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/.env +73 -0
- package/README.md +248 -0
- package/bun.lockb +0 -0
- package/config/swagger/admin.js +44 -0
- package/config/swagger/api.js +19 -0
- package/database/migrations/20240000135243_timezones.js +22 -0
- package/database/migrations/20240000135244_countries.js +23 -0
- package/database/migrations/20240000135244_create_admins_table.js +66 -0
- package/database/migrations/20240000135244_currencies.js +21 -0
- package/database/migrations/20240000135244_languages.js +21 -0
- package/database/migrations/20240000135244_taxes.js +10 -0
- package/database/migrations/20240000135245_sites.js +37 -0
- package/database/migrations/20240000135246_payment_methods.js +33 -0
- package/database/migrations/20251016113547_devices.js +37 -0
- package/database/migrations/20251019192600_users.js +62 -0
- package/database/migrations/20251019213551_language_lines.js +35 -0
- package/database/migrations/20251222214131_category_groups.js +18 -0
- package/database/migrations/20251222214619_categories.js +27 -0
- package/database/migrations/20251222214848_brands.js +23 -0
- package/database/migrations/20251222214946_products.js +30 -0
- package/database/migrations/20251222215428_product_images.js +18 -0
- package/database/migrations/20251222215553_options.js +30 -0
- package/database/migrations/20251222215806_variants.js +23 -0
- package/database/migrations/20251222215940_attributes.js +25 -0
- package/database/migrations/20251222220135_discounts.js +15 -0
- package/database/migrations/20251222220253_reviews.js +22 -0
- package/database/migrations/20251222220341_favorites.js +10 -0
- package/database/migrations/20251222220422_search_logs.js +17 -0
- package/database/migrations/20251222220636_orders.js +16 -0
- package/database/migrations/20251222220806_order_items.js +19 -0
- package/database/migrations/20251222221317_order_statuses.js +10 -0
- package/database/migrations/20251222221446_order_payments.js +13 -0
- package/database/migrations/20251222221654_order_addresses.js +23 -0
- package/database/migrations/20251222221807_order_status_logs.js +13 -0
- package/database/seeds/admins.js +37 -0
- package/database/seeds/countries.js +203 -0
- package/database/seeds/currencies.js +165 -0
- package/database/seeds/languages.js +113 -0
- package/database/seeds/timezones.js +149 -0
- package/ecosystem.config.js +26 -0
- package/env.example +73 -0
- package/knexfile.js +3 -0
- package/libraries/2fa.js +22 -0
- package/libraries/aws.js +63 -0
- package/libraries/bcrypt.js +284 -0
- package/libraries/controls.js +113 -0
- package/libraries/date.js +14 -0
- package/libraries/general.js +8 -0
- package/libraries/image.js +57 -0
- package/libraries/jwt.js +178 -0
- package/libraries/knex.js +7 -0
- package/libraries/slug.js +14 -0
- package/libraries/stores.js +22 -0
- package/libraries/upload.js +194 -0
- package/locales/en/messages.json +4 -0
- package/locales/en/sql.json +3 -0
- package/locales/en/validation.json +52 -0
- package/locales/es/validation.json +52 -0
- package/locales/tr/validation.json +59 -0
- package/package.json +75 -0
- package/routes/admin/auto/admins.json +63 -0
- package/routes/admin/auto/devices.json +37 -0
- package/routes/admin/auto/migrations.json +21 -0
- package/routes/admin/auto/users.json +61 -0
- package/routes/admin/middlewares/index.js +87 -0
- package/routes/admin/spec/auth.js +626 -0
- package/routes/admin/spec/users.js +3 -0
- package/routes/auto/handler.js +635 -0
- package/routes/common/auto/countries.json +28 -0
- package/routes/common/auto/currencies.json +26 -0
- package/routes/common/auto/languages.json +26 -0
- package/routes/common/auto/taxes.json +46 -0
- package/routes/common/auto/timezones.json +29 -0
- package/stores/base.js +73 -0
- package/stores/index.js +195 -0
- package/utils/i18n.js +187 -0
- package/utils/jsonRouteLoader.js +587 -0
- package/utils/middleware.js +154 -0
- package/utils/routeLoader.js +227 -0
- package/workers/admin.js +124 -0
- package/workers/api.js +106 -0
package/.env
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# ===========================================
|
|
2
|
+
# SB System Environment Configuration
|
|
3
|
+
# ===========================================
|
|
4
|
+
|
|
5
|
+
# Application Environment
|
|
6
|
+
NODE_ENV=development
|
|
7
|
+
LOG_LEVEL=error
|
|
8
|
+
|
|
9
|
+
# Server Configuration
|
|
10
|
+
HOST=0.0.0.0
|
|
11
|
+
API_PORT=3000
|
|
12
|
+
ADMIN_PORT=3001
|
|
13
|
+
|
|
14
|
+
# Database Configuration
|
|
15
|
+
DB_HOST=127.0.0.1
|
|
16
|
+
DB_PORT=3306
|
|
17
|
+
DB_USER=root
|
|
18
|
+
DB_PASSWORD=Gs1905ua
|
|
19
|
+
DB_NAME=ecommerce
|
|
20
|
+
|
|
21
|
+
# Alternative: Use DATABASE_URL for production
|
|
22
|
+
# DATABASE_URL=mysql://username:password@host:port/database
|
|
23
|
+
|
|
24
|
+
# CORS Configuration
|
|
25
|
+
CORS_ORIGIN=*
|
|
26
|
+
|
|
27
|
+
# PM2 Process Management
|
|
28
|
+
API_INSTANCES=1
|
|
29
|
+
API_EXEC_MODE=fork
|
|
30
|
+
ADMIN_INSTANCES=1
|
|
31
|
+
ADMIN_EXEC_MODE=fork
|
|
32
|
+
|
|
33
|
+
# Internationalization
|
|
34
|
+
DEFAULT_LOCALE=en
|
|
35
|
+
SUPPORTED_LOCALES=en,tr,es
|
|
36
|
+
|
|
37
|
+
# Security (for production)
|
|
38
|
+
# JWT_SECRET=your_jwt_secret_here
|
|
39
|
+
# BCRYPT_ROUNDS=12
|
|
40
|
+
# SESSION_SECRET=your_session_secret_here
|
|
41
|
+
|
|
42
|
+
# Redis (for caching/sessions)
|
|
43
|
+
# REDIS_HOST=localhost
|
|
44
|
+
# REDIS_PORT=6379
|
|
45
|
+
# REDIS_PASSWORD=
|
|
46
|
+
|
|
47
|
+
# Email Configuration (for notifications)
|
|
48
|
+
# SMTP_HOST=smtp.gmail.com
|
|
49
|
+
# SMTP_PORT=587
|
|
50
|
+
# SMTP_USER=your_email@gmail.com
|
|
51
|
+
# SMTP_PASS=your_app_password
|
|
52
|
+
# FROM_EMAIL=noreply@yoursystem.com
|
|
53
|
+
|
|
54
|
+
# File Upload Configuration
|
|
55
|
+
# UPLOAD_MAX_SIZE=10485760
|
|
56
|
+
# UPLOAD_ALLOWED_TYPES=image/jpeg,image/png,image/gif,application/pdf
|
|
57
|
+
|
|
58
|
+
# Rate Limiting
|
|
59
|
+
# RATE_LIMIT_WINDOW_MS=900000
|
|
60
|
+
# RATE_LIMIT_MAX_REQUESTS=100
|
|
61
|
+
|
|
62
|
+
# Logging
|
|
63
|
+
# LOG_FILE_PATH=./logs/app.log
|
|
64
|
+
# LOG_MAX_SIZE=10m
|
|
65
|
+
# LOG_MAX_FILES=5
|
|
66
|
+
|
|
67
|
+
# Monitoring
|
|
68
|
+
# HEALTH_CHECK_INTERVAL=30000
|
|
69
|
+
# METRICS_ENABLED=true
|
|
70
|
+
|
|
71
|
+
# Development Tools
|
|
72
|
+
# DEBUG=sb_system:*
|
|
73
|
+
# NODE_OPTIONS=--max-old-space-size=4096
|
package/README.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# SB System
|
|
2
|
+
|
|
3
|
+
A Node.js system built with Fastify, Knex, and Ajv for automatic routing and database management with PM2 process management.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Fastify**: High-performance web framework
|
|
8
|
+
- **Knex**: SQL query builder with migrations and seeds
|
|
9
|
+
- **Ajv**: JSON schema validation
|
|
10
|
+
- **MySQL**: Database support
|
|
11
|
+
- **PM2**: Process management for production
|
|
12
|
+
- **Microservices**: Separate admin and API services
|
|
13
|
+
- **Automatic Routing**: Dynamic route loading from directory structure
|
|
14
|
+
|
|
15
|
+
## Project Structure
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
sb_system/
|
|
19
|
+
├── workers/
|
|
20
|
+
│ ├── admin.js # Admin service worker
|
|
21
|
+
│ └── api.js # API service worker
|
|
22
|
+
├── routes/
|
|
23
|
+
│ ├── admin/
|
|
24
|
+
│ │ └── users/
|
|
25
|
+
│ │ ├── route.js # Route definitions
|
|
26
|
+
│ │ ├── handler.js # Route handlers
|
|
27
|
+
│ │ └── validation.js # Validation schemas
|
|
28
|
+
│ └── api/
|
|
29
|
+
├── database/
|
|
30
|
+
│ ├── migrations/
|
|
31
|
+
│ └── seeds/
|
|
32
|
+
├── config/
|
|
33
|
+
│ └── database.js
|
|
34
|
+
├── utils/
|
|
35
|
+
│ ├── routeLoader.js
|
|
36
|
+
│ └── validator.js
|
|
37
|
+
├── logs/ # PM2 log files
|
|
38
|
+
├── ecosystem.config.js # PM2 configuration
|
|
39
|
+
└── package.json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
1. Install dependencies:
|
|
45
|
+
```bash
|
|
46
|
+
npm install
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
2. Set up environment variables:
|
|
50
|
+
```bash
|
|
51
|
+
# Copy the example and configure your settings
|
|
52
|
+
cp env.example .env
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
3. Configure your environment in `.env`:
|
|
56
|
+
```bash
|
|
57
|
+
# Database Configuration
|
|
58
|
+
DB_HOST=localhost
|
|
59
|
+
DB_PORT=3306
|
|
60
|
+
DB_USER=your_username
|
|
61
|
+
DB_PASSWORD=your_password
|
|
62
|
+
DB_NAME=sb_system
|
|
63
|
+
|
|
64
|
+
# Server Configuration
|
|
65
|
+
API_PORT=3000
|
|
66
|
+
ADMIN_PORT=3001
|
|
67
|
+
HOST=0.0.0.0
|
|
68
|
+
|
|
69
|
+
# Optional: Internationalization
|
|
70
|
+
DEFAULT_LOCALE=en
|
|
71
|
+
SUPPORTED_LOCALES=en,tr,es
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
4. Run database migrations:
|
|
75
|
+
```bash
|
|
76
|
+
npm run migrate
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
5. Seed the database (optional):
|
|
80
|
+
```bash
|
|
81
|
+
npm run seed
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
> **Note**: See [ENVIRONMENT.md](./ENVIRONMENT.md) for complete environment variable documentation.
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
### Development
|
|
89
|
+
```bash
|
|
90
|
+
# Start both services with PM2
|
|
91
|
+
npm run dev
|
|
92
|
+
|
|
93
|
+
# Or start individual services for development
|
|
94
|
+
npm run dev:api # API service only
|
|
95
|
+
npm run dev:admin # Admin service only
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Production
|
|
99
|
+
```bash
|
|
100
|
+
# Start all services
|
|
101
|
+
npm start
|
|
102
|
+
|
|
103
|
+
# Or start with production environment
|
|
104
|
+
npm run prod
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### PM2 Management
|
|
108
|
+
```bash
|
|
109
|
+
# View status
|
|
110
|
+
npm run status
|
|
111
|
+
|
|
112
|
+
# View logs
|
|
113
|
+
npm run logs
|
|
114
|
+
|
|
115
|
+
# Restart services
|
|
116
|
+
npm run restart
|
|
117
|
+
|
|
118
|
+
# Stop services
|
|
119
|
+
npm run stop
|
|
120
|
+
|
|
121
|
+
# Reload services (zero-downtime)
|
|
122
|
+
npm run reload
|
|
123
|
+
|
|
124
|
+
# Delete all services
|
|
125
|
+
npm run delete
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Services
|
|
129
|
+
|
|
130
|
+
### API Service (Port 3000)
|
|
131
|
+
- **Health Check**: `GET http://localhost:3000/health`
|
|
132
|
+
- **Documentation**: `GET http://localhost:3000/docs`
|
|
133
|
+
- **Welcome**: `GET http://localhost:3000/`
|
|
134
|
+
- **Language Management**:
|
|
135
|
+
- `GET /api/language` - Get supported languages
|
|
136
|
+
- `POST /api/language/set` - Set preferred language
|
|
137
|
+
- `GET /api/language/detect` - Detect language from headers
|
|
138
|
+
|
|
139
|
+
### Admin Service (Port 3001)
|
|
140
|
+
- **Health Check**: `GET http://localhost:3001/health`
|
|
141
|
+
- **Documentation**: `GET http://localhost:3001/docs`
|
|
142
|
+
- **User Management**:
|
|
143
|
+
- `GET /admin/users` - Get all users (with pagination)
|
|
144
|
+
- `GET /admin/users/:id` - Get user by ID
|
|
145
|
+
- `POST /admin/users` - Create new user
|
|
146
|
+
- `PUT /admin/users/:id` - Update user
|
|
147
|
+
- `DELETE /admin/users/:id` - Delete user
|
|
148
|
+
|
|
149
|
+
## Adding New Routes
|
|
150
|
+
|
|
151
|
+
1. Create a new directory under `routes/admin/` or `routes/api/`
|
|
152
|
+
2. Add three files:
|
|
153
|
+
- `route.js` - Define your routes
|
|
154
|
+
- `handler.js` - Implement route handlers
|
|
155
|
+
- `validation.js` - Define validation schemas
|
|
156
|
+
|
|
157
|
+
### Example Route Structure
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
// routes/admin/products/route.js
|
|
161
|
+
module.exports = [
|
|
162
|
+
{
|
|
163
|
+
method: 'GET',
|
|
164
|
+
url: '',
|
|
165
|
+
handlerName: 'getAllProducts',
|
|
166
|
+
validationName: 'getAllProductsValidation',
|
|
167
|
+
schema: {
|
|
168
|
+
// Fastify schema definition
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
// routes/admin/products/handler.js
|
|
174
|
+
class ProductHandler {
|
|
175
|
+
static async getAllProducts(request, reply) {
|
|
176
|
+
// Handler implementation
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = ProductHandler;
|
|
181
|
+
|
|
182
|
+
// routes/admin/products/validation.js
|
|
183
|
+
module.exports = {
|
|
184
|
+
getAllProductsValidation: {
|
|
185
|
+
// Ajv validation schema
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Database
|
|
191
|
+
|
|
192
|
+
The system uses Knex.js for database operations with MySQL. Migrations and seeds are included for easy setup.
|
|
193
|
+
|
|
194
|
+
### Available Commands
|
|
195
|
+
- `npm run migrate` - Run migrations
|
|
196
|
+
- `npm run migrate:rollback` - Rollback migrations
|
|
197
|
+
- `npm run seed` - Run seeds
|
|
198
|
+
|
|
199
|
+
## Multi-Language Support
|
|
200
|
+
|
|
201
|
+
The system includes comprehensive internationalization (i18n) support with automatic language detection and validation error translation.
|
|
202
|
+
|
|
203
|
+
### Supported Languages
|
|
204
|
+
- **English (en)** - Default
|
|
205
|
+
- **Turkish (tr)** - Türkçe
|
|
206
|
+
- **Spanish (es)** - Español
|
|
207
|
+
|
|
208
|
+
### Language Detection
|
|
209
|
+
The system automatically detects language from:
|
|
210
|
+
1. `Accept-Language` header
|
|
211
|
+
2. `X-Language` header
|
|
212
|
+
3. `lang` query parameter
|
|
213
|
+
4. Falls back to default locale
|
|
214
|
+
|
|
215
|
+
### Language Endpoints
|
|
216
|
+
```bash
|
|
217
|
+
# Get supported languages
|
|
218
|
+
GET /api/language
|
|
219
|
+
|
|
220
|
+
# Set preferred language
|
|
221
|
+
POST /api/language/set
|
|
222
|
+
{
|
|
223
|
+
"language": "tr"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# Detect language from headers
|
|
227
|
+
GET /api/language/detect
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Usage Examples
|
|
231
|
+
```bash
|
|
232
|
+
# Request with Turkish language
|
|
233
|
+
curl -H "Accept-Language: tr" http://localhost:3000/api/language
|
|
234
|
+
|
|
235
|
+
# Request with custom language header
|
|
236
|
+
curl -H "X-Language: es" http://localhost:3000/admin/users
|
|
237
|
+
|
|
238
|
+
# Request with query parameter
|
|
239
|
+
curl "http://localhost:3000/admin/users?lang=tr"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Validation
|
|
243
|
+
|
|
244
|
+
The system uses Ajv for JSON schema validation with multi-language error messages. Validation schemas are defined in the `validation.js` files and automatically applied to routes.
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT
|
package/bun.lockb
ADDED
|
Binary file
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swagger configuration for Admin service
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
swagger: {
|
|
6
|
+
openapi: {
|
|
7
|
+
info: {
|
|
8
|
+
title: 'Ecommerce Admin API',
|
|
9
|
+
description: 'Admin API documentation for Ecommerce',
|
|
10
|
+
version: '1.0.0'
|
|
11
|
+
},
|
|
12
|
+
host: `${process.env.HOST || 'localhost'}:${process.env.ADMIN_PORT || 3001}`,
|
|
13
|
+
schemes: ['http', 'https'],
|
|
14
|
+
consumes: ['application/json'],
|
|
15
|
+
produces: ['application/json'],
|
|
16
|
+
components: {
|
|
17
|
+
securitySchemes: {
|
|
18
|
+
bearer: {
|
|
19
|
+
type: 'http',
|
|
20
|
+
scheme: 'bearer',
|
|
21
|
+
bearerFormat: 'JWT',
|
|
22
|
+
description: 'Enter the token with the `Bearer: ` prefix, e.g. "Bearer abcde12345".'
|
|
23
|
+
},
|
|
24
|
+
apiKey: {
|
|
25
|
+
type: 'apiKey',
|
|
26
|
+
in: 'header',
|
|
27
|
+
name: 'apiKey',
|
|
28
|
+
description: 'Enter token e.g. "abcde1-234533-333312-2222".'
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
headers: {
|
|
32
|
+
description: 'Headers',
|
|
33
|
+
scheme: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
swaggerUi: {
|
|
42
|
+
routePrefix: '/docs'
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swagger configuration for API service
|
|
3
|
+
*/
|
|
4
|
+
module.exports = {
|
|
5
|
+
swagger: {
|
|
6
|
+
info: {
|
|
7
|
+
title: 'SB System API',
|
|
8
|
+
description: 'API documentation for SB System',
|
|
9
|
+
version: '1.0.0'
|
|
10
|
+
},
|
|
11
|
+
host: `${process.env.HOST || 'localhost'}:${process.env.API_PORT || 3000}`,
|
|
12
|
+
schemes: ['http', 'https'],
|
|
13
|
+
consumes: ['application/json'],
|
|
14
|
+
produces: ['application/json']
|
|
15
|
+
},
|
|
16
|
+
swaggerUi: {
|
|
17
|
+
routePrefix: '/docs'
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = function(knex) {
|
|
6
|
+
return knex.schema.createTable('timezones', function(table) {
|
|
7
|
+
// Primary key
|
|
8
|
+
table.increments('timezone_id').primary();
|
|
9
|
+
table.string('name', 100).notNullable();
|
|
10
|
+
table.string('code', 100).notNullable();
|
|
11
|
+
table.string('offset', 100).notNullable();
|
|
12
|
+
table.string('gmt', 100).notNullable();
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param { import("knex").Knex } knex
|
|
18
|
+
* @returns { Promise<void> }
|
|
19
|
+
*/
|
|
20
|
+
exports.down = function(knex) {
|
|
21
|
+
return knex.schema.dropTable('timezones');
|
|
22
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = function(knex) {
|
|
6
|
+
return knex.schema.createTable('countries', function(table) {
|
|
7
|
+
// Primary key
|
|
8
|
+
table.increments('country_id').primary();
|
|
9
|
+
table.string('name', 100).notNullable();
|
|
10
|
+
table.string('code', 100).notNullable();
|
|
11
|
+
table.string('iso_code', 100).notNullable();
|
|
12
|
+
table.string('phone_code', 100).notNullable();
|
|
13
|
+
table.string('flag', 100).notNullable();
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param { import("knex").Knex } knex
|
|
19
|
+
* @returns { Promise<void> }
|
|
20
|
+
*/
|
|
21
|
+
exports.down = function(knex) {
|
|
22
|
+
return knex.schema.dropTable('countries');
|
|
23
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = function(knex) {
|
|
6
|
+
return knex.schema.createTable('admins', function(table) {
|
|
7
|
+
// Primary key
|
|
8
|
+
table.increments('admin_id').primary();
|
|
9
|
+
table.integer('country_id').unsigned().references('country_id').inTable('countries');
|
|
10
|
+
|
|
11
|
+
// Basic admin information
|
|
12
|
+
table.string('firstname', 100).nullable();
|
|
13
|
+
table.string('lastname', 100).nullable();
|
|
14
|
+
table.string('username', 100).notNullable().unique();
|
|
15
|
+
table.string('email', 255).nullable().unique();
|
|
16
|
+
table.string('password', 255).notNullable();
|
|
17
|
+
table.enum('role', ['admin', 'manager']).defaultTo('manager');
|
|
18
|
+
|
|
19
|
+
// Two-factor authentication fields
|
|
20
|
+
table.boolean('two_factor_enabled').defaultTo(false);
|
|
21
|
+
table.string('two_factor_secret', 32).nullable(); // TOTP secret
|
|
22
|
+
table.string('phone_number', 20).nullable();
|
|
23
|
+
table.boolean('phone_verified').defaultTo(false);
|
|
24
|
+
table.boolean('email_verified').defaultTo(false);
|
|
25
|
+
|
|
26
|
+
// Login security
|
|
27
|
+
table.integer('login_attempts').defaultTo(0);
|
|
28
|
+
table.timestamp('locked_until').nullable();
|
|
29
|
+
|
|
30
|
+
// Password security
|
|
31
|
+
table.timestamp('password_changed_at').nullable();
|
|
32
|
+
table.string('password_reset_token', 255).nullable();
|
|
33
|
+
|
|
34
|
+
// Email verification
|
|
35
|
+
table.string('email_verification_token', 255).nullable();
|
|
36
|
+
|
|
37
|
+
// Phone verification
|
|
38
|
+
table.string('phone_verification_code', 10).nullable();
|
|
39
|
+
|
|
40
|
+
// Address information
|
|
41
|
+
table.string('address', 255).nullable();
|
|
42
|
+
table.string('city', 100).nullable();
|
|
43
|
+
table.string('state', 100).nullable();
|
|
44
|
+
table.string('zip', 20).nullable();
|
|
45
|
+
|
|
46
|
+
// Status and timestamps
|
|
47
|
+
table.boolean('is_active').defaultTo(true);
|
|
48
|
+
|
|
49
|
+
// Audit timestamps
|
|
50
|
+
table.timestamps(true, true); // created_at and updated_at
|
|
51
|
+
|
|
52
|
+
// Indexes for performance
|
|
53
|
+
table.index(['role']);
|
|
54
|
+
table.index(['is_active']);
|
|
55
|
+
table.index(['two_factor_enabled']);
|
|
56
|
+
table.index(['created_at']);
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param { import("knex").Knex } knex
|
|
62
|
+
* @returns { Promise<void> }
|
|
63
|
+
*/
|
|
64
|
+
exports.down = function(knex) {
|
|
65
|
+
return knex.schema.dropTable('admins');
|
|
66
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = function(knex) {
|
|
6
|
+
return knex.schema.createTable('currencies', function(table) {
|
|
7
|
+
// Primary key
|
|
8
|
+
table.increments('currency_id').primary();
|
|
9
|
+
table.string('name', 100).notNullable();
|
|
10
|
+
table.string('code', 100).notNullable();
|
|
11
|
+
table.string('symbol', 100).notNullable();
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param { import("knex").Knex } knex
|
|
17
|
+
* @returns { Promise<void> }
|
|
18
|
+
*/
|
|
19
|
+
exports.down = function(knex) {
|
|
20
|
+
return knex.schema.dropTable('currencies');
|
|
21
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = function(knex) {
|
|
6
|
+
return knex.schema.createTable('languages', function(table) {
|
|
7
|
+
// Primary key
|
|
8
|
+
table.increments('language_id').primary();
|
|
9
|
+
table.string('name', 100).notNullable();
|
|
10
|
+
table.string('code', 100).notNullable();
|
|
11
|
+
table.string('flag', 100).notNullable();
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param { import("knex").Knex } knex
|
|
17
|
+
* @returns { Promise<void> }
|
|
18
|
+
*/
|
|
19
|
+
exports.down = function(knex) {
|
|
20
|
+
return knex.schema.dropTable('languages');
|
|
21
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = function(knex) {
|
|
6
|
+
return knex.schema.createTable('sites', function(table) {
|
|
7
|
+
table.increments('site_id').primary();
|
|
8
|
+
table.integer('admin_id').unsigned().references('admin_id').inTable('admins');
|
|
9
|
+
table.integer('currency_id').unsigned().references('currency_id').inTable('currencies');
|
|
10
|
+
table.integer('timezone_id').unsigned().references('timezone_id').inTable('timezones');
|
|
11
|
+
table.integer('language_id').unsigned().references('language_id').inTable('languages');
|
|
12
|
+
table.integer('country_id').unsigned().references('country_id').inTable('countries');
|
|
13
|
+
table.integer('tax_id').unsigned().references('tax_id').inTable('taxes');
|
|
14
|
+
table.string('name', 100).notNullable();
|
|
15
|
+
table.string('domain', 100).notNullable().unique();
|
|
16
|
+
table.string('logo', 100).notNullable();
|
|
17
|
+
table.string('favicon', 100).notNullable();
|
|
18
|
+
table.string('description', 100).notNullable();
|
|
19
|
+
table.string('keywords', 100).notNullable();
|
|
20
|
+
table.boolean('is_active').defaultTo(true);
|
|
21
|
+
table.datetime('expires_at').notNullable();
|
|
22
|
+
table.timestamps(true, true);
|
|
23
|
+
table.index(['admin_id'], 'site_admin_id_index');
|
|
24
|
+
table.index(['name'], 'site_name_index');
|
|
25
|
+
table.index(['domain'], 'site_domain_index');
|
|
26
|
+
table.index(['created_at'], 'site_created_at_index');
|
|
27
|
+
table.index(['updated_at'], 'site_updated_at_index');
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param { import("knex").Knex } knex
|
|
33
|
+
* @returns { Promise<void> }
|
|
34
|
+
*/
|
|
35
|
+
exports.down = function(knex) {
|
|
36
|
+
return knex.schema.dropTable('sites');
|
|
37
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = knex => knex.schema.createTable('payment_methods', function(table) {
|
|
6
|
+
// Primary key
|
|
7
|
+
table.increments('payment_method_id').primary();
|
|
8
|
+
table.integer('site_id').unsigned().references('site_id').inTable('sites');
|
|
9
|
+
table.integer('currency_id').unsigned().references('currency_id').inTable('currencies');
|
|
10
|
+
table.string('logo').defaultTo(null);
|
|
11
|
+
table.string('name').notNullable();
|
|
12
|
+
table.string('code').unique().notNullable();
|
|
13
|
+
table.string('provider').defaultTo(null);
|
|
14
|
+
table.string('api_key').defaultTo(null);
|
|
15
|
+
table.string('api_secret').defaultTo(null);
|
|
16
|
+
table.string('api_url').defaultTo(null);
|
|
17
|
+
table.string('api_version').defaultTo(null);
|
|
18
|
+
table.boolean('is_active').defaultTo(true);
|
|
19
|
+
table.timestamps(true, true);
|
|
20
|
+
}).createTable('payment_method_translations', table => {
|
|
21
|
+
table.increments('payment_method_translation_id').primary();
|
|
22
|
+
table.integer('payment_method_id').unsigned().references('payment_method_id').inTable('payment_methods').onDelete('CASCADE');
|
|
23
|
+
table.integer('language_id').unsigned().references('language_id').inTable('languages');
|
|
24
|
+
table.string('description').notNullable();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param { import("knex").Knex } knex
|
|
29
|
+
* @returns { Promise<void> }
|
|
30
|
+
*/
|
|
31
|
+
exports.down = knex =>
|
|
32
|
+
knex.schema.dropTable('payment_method_translations')
|
|
33
|
+
.dropTable('payment_methods');
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param { import("knex").Knex } knex
|
|
3
|
+
* @returns { Promise<void> }
|
|
4
|
+
*/
|
|
5
|
+
exports.up = function(knex) {
|
|
6
|
+
return knex.schema.createTable('devices', function (table) {
|
|
7
|
+
table.increments('device_id').unsigned().notNullable().primary();
|
|
8
|
+
table.integer('personal_id');
|
|
9
|
+
table.enum('personal_type', ['admin', 'site', 'user']).notNullable();
|
|
10
|
+
table.string('unique_id', 100).notNullable();
|
|
11
|
+
table.string('ip_address', 100).notNullable();
|
|
12
|
+
table.text('device_information').notNullable();
|
|
13
|
+
table.boolean('two_factor_approved').defaultTo(false);
|
|
14
|
+
table.string('token', 255).notNullable();
|
|
15
|
+
table.datetime('last_login_at').notNullable();
|
|
16
|
+
table.string('last_login_ip', 100).notNullable();
|
|
17
|
+
table.datetime('created_at').notNullable().defaultTo(knex.fn.now());
|
|
18
|
+
table.datetime('updated_at').notNullable().defaultTo(knex.raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
|
|
19
|
+
table.unique(['unique_id', 'personal_id', 'personal_type'], 'device_unique_index')
|
|
20
|
+
table.index(['personal_id', 'personal_type'], 'device_personal_index')
|
|
21
|
+
table.index(['token'], 'device_token_index')
|
|
22
|
+
})
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param { import("knex").Knex } knex
|
|
27
|
+
* @returns { Promise<void> }
|
|
28
|
+
*/
|
|
29
|
+
exports.down = async function(knex) {
|
|
30
|
+
if (process.env.NODE_ENV === 'production') {
|
|
31
|
+
throw new Error('Cannot drop table in production environment.')
|
|
32
|
+
} else {
|
|
33
|
+
if(await knex.schema.hasTable('devices')) {
|
|
34
|
+
return knex.schema.dropTable('devices');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|