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.
@@ -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 with pm2 daemon (allows pm2 logs, pm2 status, etc.)
171
- echo "Starting PM2 with ecosystem config..."
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
- # Wait a moment for services to start
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
@@ -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.11",
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