spaps 0.2.0 ā 0.2.3
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/bin/spaps.js +111 -40
- package/client.js +147 -0
- package/package.json +6 -1
- package/src/local-server.js +21 -14
package/bin/spaps.js
CHANGED
|
@@ -23,7 +23,8 @@ ${chalk.yellow('š SPAPS')} - Sweet Potato Authentication & Payment Service
|
|
|
23
23
|
program
|
|
24
24
|
.name('spaps')
|
|
25
25
|
.description('CLI for Sweet Potato Authentication & Payment Service')
|
|
26
|
-
.version(version)
|
|
26
|
+
.version(version)
|
|
27
|
+
.option('--json', 'Output in JSON format for machine parsing');
|
|
27
28
|
|
|
28
29
|
// Local command - Start local development server
|
|
29
30
|
program
|
|
@@ -31,45 +32,87 @@ program
|
|
|
31
32
|
.description('Start local SPAPS server (no API keys required!)')
|
|
32
33
|
.option('-p, --port <port>', 'Port to run on', '3300')
|
|
33
34
|
.option('-o, --open', 'Open browser automatically', false)
|
|
34
|
-
.
|
|
35
|
-
|
|
35
|
+
.option('--json', 'Output in JSON format')
|
|
36
|
+
.action(async (options, command) => {
|
|
37
|
+
const isJson = options.json || command.parent.opts().json;
|
|
38
|
+
|
|
39
|
+
if (!isJson) {
|
|
40
|
+
console.log(logo);
|
|
41
|
+
}
|
|
36
42
|
|
|
37
43
|
try {
|
|
38
44
|
// Import and start the local server
|
|
39
45
|
const LocalServer = require('../src/local-server.js');
|
|
40
|
-
const server = new LocalServer({ port: options.port });
|
|
46
|
+
const server = new LocalServer({ port: options.port, json: isJson });
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
if (isJson) {
|
|
49
|
+
// For JSON output, start server and return immediately
|
|
50
|
+
await server.start();
|
|
51
|
+
console.log(JSON.stringify({
|
|
52
|
+
success: true,
|
|
53
|
+
command: 'local',
|
|
54
|
+
server: {
|
|
55
|
+
url: `http://localhost:${options.port}`,
|
|
56
|
+
docs: `http://localhost:${options.port}/docs`,
|
|
57
|
+
mode: 'local-development',
|
|
58
|
+
port: parseInt(options.port),
|
|
59
|
+
features: {
|
|
60
|
+
autoAuth: true,
|
|
61
|
+
corsEnabled: true,
|
|
62
|
+
testUsers: ['user', 'admin', 'premium'],
|
|
63
|
+
apiKeyRequired: false
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}));
|
|
67
|
+
} else {
|
|
68
|
+
await server.start();
|
|
69
|
+
|
|
70
|
+
// Open browser if requested
|
|
71
|
+
if (options.open) {
|
|
72
|
+
const { exec } = require('child_process');
|
|
73
|
+
const url = `http://localhost:${options.port}/docs`;
|
|
74
|
+
const start = process.platform === 'darwin' ? 'open' :
|
|
75
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
76
|
+
exec(`${start} ${url}`);
|
|
77
|
+
}
|
|
51
78
|
}
|
|
52
79
|
|
|
53
80
|
// Keep process running
|
|
54
81
|
process.on('SIGINT', () => {
|
|
55
|
-
|
|
82
|
+
if (!isJson) {
|
|
83
|
+
console.log(chalk.yellow('\nš Shutting down SPAPS local server...'));
|
|
84
|
+
}
|
|
56
85
|
process.exit(0);
|
|
57
86
|
});
|
|
58
87
|
|
|
59
88
|
} catch (error) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
89
|
+
if (isJson) {
|
|
90
|
+
console.log(JSON.stringify({
|
|
91
|
+
success: false,
|
|
92
|
+
command: 'local',
|
|
93
|
+
error: {
|
|
94
|
+
message: error.message,
|
|
95
|
+
code: error.code,
|
|
96
|
+
suggestion: error.code === 'EADDRINUSE' ?
|
|
97
|
+
'Try a different port with --port option' :
|
|
98
|
+
'Check error message for details'
|
|
99
|
+
}
|
|
100
|
+
}));
|
|
101
|
+
} else {
|
|
102
|
+
console.error(chalk.red('ā Failed to start server:'), error.message);
|
|
103
|
+
|
|
104
|
+
if (error.code === 'MODULE_NOT_FOUND') {
|
|
105
|
+
console.log(chalk.yellow('\nš” Installing dependencies...'));
|
|
106
|
+
const { execSync } = require('child_process');
|
|
107
|
+
try {
|
|
108
|
+
execSync('npm install express cors', {
|
|
109
|
+
cwd: path.join(__dirname, '..'),
|
|
110
|
+
stdio: 'inherit'
|
|
111
|
+
});
|
|
112
|
+
console.log(chalk.green('ā
Dependencies installed! Please run the command again.'));
|
|
113
|
+
} catch (installError) {
|
|
114
|
+
console.error(chalk.red('Failed to install dependencies. Please run: npm install express cors'));
|
|
115
|
+
}
|
|
73
116
|
}
|
|
74
117
|
}
|
|
75
118
|
|
|
@@ -81,10 +124,15 @@ program
|
|
|
81
124
|
program
|
|
82
125
|
.command('init')
|
|
83
126
|
.description('Initialize SPAPS in your project')
|
|
84
|
-
.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
127
|
+
.option('--json', 'Output in JSON format')
|
|
128
|
+
.action((options, command) => {
|
|
129
|
+
const isJson = options.json || command.parent.opts().json;
|
|
130
|
+
|
|
131
|
+
if (!isJson) {
|
|
132
|
+
console.log(logo);
|
|
133
|
+
console.log(chalk.green('š§ Initializing SPAPS...'));
|
|
134
|
+
console.log();
|
|
135
|
+
}
|
|
88
136
|
|
|
89
137
|
// Create minimal .env.local
|
|
90
138
|
const envContent = `# SPAPS Local Development Configuration
|
|
@@ -97,20 +145,43 @@ NODE_ENV=development
|
|
|
97
145
|
# SPAPS_API_KEY=your-api-key-here
|
|
98
146
|
`;
|
|
99
147
|
|
|
148
|
+
const result = {
|
|
149
|
+
success: true,
|
|
150
|
+
command: 'init',
|
|
151
|
+
files_created: [],
|
|
152
|
+
files_skipped: [],
|
|
153
|
+
next_steps: [
|
|
154
|
+
'npx spaps local',
|
|
155
|
+
'npm install @spaps/sdk',
|
|
156
|
+
'Start coding!'
|
|
157
|
+
]
|
|
158
|
+
};
|
|
159
|
+
|
|
100
160
|
if (!fs.existsSync('.env.local')) {
|
|
101
161
|
fs.writeFileSync('.env.local', envContent);
|
|
102
|
-
|
|
162
|
+
result.files_created.push('.env.local');
|
|
163
|
+
if (!isJson) {
|
|
164
|
+
console.log(chalk.green('ā
Created .env.local'));
|
|
165
|
+
}
|
|
103
166
|
} else {
|
|
104
|
-
|
|
167
|
+
result.files_skipped.push('.env.local');
|
|
168
|
+
result.message = '.env.local already exists';
|
|
169
|
+
if (!isJson) {
|
|
170
|
+
console.log(chalk.yellow('ā ļø .env.local already exists'));
|
|
171
|
+
}
|
|
105
172
|
}
|
|
106
173
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
174
|
+
if (isJson) {
|
|
175
|
+
console.log(JSON.stringify(result));
|
|
176
|
+
} else {
|
|
177
|
+
console.log();
|
|
178
|
+
console.log(chalk.green('⨠SPAPS initialized!'));
|
|
179
|
+
console.log();
|
|
180
|
+
console.log('Next steps:');
|
|
181
|
+
console.log(chalk.cyan(' 1. Run: npx spaps local'));
|
|
182
|
+
console.log(chalk.cyan(' 2. Install SDK: npm install @spaps/sdk'));
|
|
183
|
+
console.log(chalk.cyan(' 3. Start coding!'));
|
|
184
|
+
}
|
|
114
185
|
});
|
|
115
186
|
|
|
116
187
|
// Create command (placeholder)
|
package/client.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPAPS Client - Re-export from spaps-sdk
|
|
3
|
+
* This allows users to import from 'spaps/client'
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
// Try to load spaps-sdk if it's installed
|
|
8
|
+
const sdk = require('spaps-sdk');
|
|
9
|
+
|
|
10
|
+
// Support both default export and named export
|
|
11
|
+
const SPAPSClient = sdk.SPAPSClient || sdk.default || sdk;
|
|
12
|
+
|
|
13
|
+
module.exports = SPAPSClient;
|
|
14
|
+
module.exports.SPAPSClient = SPAPSClient;
|
|
15
|
+
module.exports.default = SPAPSClient;
|
|
16
|
+
module.exports.SPAPS = SPAPSClient;
|
|
17
|
+
module.exports.SweetPotatoSDK = SPAPSClient;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
// Fallback to a simple client implementation for local development
|
|
20
|
+
const axios = require('axios');
|
|
21
|
+
|
|
22
|
+
class SPAPSClient {
|
|
23
|
+
constructor(config = {}) {
|
|
24
|
+
const apiUrl = config.apiUrl || process.env.SPAPS_API_URL || 'http://localhost:3300';
|
|
25
|
+
this.isLocalMode = apiUrl.includes('localhost') || apiUrl.includes('127.0.0.1');
|
|
26
|
+
|
|
27
|
+
this.client = axios.create({
|
|
28
|
+
baseURL: apiUrl,
|
|
29
|
+
timeout: config.timeout || 10000,
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
...(config.apiKey && { 'X-API-Key': config.apiKey })
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
this.accessToken = null;
|
|
37
|
+
this.refreshToken = null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async login(email, password) {
|
|
41
|
+
const response = await this.client.post('/api/auth/login', { email, password });
|
|
42
|
+
this.accessToken = response.data.access_token;
|
|
43
|
+
this.refreshToken = response.data.refresh_token;
|
|
44
|
+
return response;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async register(email, password) {
|
|
48
|
+
const response = await this.client.post('/api/auth/register', { email, password });
|
|
49
|
+
this.accessToken = response.data.access_token;
|
|
50
|
+
this.refreshToken = response.data.refresh_token;
|
|
51
|
+
return response;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async walletSignIn(walletAddress, signature, message, chainType = 'solana') {
|
|
55
|
+
const response = await this.client.post('/api/auth/wallet-sign-in', {
|
|
56
|
+
wallet_address: walletAddress,
|
|
57
|
+
signature,
|
|
58
|
+
message,
|
|
59
|
+
chain_type: chainType
|
|
60
|
+
});
|
|
61
|
+
this.accessToken = response.data.access_token;
|
|
62
|
+
this.refreshToken = response.data.refresh_token;
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getUser() {
|
|
67
|
+
return this.client.get('/api/auth/user', {
|
|
68
|
+
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async createCheckoutSession(priceId, successUrl, cancelUrl) {
|
|
73
|
+
return this.client.post('/api/stripe/create-checkout-session',
|
|
74
|
+
{ price_id: priceId, success_url: successUrl, cancel_url: cancelUrl },
|
|
75
|
+
{ headers: { Authorization: `Bearer ${this.accessToken}` } }
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async getSubscription() {
|
|
80
|
+
return this.client.get('/api/stripe/subscription', {
|
|
81
|
+
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getUsageBalance() {
|
|
86
|
+
return this.client.get('/api/usage/balance', {
|
|
87
|
+
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isAuthenticated() {
|
|
92
|
+
return !!this.accessToken;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
getAccessToken() {
|
|
96
|
+
return this.accessToken;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setAccessToken(token) {
|
|
100
|
+
this.accessToken = token;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
isLocalMode() {
|
|
104
|
+
return this.isLocalMode;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async health() {
|
|
108
|
+
return this.client.get('/health');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async logout() {
|
|
112
|
+
await this.client.post('/api/auth/logout', {}, {
|
|
113
|
+
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
114
|
+
});
|
|
115
|
+
this.accessToken = null;
|
|
116
|
+
this.refreshToken = null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async refresh(refreshToken) {
|
|
120
|
+
const response = await this.client.post('/api/auth/refresh', {
|
|
121
|
+
refresh_token: refreshToken || this.refreshToken
|
|
122
|
+
});
|
|
123
|
+
this.accessToken = response.data.access_token;
|
|
124
|
+
this.refreshToken = response.data.refresh_token;
|
|
125
|
+
return response;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async recordUsage(feature, amount) {
|
|
129
|
+
return this.client.post('/api/usage/record',
|
|
130
|
+
{ feature, amount },
|
|
131
|
+
{ headers: { Authorization: `Bearer ${this.accessToken}` } }
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async cancelSubscription() {
|
|
136
|
+
return this.client.delete('/api/stripe/subscription', {
|
|
137
|
+
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = SPAPSClient;
|
|
143
|
+
module.exports.SPAPSClient = SPAPSClient;
|
|
144
|
+
module.exports.default = SPAPSClient;
|
|
145
|
+
module.exports.SPAPS = SPAPSClient;
|
|
146
|
+
module.exports.SweetPotatoSDK = SPAPSClient;
|
|
147
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spaps",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
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": {
|
|
7
7
|
"spaps": "./bin/spaps.js"
|
|
8
8
|
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./bin/spaps.js",
|
|
11
|
+
"./client": "./client.js"
|
|
12
|
+
},
|
|
9
13
|
"scripts": {
|
|
10
14
|
"test": "echo \"No tests yet\""
|
|
11
15
|
},
|
|
@@ -47,6 +51,7 @@
|
|
|
47
51
|
"files": [
|
|
48
52
|
"bin",
|
|
49
53
|
"src",
|
|
54
|
+
"client.js",
|
|
50
55
|
"README.md"
|
|
51
56
|
]
|
|
52
57
|
}
|
package/src/local-server.js
CHANGED
|
@@ -12,6 +12,7 @@ const chalk = require('chalk');
|
|
|
12
12
|
class LocalServer {
|
|
13
13
|
constructor(options = {}) {
|
|
14
14
|
this.port = options.port || process.env.PORT || 3300;
|
|
15
|
+
this.json = options.json || false;
|
|
15
16
|
this.app = express();
|
|
16
17
|
this.setupMiddleware();
|
|
17
18
|
this.setupRoutes();
|
|
@@ -44,8 +45,10 @@ class LocalServer {
|
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
// Log requests
|
|
48
|
-
|
|
48
|
+
// Log requests (unless in JSON mode)
|
|
49
|
+
if (!this.json) {
|
|
50
|
+
console.log(chalk.dim(`${req.method} ${req.path}`));
|
|
51
|
+
}
|
|
49
52
|
next();
|
|
50
53
|
});
|
|
51
54
|
}
|
|
@@ -276,25 +279,29 @@ CORS: Enabled (all origins)
|
|
|
276
279
|
if (err) {
|
|
277
280
|
reject(err);
|
|
278
281
|
} else {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
if (!this.json) {
|
|
283
|
+
console.log();
|
|
284
|
+
console.log(chalk.yellow('š SPAPS Local Development Server'));
|
|
285
|
+
console.log(chalk.green(`⨠Running at: http://localhost:${this.port}`));
|
|
286
|
+
console.log(chalk.blue(`š Documentation: http://localhost:${this.port}/docs`));
|
|
287
|
+
console.log(chalk.dim(' Press Ctrl+C to stop'));
|
|
288
|
+
console.log();
|
|
289
|
+
}
|
|
285
290
|
resolve(server);
|
|
286
291
|
}
|
|
287
292
|
});
|
|
288
293
|
|
|
289
294
|
// Handle errors
|
|
290
295
|
server.on('error', (err) => {
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
+
if (!this.json) {
|
|
297
|
+
if (err.code === 'EADDRINUSE') {
|
|
298
|
+
console.error(chalk.red(`ā Port ${this.port} is already in use`));
|
|
299
|
+
console.log(chalk.yellow('š” Try: spaps local --port 3301'));
|
|
300
|
+
} else {
|
|
301
|
+
console.error(chalk.red('ā Server error:'), err.message);
|
|
302
|
+
}
|
|
296
303
|
}
|
|
297
|
-
|
|
304
|
+
reject(err);
|
|
298
305
|
});
|
|
299
306
|
});
|
|
300
307
|
}
|