wiki-plugin-allyabase 0.0.11 → 0.0.13
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/allyabase_setup.sh +19 -19
- package/client/allyabase.js +3 -0
- package/package.json +3 -2
- package/server/get-paid.js +216 -0
- package/server/server.js +4 -0
package/allyabase_setup.sh
CHANGED
|
@@ -51,22 +51,9 @@ setup_services() {
|
|
|
51
51
|
fi
|
|
52
52
|
|
|
53
53
|
printf '%s\n' "Installing '$service'..."
|
|
54
|
-
npm install "$service/src/server/node"
|
|
54
|
+
npm install --prefix "$buildDir/$service/src/server/node"
|
|
55
55
|
done
|
|
56
56
|
|
|
57
|
-
# Add logging to BDO service to debug request handling
|
|
58
|
-
echo "Adding debug logging to BDO service..."
|
|
59
|
-
|
|
60
|
-
# Find the PUT /user/create endpoint and add logging at the start
|
|
61
|
-
sed -i.bak "/app.put.*user\/create/,/^app\./ {
|
|
62
|
-
/^app\.put/a\\
|
|
63
|
-
console.log('[BDO] Received PUT /user/create request');\
|
|
64
|
-
console.log('[BDO] Headers:', JSON.stringify(req.headers, null, 2));\
|
|
65
|
-
console.log('[BDO] Body:', JSON.stringify(req.body, null, 2));
|
|
66
|
-
}" bdo/src/server/node/bdo.js
|
|
67
|
-
|
|
68
|
-
echo "✓ Added logging to BDO service"
|
|
69
|
-
|
|
70
57
|
} # setup_services
|
|
71
58
|
|
|
72
59
|
setup_ecosystem() {
|
|
@@ -155,6 +142,8 @@ setup_ecosystem() {
|
|
|
155
142
|
" {" \
|
|
156
143
|
" name: '$service'," \
|
|
157
144
|
" script: '$buildDir/$service/src/server/node/${service}.js'," \
|
|
145
|
+
" node_args: '--max-old-space-size=256'," \
|
|
146
|
+
" max_memory_restart: '350M'," \
|
|
158
147
|
" env: $env" \
|
|
159
148
|
" }," >>"$ecosystem_config"
|
|
160
149
|
done
|
|
@@ -167,12 +156,23 @@ main() {
|
|
|
167
156
|
setup_services
|
|
168
157
|
setup_ecosystem
|
|
169
158
|
|
|
170
|
-
# Start
|
|
171
|
-
echo "Starting
|
|
172
|
-
./node_modules/.bin/pm2 start ecosystem.config.js
|
|
159
|
+
# Start BDO first — all other services register with it on boot
|
|
160
|
+
echo "Starting BDO first..."
|
|
161
|
+
./node_modules/.bin/pm2 start ecosystem.config.js --only bdo
|
|
162
|
+
|
|
163
|
+
echo "Waiting for BDO to initialize..."
|
|
164
|
+
sleep 5
|
|
165
|
+
|
|
166
|
+
# Start remaining services one at a time to avoid memory spike on small droplets
|
|
167
|
+
echo "Starting remaining services serially..."
|
|
168
|
+
for service in "${services[@]}"; do
|
|
169
|
+
[[ $service == 'bdo' ]] && continue
|
|
170
|
+
echo " Starting $service..."
|
|
171
|
+
./node_modules/.bin/pm2 start ecosystem.config.js --only "$service"
|
|
172
|
+
sleep 2
|
|
173
|
+
done
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
echo "Waiting for services to initialize..."
|
|
175
|
+
echo "Waiting for services to settle..."
|
|
176
176
|
sleep 5
|
|
177
177
|
|
|
178
178
|
# Show PM2 status
|
package/client/allyabase.js
CHANGED
|
@@ -575,6 +575,9 @@ function emit($item, item) {
|
|
|
575
575
|
|
|
576
576
|
try {
|
|
577
577
|
const response = await post('/plugin/allyabase/launch');
|
|
578
|
+
if (response.status === 401) {
|
|
579
|
+
throw new Error('You must be logged in as the wiki owner to launch a base');
|
|
580
|
+
}
|
|
578
581
|
const result = await response.json();
|
|
579
582
|
|
|
580
583
|
if (result.success) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-plugin-allyabase",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
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
|