spaps 0.1.0 → 0.2.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 CHANGED
@@ -12,6 +12,8 @@ npx spaps local
12
12
 
13
13
  That's it! Your local SPAPS server is running at http://localhost:3300 šŸš€
14
14
 
15
+ ✨ **New in v0.2.0**: The `spaps local` command now starts a real server!
16
+
15
17
  ## Installation
16
18
 
17
19
  ```bash
@@ -28,10 +30,17 @@ Start a local SPAPS development server with:
28
30
  - Auto-authentication enabled
29
31
  - Test users (user/admin/premium)
30
32
  - CORS configured for any frontend
33
+ - Mock Stripe endpoints
34
+ - Full auth flow support
31
35
 
32
36
  ```bash
33
37
  spaps local
34
38
  # Server running at http://localhost:3300
39
+ # Documentation at http://localhost:3300/docs
40
+
41
+ # Options:
42
+ spaps local --port 3301 # Use different port
43
+ spaps local --open # Open browser automatically
35
44
  ```
36
45
 
37
46
  ### `spaps init`
package/bin/spaps.js CHANGED
@@ -30,24 +30,51 @@ program
30
30
  .command('local')
31
31
  .description('Start local SPAPS server (no API keys required!)')
32
32
  .option('-p, --port <port>', 'Port to run on', '3300')
33
- .action((options) => {
33
+ .option('-o, --open', 'Open browser automatically', false)
34
+ .action(async (options) => {
34
35
  console.log(logo);
35
- console.log(chalk.green('šŸš€ Starting SPAPS local development server...'));
36
- console.log(chalk.dim(` Port: ${options.port}`));
37
- console.log(chalk.dim(' Mode: Local Development (auto-auth enabled)'));
38
- console.log();
39
36
 
40
- // For now, just show instructions
41
- // TODO: Actually spawn the server process
42
- console.log(chalk.yellow('šŸ“¦ Local mode coming soon!'));
43
- console.log();
44
- console.log('For now, run SPAPS locally with:');
45
- console.log(chalk.cyan(' git clone https://github.com/yourusername/sweet-potato'));
46
- console.log(chalk.cyan(' cd sweet-potato'));
47
- console.log(chalk.cyan(' npm install'));
48
- console.log(chalk.cyan(' npm run dev'));
49
- console.log();
50
- console.log('Full CLI functionality coming in v0.2.0! šŸš€');
37
+ try {
38
+ // Import and start the local server
39
+ const LocalServer = require('../src/local-server.js');
40
+ const server = new LocalServer({ port: options.port });
41
+
42
+ await server.start();
43
+
44
+ // Open browser if requested
45
+ if (options.open) {
46
+ const { exec } = require('child_process');
47
+ const url = `http://localhost:${options.port}/docs`;
48
+ const start = process.platform === 'darwin' ? 'open' :
49
+ process.platform === 'win32' ? 'start' : 'xdg-open';
50
+ exec(`${start} ${url}`);
51
+ }
52
+
53
+ // Keep process running
54
+ process.on('SIGINT', () => {
55
+ console.log(chalk.yellow('\nšŸ‘‹ Shutting down SPAPS local server...'));
56
+ process.exit(0);
57
+ });
58
+
59
+ } catch (error) {
60
+ console.error(chalk.red('āŒ Failed to start server:'), error.message);
61
+
62
+ if (error.code === 'MODULE_NOT_FOUND') {
63
+ console.log(chalk.yellow('\nšŸ’” Installing dependencies...'));
64
+ const { execSync } = require('child_process');
65
+ try {
66
+ execSync('npm install express cors', {
67
+ cwd: path.join(__dirname, '..'),
68
+ stdio: 'inherit'
69
+ });
70
+ console.log(chalk.green('āœ… Dependencies installed! Please run the command again.'));
71
+ } catch (installError) {
72
+ console.error(chalk.red('Failed to install dependencies. Please run: npm install express cors'));
73
+ }
74
+ }
75
+
76
+ process.exit(1);
77
+ }
51
78
  });
52
79
 
53
80
  // Init command - Initialize SPAPS in existing project
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spaps",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Sweet Potato Authentication & Payment Service CLI - Zero-config local development and project scaffolding",
5
5
  "main": "bin/spaps.js",
6
6
  "bin": {
@@ -37,13 +37,16 @@
37
37
  "commander": "^11.1.0",
38
38
  "ora": "^5.4.1",
39
39
  "prompts": "^2.4.2",
40
- "axios": "^1.6.0"
40
+ "axios": "^1.6.0",
41
+ "express": "^4.18.2",
42
+ "cors": "^2.8.5"
41
43
  },
42
44
  "engines": {
43
45
  "node": ">=16.0.0"
44
46
  },
45
47
  "files": [
46
48
  "bin",
49
+ "src",
47
50
  "README.md"
48
51
  ]
49
52
  }
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * SPAPS Local Development Server
5
+ * Minimal, zero-config server for local development
6
+ */
7
+
8
+ const express = require('express');
9
+ const cors = require('cors');
10
+ const chalk = require('chalk');
11
+
12
+ class LocalServer {
13
+ constructor(options = {}) {
14
+ this.port = options.port || process.env.PORT || 3300;
15
+ this.app = express();
16
+ this.setupMiddleware();
17
+ this.setupRoutes();
18
+ }
19
+
20
+ setupMiddleware() {
21
+ // CORS - allow everything in local mode
22
+ this.app.use(cors({
23
+ origin: true,
24
+ credentials: true,
25
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
26
+ allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key', 'X-Test-User'],
27
+ }));
28
+
29
+ // Body parsing
30
+ this.app.use(express.json());
31
+ this.app.use(express.urlencoded({ extended: true }));
32
+
33
+ // Local mode indicator
34
+ this.app.use((req, res, next) => {
35
+ res.setHeader('X-SPAPS-Mode', 'local-development');
36
+
37
+ // Auto-auth in local mode
38
+ if (!req.headers.authorization && !req.headers['x-api-key']) {
39
+ req.headers['x-api-key'] = 'local-dev-key';
40
+ req.user = {
41
+ id: 'local-user-123',
42
+ email: 'dev@localhost',
43
+ role: req.query._user || req.headers['x-test-user'] || 'user'
44
+ };
45
+ }
46
+
47
+ // Log requests
48
+ console.log(chalk.dim(`${req.method} ${req.path}`));
49
+ next();
50
+ });
51
+ }
52
+
53
+ setupRoutes() {
54
+ // Health check
55
+ this.app.get('/health', (req, res) => {
56
+ res.json({
57
+ status: 'healthy',
58
+ mode: 'local-development',
59
+ version: '0.2.0',
60
+ timestamp: new Date().toISOString()
61
+ });
62
+ });
63
+
64
+ // Local mode status
65
+ this.app.get('/health/local-mode', (req, res) => {
66
+ res.json({
67
+ enabled: true,
68
+ environment: 'local-development',
69
+ features: {
70
+ autoAuth: true,
71
+ corsEnabled: true,
72
+ testUsers: ['user', 'admin', 'premium'],
73
+ apiKeyRequired: false
74
+ }
75
+ });
76
+ });
77
+
78
+ // Mock authentication endpoints
79
+ this.app.post('/api/auth/login', (req, res) => {
80
+ const { email, password } = req.body;
81
+ res.json({
82
+ access_token: 'local-jwt-token-' + Date.now(),
83
+ refresh_token: 'local-refresh-token-' + Date.now(),
84
+ user: {
85
+ id: 'local-user-123',
86
+ email: email || 'dev@localhost',
87
+ role: 'user'
88
+ }
89
+ });
90
+ });
91
+
92
+ this.app.post('/api/auth/register', (req, res) => {
93
+ const { email, password } = req.body;
94
+ res.json({
95
+ access_token: 'local-jwt-token-' + Date.now(),
96
+ refresh_token: 'local-refresh-token-' + Date.now(),
97
+ user: {
98
+ id: 'local-user-' + Date.now(),
99
+ email: email || 'dev@localhost',
100
+ role: 'user'
101
+ }
102
+ });
103
+ });
104
+
105
+ this.app.post('/api/auth/wallet-sign-in', (req, res) => {
106
+ const { wallet_address, chain_type } = req.body;
107
+ res.json({
108
+ access_token: 'local-jwt-token-' + Date.now(),
109
+ refresh_token: 'local-refresh-token-' + Date.now(),
110
+ user: {
111
+ id: 'local-wallet-user-123',
112
+ wallet_address,
113
+ chain_type,
114
+ role: 'user'
115
+ }
116
+ });
117
+ });
118
+
119
+ this.app.post('/api/auth/refresh', (req, res) => {
120
+ res.json({
121
+ access_token: 'local-jwt-token-refreshed-' + Date.now(),
122
+ refresh_token: 'local-refresh-token-refreshed-' + Date.now()
123
+ });
124
+ });
125
+
126
+ this.app.post('/api/auth/logout', (req, res) => {
127
+ res.json({ success: true, message: 'Logged out successfully' });
128
+ });
129
+
130
+ this.app.get('/api/auth/user', (req, res) => {
131
+ res.json({
132
+ id: req.user?.id || 'local-user-123',
133
+ email: req.user?.email || 'dev@localhost',
134
+ role: req.user?.role || 'user',
135
+ created_at: new Date().toISOString()
136
+ });
137
+ });
138
+
139
+ // Mock Stripe endpoints
140
+ this.app.post('/api/stripe/create-checkout-session', (req, res) => {
141
+ res.json({
142
+ sessionId: 'cs_test_local_' + Date.now(),
143
+ url: 'https://checkout.stripe.com/pay/cs_test_local'
144
+ });
145
+ });
146
+
147
+ this.app.get('/api/stripe/subscription', (req, res) => {
148
+ res.json({
149
+ id: 'sub_local_123',
150
+ status: 'active',
151
+ plan: 'premium',
152
+ current_period_end: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()
153
+ });
154
+ });
155
+
156
+ // Mock usage endpoints
157
+ this.app.get('/api/usage/balance', (req, res) => {
158
+ res.json({
159
+ balance: 1000,
160
+ currency: 'credits',
161
+ updated_at: new Date().toISOString()
162
+ });
163
+ });
164
+
165
+ // Documentation endpoint
166
+ this.app.get('/docs', (req, res) => {
167
+ res.send(`
168
+ <!DOCTYPE html>
169
+ <html>
170
+ <head>
171
+ <title>SPAPS Local Mode</title>
172
+ <style>
173
+ body {
174
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
175
+ max-width: 800px;
176
+ margin: 50px auto;
177
+ padding: 20px;
178
+ line-height: 1.6;
179
+ }
180
+ h1 { color: #ff6b6b; }
181
+ code {
182
+ background: #f4f4f4;
183
+ padding: 2px 6px;
184
+ border-radius: 3px;
185
+ }
186
+ pre {
187
+ background: #f4f4f4;
188
+ padding: 15px;
189
+ border-radius: 5px;
190
+ overflow-x: auto;
191
+ }
192
+ .endpoint {
193
+ margin: 20px 0;
194
+ padding: 15px;
195
+ border-left: 3px solid #ff6b6b;
196
+ background: #fff9f9;
197
+ }
198
+ </style>
199
+ </head>
200
+ <body>
201
+ <h1>šŸ  SPAPS Local Development Mode</h1>
202
+ <p>You're running in <strong>local development mode</strong>. No API keys required!</p>
203
+
204
+ <h2>Quick Start</h2>
205
+ <pre>
206
+ // No configuration needed!
207
+ const response = await fetch('http://localhost:${this.port}/api/auth/login', {
208
+ method: 'POST',
209
+ headers: { 'Content-Type': 'application/json' },
210
+ body: JSON.stringify({
211
+ email: 'test@example.com',
212
+ password: 'password'
213
+ })
214
+ });
215
+ const { access_token, user } = await response.json();
216
+ </pre>
217
+
218
+ <h2>Available Endpoints</h2>
219
+
220
+ <div class="endpoint">
221
+ <strong>GET /health</strong> - Health check
222
+ </div>
223
+
224
+ <div class="endpoint">
225
+ <strong>POST /api/auth/login</strong> - Email/password login
226
+ </div>
227
+
228
+ <div class="endpoint">
229
+ <strong>POST /api/auth/register</strong> - Create account
230
+ </div>
231
+
232
+ <div class="endpoint">
233
+ <strong>POST /api/auth/wallet-sign-in</strong> - Wallet authentication
234
+ </div>
235
+
236
+ <div class="endpoint">
237
+ <strong>GET /api/auth/user</strong> - Get current user
238
+ </div>
239
+
240
+ <div class="endpoint">
241
+ <strong>POST /api/stripe/create-checkout-session</strong> - Create payment session
242
+ </div>
243
+
244
+ <div class="endpoint">
245
+ <strong>GET /api/usage/balance</strong> - Check usage balance
246
+ </div>
247
+
248
+ <h2>Test Users</h2>
249
+ <p>Switch between test users by adding <code>?_user=admin</code> or <code>?_user=premium</code> to any request.</p>
250
+
251
+ <h2>Environment</h2>
252
+ <pre>
253
+ Mode: ${process.env.NODE_ENV || 'development'}
254
+ Port: ${this.port}
255
+ Auto-Auth: Enabled
256
+ CORS: Enabled (all origins)
257
+ </pre>
258
+ </body>
259
+ </html>
260
+ `);
261
+ });
262
+
263
+ // Catch-all for unimplemented routes
264
+ this.app.use((req, res) => {
265
+ res.status(404).json({
266
+ error: 'Not found',
267
+ message: `Endpoint ${req.method} ${req.path} not implemented in local mode`,
268
+ suggestion: 'Check /docs for available endpoints'
269
+ });
270
+ });
271
+ }
272
+
273
+ start() {
274
+ return new Promise((resolve, reject) => {
275
+ const server = this.app.listen(this.port, (err) => {
276
+ if (err) {
277
+ reject(err);
278
+ } else {
279
+ console.log();
280
+ console.log(chalk.yellow('šŸ  SPAPS Local Development Server'));
281
+ console.log(chalk.green(`✨ Running at: http://localhost:${this.port}`));
282
+ console.log(chalk.blue(`šŸ“ Documentation: http://localhost:${this.port}/docs`));
283
+ console.log(chalk.dim(' Press Ctrl+C to stop'));
284
+ console.log();
285
+ resolve(server);
286
+ }
287
+ });
288
+
289
+ // Handle errors
290
+ server.on('error', (err) => {
291
+ if (err.code === 'EADDRINUSE') {
292
+ console.error(chalk.red(`āŒ Port ${this.port} is already in use`));
293
+ console.log(chalk.yellow('šŸ’” Try: spaps local --port 3301'));
294
+ } else {
295
+ console.error(chalk.red('āŒ Server error:'), err.message);
296
+ }
297
+ process.exit(1);
298
+ });
299
+ });
300
+ }
301
+ }
302
+
303
+ // Export for use in CLI
304
+ module.exports = LocalServer;
305
+
306
+ // Run directly if called as script
307
+ if (require.main === module) {
308
+ const server = new LocalServer();
309
+ server.start().catch(console.error);
310
+
311
+ // Graceful shutdown
312
+ process.on('SIGINT', () => {
313
+ console.log(chalk.yellow('\nšŸ‘‹ Shutting down...'));
314
+ process.exit(0);
315
+ });
316
+ }