spaps 0.2.6 → 0.2.7
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/package.json +1 -1
- package/src/docs-html.js +763 -0
- package/src/local-server.js +2 -94
package/package.json
CHANGED
package/src/docs-html.js
ADDED
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPAPS Local Server Documentation HTML Generator
|
|
3
|
+
* Generates comprehensive documentation page
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
function generateDocsHTML(port = 3300) {
|
|
7
|
+
return `<!DOCTYPE html>
|
|
8
|
+
<html lang="en">
|
|
9
|
+
<head>
|
|
10
|
+
<meta charset="UTF-8">
|
|
11
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
12
|
+
<title>SPAPS Documentation - Local Development Mode</title>
|
|
13
|
+
<style>
|
|
14
|
+
* {
|
|
15
|
+
margin: 0;
|
|
16
|
+
padding: 0;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
22
|
+
line-height: 1.6;
|
|
23
|
+
color: #333;
|
|
24
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
25
|
+
min-height: 100vh;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.container {
|
|
29
|
+
max-width: 1200px;
|
|
30
|
+
margin: 0 auto;
|
|
31
|
+
padding: 2rem;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.header {
|
|
35
|
+
background: white;
|
|
36
|
+
border-radius: 12px;
|
|
37
|
+
padding: 2rem;
|
|
38
|
+
margin-bottom: 2rem;
|
|
39
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.header h1 {
|
|
43
|
+
color: #764ba2;
|
|
44
|
+
font-size: 2.5rem;
|
|
45
|
+
margin-bottom: 0.5rem;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.header .subtitle {
|
|
49
|
+
color: #666;
|
|
50
|
+
font-size: 1.1rem;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.status-badge {
|
|
54
|
+
display: inline-block;
|
|
55
|
+
background: #48bb78;
|
|
56
|
+
color: white;
|
|
57
|
+
padding: 0.25rem 0.75rem;
|
|
58
|
+
border-radius: 20px;
|
|
59
|
+
font-size: 0.9rem;
|
|
60
|
+
margin-top: 1rem;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.nav {
|
|
64
|
+
background: white;
|
|
65
|
+
border-radius: 12px;
|
|
66
|
+
padding: 1rem;
|
|
67
|
+
margin-bottom: 2rem;
|
|
68
|
+
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
|
|
69
|
+
position: sticky;
|
|
70
|
+
top: 1rem;
|
|
71
|
+
z-index: 100;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.nav ul {
|
|
75
|
+
list-style: none;
|
|
76
|
+
display: flex;
|
|
77
|
+
gap: 2rem;
|
|
78
|
+
overflow-x: auto;
|
|
79
|
+
padding: 0.5rem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.nav a {
|
|
83
|
+
color: #764ba2;
|
|
84
|
+
text-decoration: none;
|
|
85
|
+
font-weight: 500;
|
|
86
|
+
white-space: nowrap;
|
|
87
|
+
transition: color 0.3s;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.nav a:hover {
|
|
91
|
+
color: #667eea;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.content {
|
|
95
|
+
background: white;
|
|
96
|
+
border-radius: 12px;
|
|
97
|
+
padding: 2rem;
|
|
98
|
+
margin-bottom: 2rem;
|
|
99
|
+
box-shadow: 0 5px 15px rgba(0,0,0,0.08);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
h2 {
|
|
103
|
+
color: #764ba2;
|
|
104
|
+
margin-top: 2rem;
|
|
105
|
+
margin-bottom: 1rem;
|
|
106
|
+
padding-bottom: 0.5rem;
|
|
107
|
+
border-bottom: 2px solid #f0f0f0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
h3 {
|
|
111
|
+
color: #667eea;
|
|
112
|
+
margin-top: 1.5rem;
|
|
113
|
+
margin-bottom: 0.75rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
pre {
|
|
117
|
+
background: #f7f7f7;
|
|
118
|
+
border: 1px solid #e0e0e0;
|
|
119
|
+
border-radius: 8px;
|
|
120
|
+
padding: 1rem;
|
|
121
|
+
overflow-x: auto;
|
|
122
|
+
margin: 1rem 0;
|
|
123
|
+
position: relative;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
code {
|
|
127
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace;
|
|
128
|
+
font-size: 0.9rem;
|
|
129
|
+
background: #f7f7f7;
|
|
130
|
+
padding: 0.2rem 0.4rem;
|
|
131
|
+
border-radius: 4px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pre code {
|
|
135
|
+
background: none;
|
|
136
|
+
padding: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.endpoint {
|
|
140
|
+
background: #f9f9f9;
|
|
141
|
+
border-left: 4px solid #667eea;
|
|
142
|
+
padding: 1rem;
|
|
143
|
+
margin: 1rem 0;
|
|
144
|
+
border-radius: 4px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.endpoint strong {
|
|
148
|
+
color: #764ba2;
|
|
149
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
150
|
+
font-size: 0.95rem;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.endpoint .method {
|
|
154
|
+
display: inline-block;
|
|
155
|
+
padding: 0.2rem 0.5rem;
|
|
156
|
+
border-radius: 4px;
|
|
157
|
+
font-weight: bold;
|
|
158
|
+
font-size: 0.85rem;
|
|
159
|
+
margin-right: 0.5rem;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.endpoint .method.get { background: #48bb78; color: white; }
|
|
163
|
+
.endpoint .method.post { background: #4299e1; color: white; }
|
|
164
|
+
.endpoint .method.put { background: #ed8936; color: white; }
|
|
165
|
+
.endpoint .method.delete { background: #f56565; color: white; }
|
|
166
|
+
|
|
167
|
+
.tabs {
|
|
168
|
+
display: flex;
|
|
169
|
+
gap: 1rem;
|
|
170
|
+
margin-bottom: 1rem;
|
|
171
|
+
border-bottom: 2px solid #f0f0f0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.tab {
|
|
175
|
+
padding: 0.5rem 1rem;
|
|
176
|
+
cursor: pointer;
|
|
177
|
+
border: none;
|
|
178
|
+
background: none;
|
|
179
|
+
color: #666;
|
|
180
|
+
font-size: 1rem;
|
|
181
|
+
transition: all 0.3s;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.tab.active {
|
|
185
|
+
color: #764ba2;
|
|
186
|
+
border-bottom: 2px solid #764ba2;
|
|
187
|
+
margin-bottom: -2px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.tab-content {
|
|
191
|
+
display: none;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.tab-content.active {
|
|
195
|
+
display: block;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.feature-grid {
|
|
199
|
+
display: grid;
|
|
200
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
201
|
+
gap: 1.5rem;
|
|
202
|
+
margin: 2rem 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.feature-card {
|
|
206
|
+
background: #f9f9f9;
|
|
207
|
+
padding: 1.5rem;
|
|
208
|
+
border-radius: 8px;
|
|
209
|
+
transition: transform 0.3s;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.feature-card:hover {
|
|
213
|
+
transform: translateY(-2px);
|
|
214
|
+
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.feature-card h3 {
|
|
218
|
+
margin-top: 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.copy-button {
|
|
222
|
+
position: absolute;
|
|
223
|
+
top: 0.5rem;
|
|
224
|
+
right: 0.5rem;
|
|
225
|
+
background: #667eea;
|
|
226
|
+
color: white;
|
|
227
|
+
border: none;
|
|
228
|
+
padding: 0.25rem 0.75rem;
|
|
229
|
+
border-radius: 4px;
|
|
230
|
+
cursor: pointer;
|
|
231
|
+
font-size: 0.85rem;
|
|
232
|
+
transition: background 0.3s;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.copy-button:hover {
|
|
236
|
+
background: #764ba2;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.copy-button.copied {
|
|
240
|
+
background: #48bb78;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.alert {
|
|
244
|
+
background: #fef5e7;
|
|
245
|
+
border: 1px solid #f9e79f;
|
|
246
|
+
border-radius: 8px;
|
|
247
|
+
padding: 1rem;
|
|
248
|
+
margin: 1rem 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.alert.info {
|
|
252
|
+
background: #e8f4fd;
|
|
253
|
+
border-color: #bee5eb;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.alert.success {
|
|
257
|
+
background: #d4edda;
|
|
258
|
+
border-color: #c3e6cb;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.footer {
|
|
262
|
+
text-align: center;
|
|
263
|
+
color: white;
|
|
264
|
+
padding: 2rem;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.footer a {
|
|
268
|
+
color: white;
|
|
269
|
+
text-decoration: underline;
|
|
270
|
+
}
|
|
271
|
+
</style>
|
|
272
|
+
</head>
|
|
273
|
+
<body>
|
|
274
|
+
<div class="container">
|
|
275
|
+
<div class="header">
|
|
276
|
+
<h1>🍠 SPAPS Documentation</h1>
|
|
277
|
+
<p class="subtitle">Sweet Potato Authentication & Payment Service</p>
|
|
278
|
+
<span class="status-badge">✅ Local Mode Active - Port ${port}</span>
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
<nav class="nav">
|
|
282
|
+
<ul>
|
|
283
|
+
<li><a href="#quickstart">Quick Start</a></li>
|
|
284
|
+
<li><a href="#sdk">SDK Setup</a></li>
|
|
285
|
+
<li><a href="#authentication">Authentication</a></li>
|
|
286
|
+
<li><a href="#payments">Payments</a></li>
|
|
287
|
+
<li><a href="#endpoints">API Endpoints</a></li>
|
|
288
|
+
<li><a href="#examples">Examples</a></li>
|
|
289
|
+
<li><a href="#testing">Testing</a></li>
|
|
290
|
+
</ul>
|
|
291
|
+
</nav>
|
|
292
|
+
|
|
293
|
+
<div class="content">
|
|
294
|
+
<div class="alert info">
|
|
295
|
+
<strong>🚀 Local Development Mode</strong><br>
|
|
296
|
+
You're running in local mode. No API keys, database, or external services required!
|
|
297
|
+
All responses are mocked for rapid development.
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<section id="quickstart">
|
|
301
|
+
<h2>Quick Start</h2>
|
|
302
|
+
|
|
303
|
+
<h3>1. Install the SDK</h3>
|
|
304
|
+
<pre><code>npm install spaps-sdk
|
|
305
|
+
# or
|
|
306
|
+
yarn add spaps-sdk</code></pre>
|
|
307
|
+
|
|
308
|
+
<h3>2. Initialize the Client</h3>
|
|
309
|
+
<div class="tabs">
|
|
310
|
+
<button class="tab active" onclick="showTab(event, 'js-init')">JavaScript</button>
|
|
311
|
+
<button class="tab" onclick="showTab(event, 'ts-init')">TypeScript</button>
|
|
312
|
+
<button class="tab" onclick="showTab(event, 'react-init')">React</button>
|
|
313
|
+
</div>
|
|
314
|
+
|
|
315
|
+
<div id="js-init" class="tab-content active">
|
|
316
|
+
<pre><code>const { SPAPSClient } = require('spaps-sdk');
|
|
317
|
+
|
|
318
|
+
// Auto-detects local mode - no config needed!
|
|
319
|
+
const spaps = new SPAPSClient();
|
|
320
|
+
|
|
321
|
+
// Login
|
|
322
|
+
const { data } = await spaps.login('user@example.com', 'password');
|
|
323
|
+
console.log('User:', data.user);
|
|
324
|
+
console.log('Token:', data.access_token);</code></pre>
|
|
325
|
+
</div>
|
|
326
|
+
|
|
327
|
+
<div id="ts-init" class="tab-content">
|
|
328
|
+
<pre><code>import { SPAPSClient } from 'spaps-sdk';
|
|
329
|
+
|
|
330
|
+
// TypeScript types included
|
|
331
|
+
const spaps = new SPAPSClient({
|
|
332
|
+
apiUrl: 'http://localhost:${port}'
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Fully typed responses
|
|
336
|
+
const { data } = await spaps.login('user@example.com', 'password');
|
|
337
|
+
console.log('User ID:', data.user.id);</code></pre>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<div id="react-init" class="tab-content">
|
|
341
|
+
<pre><code>import { SPAPSClient } from 'spaps-sdk';
|
|
342
|
+
import { createContext, useContext } from 'react';
|
|
343
|
+
|
|
344
|
+
const spaps = new SPAPSClient();
|
|
345
|
+
const SpapsContext = createContext(spaps);
|
|
346
|
+
|
|
347
|
+
export function SpapsProvider({ children }) {
|
|
348
|
+
return (
|
|
349
|
+
<SpapsContext.Provider value={spaps}>
|
|
350
|
+
{children}
|
|
351
|
+
</SpapsContext.Provider>
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export const useSpaps = () => useContext(SpapsContext);</code></pre>
|
|
356
|
+
</div>
|
|
357
|
+
</section>
|
|
358
|
+
|
|
359
|
+
<section id="sdk">
|
|
360
|
+
<h2>SDK Setup & Configuration</h2>
|
|
361
|
+
|
|
362
|
+
<h3>Installation</h3>
|
|
363
|
+
<pre><code>npm install spaps-sdk</code></pre>
|
|
364
|
+
|
|
365
|
+
<h3>Configuration Options</h3>
|
|
366
|
+
<pre><code>const spaps = new SPAPSClient({
|
|
367
|
+
// API endpoint (auto-detected from env)
|
|
368
|
+
apiUrl: 'http://localhost:${port}',
|
|
369
|
+
|
|
370
|
+
// API key (not needed for localhost)
|
|
371
|
+
apiKey: 'spaps_live_abc123...',
|
|
372
|
+
|
|
373
|
+
// Request timeout in milliseconds
|
|
374
|
+
timeout: 10000,
|
|
375
|
+
|
|
376
|
+
// Auto-detect local mode (default: true)
|
|
377
|
+
autoDetect: true
|
|
378
|
+
});</code></pre>
|
|
379
|
+
|
|
380
|
+
<h3>Environment Variables</h3>
|
|
381
|
+
<pre><code># .env
|
|
382
|
+
SPAPS_API_URL=http://localhost:${port}
|
|
383
|
+
SPAPS_API_KEY=your_api_key_here
|
|
384
|
+
|
|
385
|
+
# Next.js
|
|
386
|
+
NEXT_PUBLIC_SPAPS_API_URL=http://localhost:${port}</code></pre>
|
|
387
|
+
|
|
388
|
+
<div class="feature-grid">
|
|
389
|
+
<div class="feature-card">
|
|
390
|
+
<h3>🔌 Auto-Detection</h3>
|
|
391
|
+
<p>SDK automatically detects local mode when URL contains localhost or 127.0.0.1</p>
|
|
392
|
+
</div>
|
|
393
|
+
<div class="feature-card">
|
|
394
|
+
<h3>🔑 No API Key</h3>
|
|
395
|
+
<p>Local mode doesn't require API keys - perfect for rapid development</p>
|
|
396
|
+
</div>
|
|
397
|
+
<div class="feature-card">
|
|
398
|
+
<h3>📦 TypeScript</h3>
|
|
399
|
+
<p>Full TypeScript support with type definitions included</p>
|
|
400
|
+
</div>
|
|
401
|
+
<div class="feature-card">
|
|
402
|
+
<h3>🔄 Auto-Refresh</h3>
|
|
403
|
+
<p>Tokens automatically refresh when expired</p>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
</section>
|
|
407
|
+
|
|
408
|
+
<section id="authentication">
|
|
409
|
+
<h2>Authentication</h2>
|
|
410
|
+
|
|
411
|
+
<h3>Email/Password Authentication</h3>
|
|
412
|
+
<pre><code>// Register new user
|
|
413
|
+
const { data } = await spaps.register('user@example.com', 'password');
|
|
414
|
+
console.log('New user:', data.user);
|
|
415
|
+
|
|
416
|
+
// Login existing user
|
|
417
|
+
const { data } = await spaps.login('user@example.com', 'password');
|
|
418
|
+
console.log('Access token:', data.access_token);
|
|
419
|
+
|
|
420
|
+
// Get current user
|
|
421
|
+
const user = await spaps.getUser();
|
|
422
|
+
console.log('Current user:', user.data);
|
|
423
|
+
|
|
424
|
+
// Logout
|
|
425
|
+
await spaps.logout();</code></pre>
|
|
426
|
+
|
|
427
|
+
<h3>Wallet Authentication</h3>
|
|
428
|
+
<pre><code>// Solana wallet
|
|
429
|
+
await spaps.walletSignIn(
|
|
430
|
+
walletAddress,
|
|
431
|
+
signature,
|
|
432
|
+
message,
|
|
433
|
+
'solana'
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
// Ethereum wallet
|
|
437
|
+
await spaps.walletSignIn(
|
|
438
|
+
walletAddress,
|
|
439
|
+
signature,
|
|
440
|
+
message,
|
|
441
|
+
'ethereum'
|
|
442
|
+
);</code></pre>
|
|
443
|
+
|
|
444
|
+
<h3>Token Management</h3>
|
|
445
|
+
<pre><code>// Check if authenticated
|
|
446
|
+
if (spaps.isAuthenticated()) {
|
|
447
|
+
console.log('User is logged in');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Get access token
|
|
451
|
+
const token = spaps.getAccessToken();
|
|
452
|
+
|
|
453
|
+
// Set token manually
|
|
454
|
+
spaps.setAccessToken(token);
|
|
455
|
+
|
|
456
|
+
// Refresh token
|
|
457
|
+
await spaps.refresh();</code></pre>
|
|
458
|
+
</section>
|
|
459
|
+
|
|
460
|
+
<section id="payments">
|
|
461
|
+
<h2>Payment Integration</h2>
|
|
462
|
+
|
|
463
|
+
<h3>Stripe Checkout</h3>
|
|
464
|
+
<pre><code>// Create checkout session
|
|
465
|
+
const session = await spaps.createCheckoutSession(
|
|
466
|
+
'price_123abc', // Stripe price ID
|
|
467
|
+
'http://localhost:3000/success', // Success URL
|
|
468
|
+
'http://localhost:3000/cancel' // Cancel URL (optional)
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
// Redirect to Stripe
|
|
472
|
+
window.location.href = session.data.url;</code></pre>
|
|
473
|
+
|
|
474
|
+
<h3>Subscription Management</h3>
|
|
475
|
+
<pre><code>// Get current subscription
|
|
476
|
+
const subscription = await spaps.getSubscription();
|
|
477
|
+
console.log('Status:', subscription.data.status);
|
|
478
|
+
console.log('Plan:', subscription.data.plan);
|
|
479
|
+
|
|
480
|
+
// Cancel subscription
|
|
481
|
+
await spaps.cancelSubscription();</code></pre>
|
|
482
|
+
|
|
483
|
+
<h3>Usage Tracking</h3>
|
|
484
|
+
<pre><code>// Check balance
|
|
485
|
+
const balance = await spaps.getUsageBalance();
|
|
486
|
+
console.log('Credits:', balance.data.balance);
|
|
487
|
+
|
|
488
|
+
// Record usage
|
|
489
|
+
await spaps.recordUsage('api-call', 1);
|
|
490
|
+
await spaps.recordUsage('image-generation', 10);</code></pre>
|
|
491
|
+
</section>
|
|
492
|
+
|
|
493
|
+
<section id="endpoints">
|
|
494
|
+
<h2>API Endpoints</h2>
|
|
495
|
+
|
|
496
|
+
<h3>Authentication</h3>
|
|
497
|
+
<div class="endpoint">
|
|
498
|
+
<span class="method post">POST</span>
|
|
499
|
+
<strong>/api/auth/register</strong> - Register new user
|
|
500
|
+
</div>
|
|
501
|
+
<div class="endpoint">
|
|
502
|
+
<span class="method post">POST</span>
|
|
503
|
+
<strong>/api/auth/login</strong> - Login with email/password
|
|
504
|
+
</div>
|
|
505
|
+
<div class="endpoint">
|
|
506
|
+
<span class="method post">POST</span>
|
|
507
|
+
<strong>/api/auth/wallet-sign-in</strong> - Wallet authentication
|
|
508
|
+
</div>
|
|
509
|
+
<div class="endpoint">
|
|
510
|
+
<span class="method post">POST</span>
|
|
511
|
+
<strong>/api/auth/refresh</strong> - Refresh access token
|
|
512
|
+
</div>
|
|
513
|
+
<div class="endpoint">
|
|
514
|
+
<span class="method post">POST</span>
|
|
515
|
+
<strong>/api/auth/logout</strong> - Logout user
|
|
516
|
+
</div>
|
|
517
|
+
<div class="endpoint">
|
|
518
|
+
<span class="method get">GET</span>
|
|
519
|
+
<strong>/api/auth/user</strong> - Get current user
|
|
520
|
+
</div>
|
|
521
|
+
|
|
522
|
+
<h3>Payments</h3>
|
|
523
|
+
<div class="endpoint">
|
|
524
|
+
<span class="method post">POST</span>
|
|
525
|
+
<strong>/api/stripe/create-checkout-session</strong> - Create Stripe checkout
|
|
526
|
+
</div>
|
|
527
|
+
<div class="endpoint">
|
|
528
|
+
<span class="method get">GET</span>
|
|
529
|
+
<strong>/api/stripe/subscription</strong> - Get subscription status
|
|
530
|
+
</div>
|
|
531
|
+
<div class="endpoint">
|
|
532
|
+
<span class="method delete">DELETE</span>
|
|
533
|
+
<strong>/api/stripe/subscription</strong> - Cancel subscription
|
|
534
|
+
</div>
|
|
535
|
+
|
|
536
|
+
<h3>Usage</h3>
|
|
537
|
+
<div class="endpoint">
|
|
538
|
+
<span class="method get">GET</span>
|
|
539
|
+
<strong>/api/usage/balance</strong> - Get usage balance
|
|
540
|
+
</div>
|
|
541
|
+
<div class="endpoint">
|
|
542
|
+
<span class="method post">POST</span>
|
|
543
|
+
<strong>/api/usage/record</strong> - Record usage event
|
|
544
|
+
</div>
|
|
545
|
+
|
|
546
|
+
<h3>Health</h3>
|
|
547
|
+
<div class="endpoint">
|
|
548
|
+
<span class="method get">GET</span>
|
|
549
|
+
<strong>/health</strong> - Health check
|
|
550
|
+
</div>
|
|
551
|
+
<div class="endpoint">
|
|
552
|
+
<span class="method get">GET</span>
|
|
553
|
+
<strong>/health/local-mode</strong> - Local mode status
|
|
554
|
+
</div>
|
|
555
|
+
</section>
|
|
556
|
+
|
|
557
|
+
<section id="examples">
|
|
558
|
+
<h2>Complete Examples</h2>
|
|
559
|
+
|
|
560
|
+
<h3>React Login Component</h3>
|
|
561
|
+
<pre><code>import { useState } from 'react';
|
|
562
|
+
import { useSpaps } from './contexts/SpapsContext';
|
|
563
|
+
|
|
564
|
+
function LoginForm() {
|
|
565
|
+
const spaps = useSpaps();
|
|
566
|
+
const [email, setEmail] = useState('');
|
|
567
|
+
const [password, setPassword] = useState('');
|
|
568
|
+
const [loading, setLoading] = useState(false);
|
|
569
|
+
|
|
570
|
+
const handleSubmit = async (e) => {
|
|
571
|
+
e.preventDefault();
|
|
572
|
+
setLoading(true);
|
|
573
|
+
|
|
574
|
+
try {
|
|
575
|
+
const { data } = await spaps.login(email, password);
|
|
576
|
+
console.log('Logged in:', data.user);
|
|
577
|
+
// Redirect to dashboard
|
|
578
|
+
} catch (error) {
|
|
579
|
+
console.error('Login failed:', error);
|
|
580
|
+
} finally {
|
|
581
|
+
setLoading(false);
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
return (
|
|
586
|
+
<form onSubmit={handleSubmit}>
|
|
587
|
+
<input
|
|
588
|
+
type="email"
|
|
589
|
+
value={email}
|
|
590
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
591
|
+
required
|
|
592
|
+
/>
|
|
593
|
+
<input
|
|
594
|
+
type="password"
|
|
595
|
+
value={password}
|
|
596
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
597
|
+
required
|
|
598
|
+
/>
|
|
599
|
+
<button type="submit" disabled={loading}>
|
|
600
|
+
{loading ? 'Logging in...' : 'Login'}
|
|
601
|
+
</button>
|
|
602
|
+
</form>
|
|
603
|
+
);
|
|
604
|
+
}</code></pre>
|
|
605
|
+
|
|
606
|
+
<h3>Express.js Middleware</h3>
|
|
607
|
+
<pre><code>const express = require('express');
|
|
608
|
+
const { SPAPSClient } = require('spaps-sdk');
|
|
609
|
+
|
|
610
|
+
const app = express();
|
|
611
|
+
const spaps = new SPAPSClient();
|
|
612
|
+
|
|
613
|
+
// Auth middleware
|
|
614
|
+
async function requireAuth(req, res, next) {
|
|
615
|
+
const token = req.headers.authorization?.split(' ')[1];
|
|
616
|
+
|
|
617
|
+
if (!token) {
|
|
618
|
+
return res.status(401).json({ error: 'No token' });
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
try {
|
|
622
|
+
spaps.setAccessToken(token);
|
|
623
|
+
const { data } = await spaps.getUser();
|
|
624
|
+
req.user = data;
|
|
625
|
+
next();
|
|
626
|
+
} catch (error) {
|
|
627
|
+
res.status(401).json({ error: 'Invalid token' });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Protected route
|
|
632
|
+
app.get('/api/profile', requireAuth, (req, res) => {
|
|
633
|
+
res.json(req.user);
|
|
634
|
+
});</code></pre>
|
|
635
|
+
|
|
636
|
+
<h3>Next.js Server Action</h3>
|
|
637
|
+
<pre><code>'use server';
|
|
638
|
+
|
|
639
|
+
import { SPAPSClient } from 'spaps-sdk';
|
|
640
|
+
import { cookies } from 'next/headers';
|
|
641
|
+
|
|
642
|
+
const spaps = new SPAPSClient();
|
|
643
|
+
|
|
644
|
+
export async function loginAction(email: string, password: string) {
|
|
645
|
+
try {
|
|
646
|
+
const { data } = await spaps.login(email, password);
|
|
647
|
+
|
|
648
|
+
// Store token in cookie
|
|
649
|
+
cookies().set('spaps_token', data.access_token, {
|
|
650
|
+
httpOnly: true,
|
|
651
|
+
secure: process.env.NODE_ENV === 'production',
|
|
652
|
+
sameSite: 'lax',
|
|
653
|
+
maxAge: 60 * 60 * 24 * 7 // 1 week
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
return { success: true, user: data.user };
|
|
657
|
+
} catch (error) {
|
|
658
|
+
return { success: false, error: 'Invalid credentials' };
|
|
659
|
+
}
|
|
660
|
+
}</code></pre>
|
|
661
|
+
</section>
|
|
662
|
+
|
|
663
|
+
<section id="testing">
|
|
664
|
+
<h2>Testing</h2>
|
|
665
|
+
|
|
666
|
+
<h3>Test with cURL</h3>
|
|
667
|
+
<pre><code># Health check
|
|
668
|
+
curl http://localhost:${port}/health
|
|
669
|
+
|
|
670
|
+
# Login
|
|
671
|
+
curl -X POST http://localhost:${port}/api/auth/login \\
|
|
672
|
+
-H "Content-Type: application/json" \\
|
|
673
|
+
-d '{"email":"test@example.com","password":"password"}'
|
|
674
|
+
|
|
675
|
+
# Get user (with token)
|
|
676
|
+
curl http://localhost:${port}/api/auth/user \\
|
|
677
|
+
-H "Authorization: Bearer YOUR_TOKEN"</code></pre>
|
|
678
|
+
|
|
679
|
+
<h3>Test with SDK</h3>
|
|
680
|
+
<pre><code>// test.js
|
|
681
|
+
const { SPAPSClient } = require('spaps-sdk');
|
|
682
|
+
|
|
683
|
+
async function test() {
|
|
684
|
+
const spaps = new SPAPSClient();
|
|
685
|
+
|
|
686
|
+
// Test login
|
|
687
|
+
const { data } = await spaps.login('test@example.com', 'password');
|
|
688
|
+
console.log('✅ Login successful:', data.user);
|
|
689
|
+
|
|
690
|
+
// Test authenticated request
|
|
691
|
+
const user = await spaps.getUser();
|
|
692
|
+
console.log('✅ Got user:', user.data);
|
|
693
|
+
|
|
694
|
+
// Test Stripe
|
|
695
|
+
const session = await spaps.createCheckoutSession(
|
|
696
|
+
'price_123',
|
|
697
|
+
'http://localhost:3000/success'
|
|
698
|
+
);
|
|
699
|
+
console.log('✅ Checkout URL:', session.data.url);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
test().catch(console.error);</code></pre>
|
|
703
|
+
|
|
704
|
+
<div class="alert success">
|
|
705
|
+
<strong>💡 Pro Tip:</strong> In local mode, all endpoints return successful mock responses.
|
|
706
|
+
Use any email/password combination for testing.
|
|
707
|
+
</div>
|
|
708
|
+
</section>
|
|
709
|
+
</div>
|
|
710
|
+
|
|
711
|
+
<div class="footer">
|
|
712
|
+
<p>
|
|
713
|
+
<strong>SPAPS v0.2.6</strong> |
|
|
714
|
+
<a href="https://github.com/yourusername/sweet-potato">GitHub</a> |
|
|
715
|
+
<a href="https://sweetpotato.dev">Website</a> |
|
|
716
|
+
<a href="https://discord.gg/sweetpotato">Discord</a>
|
|
717
|
+
</p>
|
|
718
|
+
<p>Made with 🍠 by the Sweet Potato team</p>
|
|
719
|
+
</div>
|
|
720
|
+
</div>
|
|
721
|
+
|
|
722
|
+
<script>
|
|
723
|
+
function showTab(event, tabId) {
|
|
724
|
+
// Hide all tab contents
|
|
725
|
+
const contents = document.querySelectorAll('.tab-content');
|
|
726
|
+
contents.forEach(content => content.classList.remove('active'));
|
|
727
|
+
|
|
728
|
+
// Remove active from all tabs
|
|
729
|
+
const tabs = document.querySelectorAll('.tab');
|
|
730
|
+
tabs.forEach(tab => tab.classList.remove('active'));
|
|
731
|
+
|
|
732
|
+
// Show selected tab
|
|
733
|
+
document.getElementById(tabId).classList.add('active');
|
|
734
|
+
event.target.classList.add('active');
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Add copy buttons to code blocks
|
|
738
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
739
|
+
const codeBlocks = document.querySelectorAll('pre');
|
|
740
|
+
codeBlocks.forEach(block => {
|
|
741
|
+
const button = document.createElement('button');
|
|
742
|
+
button.className = 'copy-button';
|
|
743
|
+
button.textContent = 'Copy';
|
|
744
|
+
button.onclick = () => {
|
|
745
|
+
const code = block.querySelector('code').textContent;
|
|
746
|
+
navigator.clipboard.writeText(code).then(() => {
|
|
747
|
+
button.textContent = 'Copied!';
|
|
748
|
+
button.classList.add('copied');
|
|
749
|
+
setTimeout(() => {
|
|
750
|
+
button.textContent = 'Copy';
|
|
751
|
+
button.classList.remove('copied');
|
|
752
|
+
}, 2000);
|
|
753
|
+
});
|
|
754
|
+
};
|
|
755
|
+
block.appendChild(button);
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
</script>
|
|
759
|
+
</body>
|
|
760
|
+
</html>`;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
module.exports = { generateDocsHTML };
|
package/src/local-server.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const express = require('express');
|
|
9
9
|
const cors = require('cors');
|
|
10
10
|
const chalk = require('chalk');
|
|
11
|
+
const { generateDocsHTML } = require('./docs-html');
|
|
11
12
|
|
|
12
13
|
class LocalServer {
|
|
13
14
|
constructor(options = {}) {
|
|
@@ -167,100 +168,7 @@ class LocalServer {
|
|
|
167
168
|
|
|
168
169
|
// Documentation endpoint
|
|
169
170
|
this.app.get('/docs', (req, res) => {
|
|
170
|
-
res.send(
|
|
171
|
-
<!DOCTYPE html>
|
|
172
|
-
<html>
|
|
173
|
-
<head>
|
|
174
|
-
<title>SPAPS Local Mode</title>
|
|
175
|
-
<style>
|
|
176
|
-
body {
|
|
177
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
178
|
-
max-width: 800px;
|
|
179
|
-
margin: 50px auto;
|
|
180
|
-
padding: 20px;
|
|
181
|
-
line-height: 1.6;
|
|
182
|
-
}
|
|
183
|
-
h1 { color: #ff6b6b; }
|
|
184
|
-
code {
|
|
185
|
-
background: #f4f4f4;
|
|
186
|
-
padding: 2px 6px;
|
|
187
|
-
border-radius: 3px;
|
|
188
|
-
}
|
|
189
|
-
pre {
|
|
190
|
-
background: #f4f4f4;
|
|
191
|
-
padding: 15px;
|
|
192
|
-
border-radius: 5px;
|
|
193
|
-
overflow-x: auto;
|
|
194
|
-
}
|
|
195
|
-
.endpoint {
|
|
196
|
-
margin: 20px 0;
|
|
197
|
-
padding: 15px;
|
|
198
|
-
border-left: 3px solid #ff6b6b;
|
|
199
|
-
background: #fff9f9;
|
|
200
|
-
}
|
|
201
|
-
</style>
|
|
202
|
-
</head>
|
|
203
|
-
<body>
|
|
204
|
-
<h1>🍠 SPAPS Local Development Mode</h1>
|
|
205
|
-
<p>You're running in <strong>local development mode</strong>. No API keys required!</p>
|
|
206
|
-
|
|
207
|
-
<h2>Quick Start</h2>
|
|
208
|
-
<pre>
|
|
209
|
-
// No configuration needed!
|
|
210
|
-
const response = await fetch('http://localhost:${this.port}/api/auth/login', {
|
|
211
|
-
method: 'POST',
|
|
212
|
-
headers: { 'Content-Type': 'application/json' },
|
|
213
|
-
body: JSON.stringify({
|
|
214
|
-
email: 'test@example.com',
|
|
215
|
-
password: 'password'
|
|
216
|
-
})
|
|
217
|
-
});
|
|
218
|
-
const { access_token, user } = await response.json();
|
|
219
|
-
</pre>
|
|
220
|
-
|
|
221
|
-
<h2>Available Endpoints</h2>
|
|
222
|
-
|
|
223
|
-
<div class="endpoint">
|
|
224
|
-
<strong>GET /health</strong> - Health check
|
|
225
|
-
</div>
|
|
226
|
-
|
|
227
|
-
<div class="endpoint">
|
|
228
|
-
<strong>POST /api/auth/login</strong> - Email/password login
|
|
229
|
-
</div>
|
|
230
|
-
|
|
231
|
-
<div class="endpoint">
|
|
232
|
-
<strong>POST /api/auth/register</strong> - Create account
|
|
233
|
-
</div>
|
|
234
|
-
|
|
235
|
-
<div class="endpoint">
|
|
236
|
-
<strong>POST /api/auth/wallet-sign-in</strong> - Wallet authentication
|
|
237
|
-
</div>
|
|
238
|
-
|
|
239
|
-
<div class="endpoint">
|
|
240
|
-
<strong>GET /api/auth/user</strong> - Get current user
|
|
241
|
-
</div>
|
|
242
|
-
|
|
243
|
-
<div class="endpoint">
|
|
244
|
-
<strong>POST /api/stripe/create-checkout-session</strong> - Create payment session
|
|
245
|
-
</div>
|
|
246
|
-
|
|
247
|
-
<div class="endpoint">
|
|
248
|
-
<strong>GET /api/usage/balance</strong> - Check usage balance
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
<h2>Test Users</h2>
|
|
252
|
-
<p>Switch between test users by adding <code>?_user=admin</code> or <code>?_user=premium</code> to any request.</p>
|
|
253
|
-
|
|
254
|
-
<h2>Environment</h2>
|
|
255
|
-
<pre>
|
|
256
|
-
Mode: ${process.env.NODE_ENV || 'development'}
|
|
257
|
-
Port: ${this.port}
|
|
258
|
-
Auto-Auth: Enabled
|
|
259
|
-
CORS: Enabled (all origins)
|
|
260
|
-
</pre>
|
|
261
|
-
</body>
|
|
262
|
-
</html>
|
|
263
|
-
`);
|
|
171
|
+
res.send(generateDocsHTML(this.port));
|
|
264
172
|
});
|
|
265
173
|
|
|
266
174
|
// Catch-all for unimplemented routes
|