spaps 0.4.3 → 0.5.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/README.md +65 -2
- package/package.json +9 -7
- package/src/index.js +42 -0
- package/src/middleware/admin.js +238 -0
package/README.md
CHANGED
|
@@ -199,11 +199,74 @@ npm install --save-dev spaps
|
|
|
199
199
|
|
|
200
200
|
- 📖 **Full Documentation**: [sweetpotato.dev](https://sweetpotato.dev)
|
|
201
201
|
- 🔧 **Production Setup**: See deployment guides
|
|
202
|
-
- 💬 **Get Help**: [GitHub Issues](https://github.com/
|
|
202
|
+
- 💬 **Get Help**: [GitHub Issues](https://github.com/build000r/sweet-potato/issues)
|
|
203
203
|
- 🚀 **Examples**: Check `/examples` directory
|
|
204
204
|
|
|
205
|
+
## 🚀 Production Deployment
|
|
206
|
+
|
|
207
|
+
Ready to go live? SPAPS supports seamless migration from local to production:
|
|
208
|
+
|
|
209
|
+
### Local → Production Workflow
|
|
210
|
+
|
|
211
|
+
1. **Export Local Data**:
|
|
212
|
+
```bash
|
|
213
|
+
# Export your products, orders, and customers
|
|
214
|
+
curl http://localhost:3456/api/admin/export > spaps-data.json
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
2. **Set Up Production Server**:
|
|
218
|
+
```bash
|
|
219
|
+
# Deploy to your server (DigitalOcean, AWS, etc.)
|
|
220
|
+
# Example production server: http://104.131.188.214:3000
|
|
221
|
+
git clone https://github.com/build000r/sweet-potato
|
|
222
|
+
cd sweet-potato
|
|
223
|
+
npm install
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
3. **Configure Environment**:
|
|
227
|
+
```bash
|
|
228
|
+
# Set production environment variables
|
|
229
|
+
SUPABASE_URL=https://your-project.supabase.co
|
|
230
|
+
SUPABASE_SERVICE_KEY=eyJhb...your-service-key
|
|
231
|
+
STRIPE_SECRET_KEY=sk_live_... # Your live Stripe key
|
|
232
|
+
JWT_SECRET=your-32-char-secure-secret
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
4. **Sync Products to Production Stripe**:
|
|
236
|
+
```bash
|
|
237
|
+
# Import your local products to production Stripe
|
|
238
|
+
curl -X POST http://104.131.188.214:3000/api/v1/admin/products/sync \
|
|
239
|
+
-H "Content-Type: application/json" \
|
|
240
|
+
-d @spaps-data.json
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
5. **Update Frontend Config**:
|
|
244
|
+
```javascript
|
|
245
|
+
// Change from local to production endpoint
|
|
246
|
+
const SPAPS_URL = 'http://104.131.188.214:3000'; // Your production server
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Production Features
|
|
250
|
+
|
|
251
|
+
The production SPAPS server includes:
|
|
252
|
+
- ✅ **Real Supabase integration** with RLS policies
|
|
253
|
+
- ✅ **Live Stripe webhooks** with signature verification
|
|
254
|
+
- ✅ **Multi-wallet authentication** (Solana, Ethereum, Base, Bitcoin)
|
|
255
|
+
- ✅ **JWT authentication** with refresh tokens
|
|
256
|
+
- ✅ **Rate limiting** and security middleware
|
|
257
|
+
- ✅ **Usage tracking** and analytics
|
|
258
|
+
- ✅ **Multi-tenant support** for multiple client apps
|
|
259
|
+
|
|
260
|
+
### Health Check
|
|
261
|
+
|
|
262
|
+
Check if your production server is running:
|
|
263
|
+
```bash
|
|
264
|
+
curl http://104.131.188.214:3000/health
|
|
265
|
+
# Returns: {"status":"healthy","mode":"production"}
|
|
266
|
+
```
|
|
267
|
+
|
|
205
268
|
---
|
|
206
269
|
|
|
207
|
-
**Current Version**: v0.3
|
|
270
|
+
**Current Version**: v0.4.3
|
|
208
271
|
**License**: MIT
|
|
209
272
|
**Node.js**: >=16.0.0 required
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spaps",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Sweet Potato Authentication & Payment Service CLI - Zero-config local development and
|
|
5
|
-
"main": "
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Sweet Potato Authentication & Payment Service CLI - Zero-config local development with built-in admin middleware and permission utilities",
|
|
5
|
+
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"spaps": "./bin/spaps.js"
|
|
8
8
|
},
|
|
9
9
|
"exports": {
|
|
10
|
-
".": "./
|
|
11
|
-
"./
|
|
10
|
+
".": "./src/index.js",
|
|
11
|
+
"./cli": "./bin/spaps.js",
|
|
12
|
+
"./client": "./client.js",
|
|
13
|
+
"./middleware": "./src/middleware/admin.js"
|
|
12
14
|
},
|
|
13
15
|
"scripts": {
|
|
14
16
|
"test": "echo \"No tests yet\""
|
|
@@ -30,10 +32,10 @@
|
|
|
30
32
|
"license": "MIT",
|
|
31
33
|
"repository": {
|
|
32
34
|
"type": "git",
|
|
33
|
-
"url": "https://github.com/
|
|
35
|
+
"url": "https://github.com/build000r/sweet-potato"
|
|
34
36
|
},
|
|
35
37
|
"bugs": {
|
|
36
|
-
"url": "https://github.com/
|
|
38
|
+
"url": "https://github.com/build000r/sweet-potato/issues"
|
|
37
39
|
},
|
|
38
40
|
"homepage": "https://sweetpotato.dev",
|
|
39
41
|
"dependencies": {
|
package/src/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPAPS Package Main Export
|
|
3
|
+
* Provides middleware, utilities, and client functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const adminMiddleware = require('./middleware/admin');
|
|
7
|
+
|
|
8
|
+
// Export admin middleware and utilities
|
|
9
|
+
module.exports = {
|
|
10
|
+
// Admin middleware functions
|
|
11
|
+
requireAdmin: adminMiddleware.requireAdmin,
|
|
12
|
+
requirePermission: adminMiddleware.requirePermission,
|
|
13
|
+
|
|
14
|
+
// Permission checking utilities
|
|
15
|
+
isAdminAccount: adminMiddleware.isAdminAccount,
|
|
16
|
+
getUserRole: adminMiddleware.getUserRole,
|
|
17
|
+
hasPermission: adminMiddleware.hasPermission,
|
|
18
|
+
getRoleAwareErrorMessage: adminMiddleware.getRoleAwareErrorMessage,
|
|
19
|
+
|
|
20
|
+
// Constants
|
|
21
|
+
DEFAULT_ADMIN_ACCOUNTS: adminMiddleware.DEFAULT_ADMIN_ACCOUNTS,
|
|
22
|
+
|
|
23
|
+
// Factory function for custom admin configurations
|
|
24
|
+
createPermissionChecker: adminMiddleware.createPermissionChecker,
|
|
25
|
+
|
|
26
|
+
// Express middleware aliases for convenience
|
|
27
|
+
admin: adminMiddleware.requireAdmin,
|
|
28
|
+
permission: adminMiddleware.requirePermission,
|
|
29
|
+
|
|
30
|
+
// Version and metadata
|
|
31
|
+
version: require('../package.json').version,
|
|
32
|
+
name: 'spaps'
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Named exports for ES6 compatibility
|
|
36
|
+
module.exports.requireAdmin = adminMiddleware.requireAdmin;
|
|
37
|
+
module.exports.requirePermission = adminMiddleware.requirePermission;
|
|
38
|
+
module.exports.isAdminAccount = adminMiddleware.isAdminAccount;
|
|
39
|
+
module.exports.getUserRole = adminMiddleware.getUserRole;
|
|
40
|
+
module.exports.hasPermission = adminMiddleware.hasPermission;
|
|
41
|
+
module.exports.getRoleAwareErrorMessage = adminMiddleware.getRoleAwareErrorMessage;
|
|
42
|
+
module.exports.createPermissionChecker = adminMiddleware.createPermissionChecker;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Admin middleware for SPAPS applications
|
|
5
|
+
* Provides built-in admin role checking and permission validation
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Default admin configuration
|
|
9
|
+
const DEFAULT_ADMIN_ACCOUNTS = {
|
|
10
|
+
email: 'buildooor@gmail.com',
|
|
11
|
+
wallets: {
|
|
12
|
+
ethereum: '0xa72bb7CeF1e4B2Cc144373d8dE0Add7CCc8DF4Ba',
|
|
13
|
+
solana: 'HVEbdiYU3Rr34NHBSgKs7q8cvdTeZLqNL77Z1FB2vjLy',
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if an identifier (email/wallet) is an admin account
|
|
19
|
+
*/
|
|
20
|
+
function isAdminAccount(identifier, customAdmins = []) {
|
|
21
|
+
if (!identifier) return false;
|
|
22
|
+
|
|
23
|
+
const normalized = identifier.toLowerCase();
|
|
24
|
+
|
|
25
|
+
// Check default admin accounts
|
|
26
|
+
if (normalized === DEFAULT_ADMIN_ACCOUNTS.email.toLowerCase() ||
|
|
27
|
+
normalized === DEFAULT_ADMIN_ACCOUNTS.wallets.ethereum.toLowerCase() ||
|
|
28
|
+
normalized === DEFAULT_ADMIN_ACCOUNTS.wallets.solana.toLowerCase()) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check custom admin accounts
|
|
33
|
+
return customAdmins.some(admin => {
|
|
34
|
+
if (typeof admin === 'string') {
|
|
35
|
+
return admin.toLowerCase() === normalized;
|
|
36
|
+
}
|
|
37
|
+
if (admin.email && admin.email.toLowerCase() === normalized) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (admin.wallet_address && admin.wallet_address.toLowerCase() === normalized) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get user role based on identifier
|
|
49
|
+
*/
|
|
50
|
+
function getUserRole(identifier, customAdmins = []) {
|
|
51
|
+
if (isAdminAccount(identifier, customAdmins)) {
|
|
52
|
+
return 'admin';
|
|
53
|
+
}
|
|
54
|
+
return 'user';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get role-aware error message
|
|
59
|
+
*/
|
|
60
|
+
function getRoleAwareErrorMessage(requiredRole, userRole, action = 'perform this action') {
|
|
61
|
+
const messages = {
|
|
62
|
+
admin: {
|
|
63
|
+
user: `🔒 Admin privileges required to ${action}. Please authenticate with an admin account.`,
|
|
64
|
+
guest: `🔐 Authentication required. Please sign in with an admin account to ${action}.`
|
|
65
|
+
},
|
|
66
|
+
user: {
|
|
67
|
+
guest: `🔐 Authentication required. Please sign in to ${action}.`
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return messages[requiredRole]?.[userRole] || `Access denied. Required role: ${requiredRole}, current role: ${userRole}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Express middleware for admin authentication
|
|
76
|
+
*/
|
|
77
|
+
function requireAdmin(options = {}) {
|
|
78
|
+
const {
|
|
79
|
+
customAdmins = [],
|
|
80
|
+
errorMessage,
|
|
81
|
+
onUnauthorized,
|
|
82
|
+
allowLocalBypass = true
|
|
83
|
+
} = options;
|
|
84
|
+
|
|
85
|
+
return (req, res, next) => {
|
|
86
|
+
// Check if in local development mode
|
|
87
|
+
if (allowLocalBypass && req.isLocalMode) {
|
|
88
|
+
console.log(chalk.yellow('🏠 Local mode: Admin check bypassed'));
|
|
89
|
+
req.isAdmin = true;
|
|
90
|
+
req.userRole = 'admin';
|
|
91
|
+
return next();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Extract user information from request
|
|
95
|
+
const userEmail = req.user?.email || req.body?.email;
|
|
96
|
+
const walletAddress = req.user?.wallet_address || req.body?.wallet_address;
|
|
97
|
+
const identifier = userEmail || walletAddress;
|
|
98
|
+
|
|
99
|
+
if (!identifier) {
|
|
100
|
+
const message = errorMessage || getRoleAwareErrorMessage('admin', 'guest');
|
|
101
|
+
|
|
102
|
+
if (onUnauthorized) {
|
|
103
|
+
return onUnauthorized(req, res, { reason: 'no_identifier', message });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return res.status(401).json({
|
|
107
|
+
success: false,
|
|
108
|
+
error: {
|
|
109
|
+
code: 'AUTHENTICATION_REQUIRED',
|
|
110
|
+
message
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const isAdmin = isAdminAccount(identifier, customAdmins);
|
|
116
|
+
const userRole = getUserRole(identifier, customAdmins);
|
|
117
|
+
|
|
118
|
+
if (!isAdmin) {
|
|
119
|
+
const message = errorMessage || getRoleAwareErrorMessage('admin', userRole);
|
|
120
|
+
|
|
121
|
+
if (onUnauthorized) {
|
|
122
|
+
return onUnauthorized(req, res, {
|
|
123
|
+
reason: 'insufficient_privileges',
|
|
124
|
+
message,
|
|
125
|
+
userRole,
|
|
126
|
+
identifier
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return res.status(403).json({
|
|
131
|
+
success: false,
|
|
132
|
+
error: {
|
|
133
|
+
code: 'INSUFFICIENT_PRIVILEGES',
|
|
134
|
+
message
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Add admin information to request
|
|
140
|
+
req.isAdmin = true;
|
|
141
|
+
req.userRole = userRole;
|
|
142
|
+
req.adminAccount = identifier;
|
|
143
|
+
|
|
144
|
+
console.log(chalk.green(`👑 Admin authenticated: ${identifier}`));
|
|
145
|
+
next();
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Permission checking utility
|
|
151
|
+
*/
|
|
152
|
+
function hasPermission(user, requiredPermissions, customAdmins = []) {
|
|
153
|
+
if (!user) return false;
|
|
154
|
+
|
|
155
|
+
const identifier = user.email || user.wallet_address;
|
|
156
|
+
const userRole = getUserRole(identifier, customAdmins);
|
|
157
|
+
|
|
158
|
+
// Admins have all permissions
|
|
159
|
+
if (userRole === 'admin') {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check specific permissions
|
|
164
|
+
if (Array.isArray(requiredPermissions)) {
|
|
165
|
+
return requiredPermissions.every(permission =>
|
|
166
|
+
user.permissions?.includes(permission)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return user.permissions?.includes(requiredPermissions);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Express middleware for permission checking
|
|
175
|
+
*/
|
|
176
|
+
function requirePermission(permissions, options = {}) {
|
|
177
|
+
const {
|
|
178
|
+
customAdmins = [],
|
|
179
|
+
errorMessage,
|
|
180
|
+
onUnauthorized
|
|
181
|
+
} = options;
|
|
182
|
+
|
|
183
|
+
return (req, res, next) => {
|
|
184
|
+
const user = req.user;
|
|
185
|
+
const hasRequiredPermission = hasPermission(user, permissions, customAdmins);
|
|
186
|
+
|
|
187
|
+
if (!hasRequiredPermission) {
|
|
188
|
+
const userRole = user ? getUserRole(user.email || user.wallet_address, customAdmins) : 'guest';
|
|
189
|
+
const message = errorMessage || getRoleAwareErrorMessage('permission', userRole, `access this resource`);
|
|
190
|
+
|
|
191
|
+
if (onUnauthorized) {
|
|
192
|
+
return onUnauthorized(req, res, {
|
|
193
|
+
reason: 'insufficient_permissions',
|
|
194
|
+
message,
|
|
195
|
+
requiredPermissions: permissions,
|
|
196
|
+
userPermissions: user?.permissions || []
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return res.status(403).json({
|
|
201
|
+
success: false,
|
|
202
|
+
error: {
|
|
203
|
+
code: 'INSUFFICIENT_PERMISSIONS',
|
|
204
|
+
message,
|
|
205
|
+
details: {
|
|
206
|
+
required: permissions,
|
|
207
|
+
current: user?.permissions || []
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
next();
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = {
|
|
218
|
+
// Middleware functions
|
|
219
|
+
requireAdmin,
|
|
220
|
+
requirePermission,
|
|
221
|
+
|
|
222
|
+
// Utility functions
|
|
223
|
+
isAdminAccount,
|
|
224
|
+
getUserRole,
|
|
225
|
+
hasPermission,
|
|
226
|
+
getRoleAwareErrorMessage,
|
|
227
|
+
|
|
228
|
+
// Constants
|
|
229
|
+
DEFAULT_ADMIN_ACCOUNTS,
|
|
230
|
+
|
|
231
|
+
// Helper for client-side checking
|
|
232
|
+
createPermissionChecker: (customAdmins = []) => ({
|
|
233
|
+
isAdmin: (identifier) => isAdminAccount(identifier, customAdmins),
|
|
234
|
+
getRole: (identifier) => getUserRole(identifier, customAdmins),
|
|
235
|
+
hasPermission: (user, permissions) => hasPermission(user, permissions, customAdmins),
|
|
236
|
+
getErrorMessage: (requiredRole, userRole, action) => getRoleAwareErrorMessage(requiredRole, userRole, action)
|
|
237
|
+
})
|
|
238
|
+
};
|