wiki-plugin-allyabase 0.0.11 → 0.0.12
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 +3 -2
- package/server/get-paid.js +216 -0
- package/server/server.js +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-plugin-allyabase",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "Allyabase management plugin for the federated wiki",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wiki",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"bdo-js": "^0.0.7",
|
|
28
|
-
"http-proxy": "^1.18.1"
|
|
28
|
+
"http-proxy": "^1.18.1",
|
|
29
|
+
"sessionless-node": "latest"
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get-paid.js
|
|
3
|
+
*
|
|
4
|
+
* Manages the allyabase server's own Addie user so it can receive a
|
|
5
|
+
* commission on purchases that flow through plugins hosted on this wiki.
|
|
6
|
+
*
|
|
7
|
+
* Any plugin that knows the allyabase URL calls:
|
|
8
|
+
* GET /plugin/allyabase/get-paid
|
|
9
|
+
* and receives a payee descriptor { pubKey, addieURL, percent, signature }
|
|
10
|
+
* that it adds to the `payees` array when creating a payment intent.
|
|
11
|
+
* Addie's /verify-payee validates the signature before transferring.
|
|
12
|
+
*
|
|
13
|
+
* Stripe Connect setup (owner only):
|
|
14
|
+
* GET /plugin/allyabase/setup/stripe?token=DEPLOYMENT_TOKEN
|
|
15
|
+
* GET /plugin/allyabase/setup/stripe/done ← Stripe return URL
|
|
16
|
+
*
|
|
17
|
+
* Config stored at ~/.allyabase/addie-keys.json:
|
|
18
|
+
* { uuid, pubKey, privateKey, stripeOnboarded }
|
|
19
|
+
*
|
|
20
|
+
* Wiki startup args:
|
|
21
|
+
* --allyabase_commission N commission percent, 1–9 (default 1)
|
|
22
|
+
* --owner_email EMAIL required for Stripe Connect
|
|
23
|
+
* --deployment_token TOKEN protects the setup/stripe endpoint
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const sessionless = require('sessionless-node');
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
const os = require('os');
|
|
30
|
+
|
|
31
|
+
const KEYS_PATH = path.join(os.homedir(), '.allyabase', 'addie-keys.json');
|
|
32
|
+
const LOCAL_ADDIE_URL = 'http://localhost:3005';
|
|
33
|
+
const DEFAULT_COMMISSION = 1;
|
|
34
|
+
|
|
35
|
+
let cachedKeys = null;
|
|
36
|
+
|
|
37
|
+
function loadKeys() {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(fs.readFileSync(KEYS_PATH, 'utf8'));
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function saveKeysToDisk(keys) {
|
|
46
|
+
const dir = path.dirname(KEYS_PATH);
|
|
47
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
48
|
+
fs.writeFileSync(KEYS_PATH, JSON.stringify(keys, null, 2));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Sign with a specific private key without disturbing the shared sessionless singleton.
|
|
52
|
+
// Safe in Node.js single-threaded model as long as no await occurs between set and sign.
|
|
53
|
+
function signWith(message, privateKey) {
|
|
54
|
+
const saved = sessionless.getKeys;
|
|
55
|
+
sessionless.getKeys = () => ({ privateKey });
|
|
56
|
+
const sig = sessionless.sign(message);
|
|
57
|
+
sessionless.getKeys = saved;
|
|
58
|
+
return sig;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function wikiOrigin(req) {
|
|
62
|
+
const proto = req.headers['x-forwarded-proto'] || req.protocol;
|
|
63
|
+
return `${proto}://${req.get('host')}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function ensureAddieUser() {
|
|
67
|
+
cachedKeys = loadKeys();
|
|
68
|
+
if (cachedKeys && cachedKeys.uuid) return cachedKeys;
|
|
69
|
+
|
|
70
|
+
let generated = {};
|
|
71
|
+
await sessionless.generateKeys(
|
|
72
|
+
(k) => { generated = k; },
|
|
73
|
+
() => generated
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const timestamp = Date.now().toString();
|
|
77
|
+
const signature = signWith(timestamp + generated.pubKey, generated.privateKey);
|
|
78
|
+
|
|
79
|
+
const resp = await fetch(`${LOCAL_ADDIE_URL}/user/create`, {
|
|
80
|
+
method: 'PUT',
|
|
81
|
+
headers: { 'Content-Type': 'application/json' },
|
|
82
|
+
body: JSON.stringify({ timestamp, pubKey: generated.pubKey, signature })
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (!resp.ok) throw new Error(`Addie user create failed: ${resp.status}`);
|
|
86
|
+
|
|
87
|
+
const addieUser = await resp.json();
|
|
88
|
+
if (addieUser.error) throw new Error(`Addie: ${addieUser.error}`);
|
|
89
|
+
|
|
90
|
+
const keys = {
|
|
91
|
+
uuid: addieUser.uuid,
|
|
92
|
+
pubKey: generated.pubKey,
|
|
93
|
+
privateKey: generated.privateKey,
|
|
94
|
+
stripeOnboarded: false
|
|
95
|
+
};
|
|
96
|
+
saveKeysToDisk(keys);
|
|
97
|
+
cachedKeys = keys;
|
|
98
|
+
return keys;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function addRoutes(params) {
|
|
102
|
+
const app = params.app;
|
|
103
|
+
const argv = params.argv;
|
|
104
|
+
|
|
105
|
+
const commission = Math.min(9, Math.max(1, parseInt(argv.allyabase_commission || DEFAULT_COMMISSION, 10)));
|
|
106
|
+
const DEPLOYMENT_TOKEN = argv.deployment_token || process.env.DEPLOYMENT_TOKEN;
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await ensureAddieUser();
|
|
110
|
+
console.log(`[wiki-plugin-allyabase] 💰 Get-paid user ready (${commission}% commission, Stripe ${cachedKeys.stripeOnboarded ? 'connected' : 'not yet connected'})`);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error('[wiki-plugin-allyabase] ❌ Failed to create get-paid Addie user:', err.message);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── GET /plugin/allyabase/get-paid ──────────────────────────────────────────
|
|
116
|
+
// Returns a payee descriptor for this allyabase host.
|
|
117
|
+
// Only returns a descriptor when Stripe Connect is complete — otherwise the
|
|
118
|
+
// commission would be taken but never land anywhere.
|
|
119
|
+
app.get('/plugin/allyabase/get-paid', function(req, res) {
|
|
120
|
+
if (!cachedKeys || !cachedKeys.uuid) {
|
|
121
|
+
return res.status(503).json({ error: 'Get-paid not ready' });
|
|
122
|
+
}
|
|
123
|
+
if (!cachedKeys.stripeOnboarded) {
|
|
124
|
+
// 204 = no content: caller should skip adding this payee
|
|
125
|
+
return res.status(204).end();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const addieURL = `${wikiOrigin(req)}/plugin/allyabase/addie`;
|
|
129
|
+
const signature = signWith(cachedKeys.pubKey + addieURL + commission, cachedKeys.privateKey);
|
|
130
|
+
|
|
131
|
+
res.json({ pubKey: cachedKeys.pubKey, addieURL, percent: commission, signature });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ── GET /plugin/allyabase/setup/stripe ──────────────────────────────────────
|
|
135
|
+
// Initiates Stripe Connect Express onboarding for the allyabase host.
|
|
136
|
+
// Protected by deployment token — open in a browser with ?token=YOUR_TOKEN.
|
|
137
|
+
app.get('/plugin/allyabase/setup/stripe', async function(req, res) {
|
|
138
|
+
const token = req.query.token;
|
|
139
|
+
if (!DEPLOYMENT_TOKEN || token !== DEPLOYMENT_TOKEN) {
|
|
140
|
+
return res.status(403).send('<h1>Forbidden</h1><p>Provide your deployment token as ?token=TOKEN</p>');
|
|
141
|
+
}
|
|
142
|
+
if (!cachedKeys || !cachedKeys.uuid) {
|
|
143
|
+
return res.status(503).send('<h1>Not ready</h1><p>Addie user not initialized yet — wait a moment and retry.</p>');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const email = argv.owner_email || process.env.OWNER_EMAIL;
|
|
147
|
+
if (!email) {
|
|
148
|
+
return res.status(400).send(
|
|
149
|
+
'<h1>Owner email required</h1>' +
|
|
150
|
+
'<p>Start the wiki with <code>--owner_email YOUR@EMAIL.COM</code> to enable Stripe Connect.</p>'
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const origin = wikiOrigin(req);
|
|
156
|
+
const refreshUrl = `${origin}/plugin/allyabase/setup/stripe?token=${encodeURIComponent(token)}`;
|
|
157
|
+
const returnUrl = `${origin}/plugin/allyabase/setup/stripe/done?token=${encodeURIComponent(token)}`;
|
|
158
|
+
|
|
159
|
+
const timestamp = Date.now().toString();
|
|
160
|
+
const signature = signWith(timestamp + cachedKeys.uuid + email, cachedKeys.privateKey);
|
|
161
|
+
|
|
162
|
+
const resp = await fetch(`${LOCAL_ADDIE_URL}/user/${cachedKeys.uuid}/processor/stripe/express`, {
|
|
163
|
+
method: 'PUT',
|
|
164
|
+
headers: { 'Content-Type': 'application/json' },
|
|
165
|
+
body: JSON.stringify({ timestamp, country: 'US', email, refreshUrl, returnUrl, signature })
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const result = await resp.json();
|
|
169
|
+
if (result.error) {
|
|
170
|
+
return res.status(500).send(`<h1>Stripe setup error</h1><p>${result.error}</p>`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (result.alreadyConnected) {
|
|
174
|
+
cachedKeys.stripeOnboarded = true;
|
|
175
|
+
saveKeysToDisk(cachedKeys);
|
|
176
|
+
return res.send(`<!doctype html><html><head><title>Stripe Already Connected</title>
|
|
177
|
+
<style>body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;max-width:560px;margin:80px auto;text-align:center;color:#1d1d1f;}</style></head>
|
|
178
|
+
<body><h1>✅ Stripe already connected</h1>
|
|
179
|
+
<p>Your allyabase Stripe account is already set up.</p>
|
|
180
|
+
<a href="javascript:window.close()">Close this tab</a></body></html>`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const onboardingUrl = result.stripeOnboardingUrl;
|
|
184
|
+
if (!onboardingUrl) return res.status(500).send('<h1>No onboarding URL returned from Addie</h1>');
|
|
185
|
+
|
|
186
|
+
res.redirect(onboardingUrl);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
console.error('[wiki-plugin-allyabase] Stripe setup error:', err);
|
|
189
|
+
res.status(500).send(`<h1>Setup failed</h1><p>${err.message}</p>`);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ── GET /plugin/allyabase/setup/stripe/done ─────────────────────────────────
|
|
194
|
+
// Stripe redirects here after onboarding completes.
|
|
195
|
+
app.get('/plugin/allyabase/setup/stripe/done', function(req, res) {
|
|
196
|
+
const token = req.query.token;
|
|
197
|
+
if (!DEPLOYMENT_TOKEN || token !== DEPLOYMENT_TOKEN) {
|
|
198
|
+
return res.status(403).send('<h1>Forbidden</h1>');
|
|
199
|
+
}
|
|
200
|
+
if (cachedKeys) {
|
|
201
|
+
cachedKeys.stripeOnboarded = true;
|
|
202
|
+
saveKeysToDisk(cachedKeys);
|
|
203
|
+
}
|
|
204
|
+
console.log('[wiki-plugin-allyabase] ✅ Stripe Connect onboarding complete');
|
|
205
|
+
res.send(`<!doctype html><html><head><title>Stripe Setup Complete</title>
|
|
206
|
+
<style>body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;max-width:560px;margin:80px auto;text-align:center;color:#1d1d1f;}
|
|
207
|
+
h1{font-size:28px;margin-bottom:12px;} p{font-size:15px;color:#555;line-height:1.6;}
|
|
208
|
+
code{display:block;background:#f4f4f4;padding:10px 16px;border-radius:8px;font-size:13px;margin:16px auto;}</style></head>
|
|
209
|
+
<body><h1>✅ Allyabase payouts enabled</h1>
|
|
210
|
+
<p>Your allyabase server is now connected to Stripe. You'll receive a ${commission}% commission on all purchases flowing through this base.</p>
|
|
211
|
+
<p>Any plugin that calls <code>${LOCAL_ADDIE_URL.replace('localhost', 'your-wiki-host')}/plugin/allyabase/get-paid</code> will automatically include you as a payee.</p>
|
|
212
|
+
<a href="javascript:window.close()">Close this tab</a></body></html>`);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
module.exports = { addRoutes };
|
package/server/server.js
CHANGED
|
@@ -6,6 +6,7 @@ const httpProxy = require('http-proxy');
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const federationResolver = require('./federation-resolver');
|
|
8
8
|
const deployment = require('./deployment');
|
|
9
|
+
const getPaid = require('./get-paid');
|
|
9
10
|
const bdoModule = require('bdo-js');
|
|
10
11
|
const bdo = bdoModule.default || bdoModule;
|
|
11
12
|
|
|
@@ -1198,6 +1199,9 @@ async function startServer(params) {
|
|
|
1198
1199
|
// Add deployment routes
|
|
1199
1200
|
deployment.addRoutes(params);
|
|
1200
1201
|
|
|
1202
|
+
// Add get-paid routes (Stripe Connect commission for agora purchases)
|
|
1203
|
+
await getPaid.addRoutes(params);
|
|
1204
|
+
|
|
1201
1205
|
console.log('✅ wiki-plugin-allyabase ready!');
|
|
1202
1206
|
|
|
1203
1207
|
// Set up shutdown hooks to clean up allyabase/PM2 processes
|