wiki-plugin-linkitylink 0.0.1

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/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # wiki-plugin-linkitylink
2
+
3
+ Federated Wiki plugin that integrates Linkitylink - a privacy-first link page service.
4
+
5
+ ## What This Plugin Does
6
+
7
+ This plugin integrates Linkitylink with Federated Wiki by:
8
+ - **Launching a dedicated linkitylink instance** for each wiki
9
+ - **Auto-configuring** that instance with the wiki's base URLs
10
+ - **Proxying requests** between wiki and linkitylink
11
+ - **Managing the lifecycle** of the linkitylink service
12
+
13
+ Each wiki gets its own linkitylink instance, enabling true forking and independent operation.
14
+
15
+ ## Installation
16
+
17
+ ### 1. Install the plugin
18
+
19
+ ```bash
20
+ # In your wiki's node_modules directory
21
+ cd /path/to/wiki/node_modules
22
+ git clone https://github.com/planet-nine-app/wiki-plugin-linkitylink.git
23
+ cd wiki-plugin-linkitylink
24
+ npm install
25
+ ```
26
+
27
+ ### 2. Install linkitylink
28
+
29
+ ```bash
30
+ # Clone linkitylink where the plugin can find it
31
+ cd /path/to/planet-nine
32
+ git clone https://github.com/planet-nine-app/linkitylink.git
33
+ cd linkitylink
34
+ npm install
35
+ ```
36
+
37
+ ### 3. Use in your wiki
38
+
39
+ Create a page with a linkitylink item to trigger plugin loading. The plugin will automatically:
40
+ - Launch its own linkitylink instance
41
+ - Configure it with your wiki's base URLs
42
+ - Start proxying requests
43
+
44
+ ```json
45
+ {
46
+ "type": "linkitylink",
47
+ "id": "unique-id",
48
+ "text": "My Link Page"
49
+ }
50
+ ```
51
+
52
+ ## Routes
53
+
54
+ All routes are proxied from the wiki to the Linkitylink service:
55
+
56
+ - `/plugin/linkitylink/` → Linkitylink homepage
57
+ - `/plugin/linkitylink/create` → Create link page
58
+ - `/plugin/linkitylink/view/:emojicode` → View link page by emojicode
59
+ - `/plugin/linkitylink/t/:alphanumeric` → View link page by alphanumeric ID
60
+
61
+ ## Configuration
62
+
63
+ ### Environment Variables
64
+
65
+ **Required for multiple wikis on same machine:**
66
+
67
+ ```bash
68
+ # Wiki 1
69
+ export LINKITYLINK_PORT=3010
70
+ export LINKITYLINK_PATH=/path/to/linkitylink
71
+ wiki --port 3000
72
+
73
+ # Wiki 2 (different terminal)
74
+ export LINKITYLINK_PORT=3011
75
+ export LINKITYLINK_PATH=/path/to/linkitylink-copy
76
+ wiki --port 3001
77
+ ```
78
+
79
+ **Variables:**
80
+ - `LINKITYLINK_PORT` - Port for this wiki's linkitylink instance (default: 3010)
81
+ - `LINKITYLINK_PATH` - Path to linkitylink installation (default: `../../linkitylink` relative to plugin)
82
+
83
+ ### Base URLs (owner.json)
84
+
85
+ The plugin automatically configures linkitylink with base URLs from `~/.wiki/status/owner.json`:
86
+
87
+ ```json
88
+ {
89
+ "fountURL": "http://localhost:3006",
90
+ "bdoURL": "http://localhost:3003",
91
+ "addieURL": "http://localhost:3005"
92
+ }
93
+ ```
94
+
95
+ **For wiki federation, each wiki's owner.json should point to its own base:**
96
+
97
+ ```json
98
+ // Wiki A's owner.json
99
+ {
100
+ "fountURL": "http://base-a.example.com/plugin/allyabase/fount",
101
+ "bdoURL": "http://base-a.example.com/plugin/allyabase/bdo",
102
+ "addieURL": "http://base-a.example.com/plugin/allyabase/addie"
103
+ }
104
+
105
+ // Wiki B's owner.json
106
+ {
107
+ "fountURL": "http://base-b.example.com/plugin/allyabase/fount",
108
+ "bdoURL": "http://base-b.example.com/plugin/allyabase/bdo",
109
+ "addieURL": "http://base-b.example.com/plugin/allyabase/addie"
110
+ }
111
+ ```
112
+
113
+ ## How It Works
114
+
115
+ ### Plugin Startup Flow
116
+
117
+ 1. **Plugin loads** when wiki page uses linkitylink type
118
+ 2. **Reads configuration** from `~/.wiki/status/owner.json`
119
+ 3. **Checks for running linkitylink** on configured port
120
+ 4. **If not running:**
121
+ - Spawns linkitylink as child process
122
+ - Sets environment variables (PORT, FOUNT_BASE_URL, BDO_BASE_URL, ADDIE_BASE_URL)
123
+ - Waits for startup (3 seconds)
124
+ 5. **If already running:**
125
+ - Sends configuration update via `POST /config`
126
+ 6. **Creates proxy** for all `/plugin/linkitylink/*` routes
127
+
128
+ ### Request Flow
129
+
130
+ 1. User visits `http://your-wiki.com/plugin/linkitylink/view/🔗💎...`
131
+ 2. Plugin strips `/plugin/linkitylink` prefix
132
+ 3. Proxies to `http://localhost:{LINKITYLINK_PORT}/view/🔗💎...`
133
+ 4. Linkitylink fetches data from configured base URLs
134
+ 5. Returns beautiful SVG page to wiki
135
+
136
+ ### Architecture Benefits
137
+
138
+ - **Independent Instances** - Each wiki runs its own linkitylink
139
+ - **Isolated Bases** - Wiki A connects to Base A, Wiki B connects to Base B
140
+ - **True Forking** - Fork a wiki page, get independent linkitylink data
141
+ - **Process Management** - Plugin manages linkitylink lifecycle
142
+ - **Automatic Configuration** - No manual setup required
143
+
144
+ ## Dependencies
145
+
146
+ - `http-proxy`: ^1.18.1 - For proxying requests to Linkitylink service
147
+ - `node-fetch`: ^2.6.1 - For configuring linkitylink via HTTP
148
+
149
+ ## License
150
+
151
+ MIT
package/factory.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "Linkitylink",
3
+ "title": "Privacy-First Link Pages",
4
+ "category": "planet-nine"
5
+ }
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ server: require('./server/server.js')
3
+ };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "wiki-plugin-linkitylink",
3
+ "version": "0.0.1",
4
+ "description": "Linkitylink integration plugin for federated wiki",
5
+ "keywords": [
6
+ "wiki",
7
+ "federated",
8
+ "linkitylink",
9
+ "planet-nine"
10
+ ],
11
+ "homepage": "https://github.com/planet-nine-app/wiki-plugin-linkitylink#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/planet-nine-app/wiki-plugin-linkitylink/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/planet-nine-app/wiki-plugin-linkitylink.git"
18
+ },
19
+ "license": "MIT",
20
+ "author": "planetnineisaspaceship",
21
+ "type": "commonjs",
22
+ "main": "index.js",
23
+ "scripts": {
24
+ "test": "echo \"Error: no test specified\" && exit 1"
25
+ },
26
+ "dependencies": {
27
+ "http-proxy": "^1.18.1",
28
+ "node-fetch": "^2.6.1"
29
+ }
30
+ }
@@ -0,0 +1,242 @@
1
+ (function() {
2
+ const http = require('http');
3
+ const httpProxy = require('http-proxy');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const fetch = require('node-fetch');
7
+ const { spawn } = require('child_process');
8
+
9
+ // Linkitylink configuration
10
+ const LINKITYLINK_PORT = process.env.LINKITYLINK_PORT || 3010;
11
+ const LINKITYLINK_PATH = process.env.LINKITYLINK_PATH || path.join(__dirname, '../../../linkitylink');
12
+
13
+ let linkitylinkProcess = null;
14
+
15
+ // Function to load wiki's owner.json for base configuration
16
+ function loadWikiConfig() {
17
+ try {
18
+ const ownerPath = path.join(process.env.HOME || '/root', '.wiki/status/owner.json');
19
+ if (fs.existsSync(ownerPath)) {
20
+ const ownerData = JSON.parse(fs.readFileSync(ownerPath, 'utf8'));
21
+
22
+ // Extract base URLs from owner.json
23
+ // Default to localhost if not specified
24
+ return {
25
+ fountURL: ownerData.fountURL || 'http://localhost:3006',
26
+ bdoURL: ownerData.bdoURL || 'http://localhost:3003',
27
+ addieURL: ownerData.addieURL || 'http://localhost:3005'
28
+ };
29
+ }
30
+ console.warn('[wiki-plugin-linkitylink] No owner.json found, using localhost defaults');
31
+ return {
32
+ fountURL: 'http://localhost:3006',
33
+ bdoURL: 'http://localhost:3003',
34
+ addieURL: 'http://localhost:3005'
35
+ };
36
+ } catch (err) {
37
+ console.error('[wiki-plugin-linkitylink] Error loading wiki config:', err);
38
+ return {
39
+ fountURL: 'http://localhost:3006',
40
+ bdoURL: 'http://localhost:3003',
41
+ addieURL: 'http://localhost:3005'
42
+ };
43
+ }
44
+ }
45
+
46
+ // Function to check if linkitylink is running
47
+ async function checkLinkitylinkRunning() {
48
+ try {
49
+ const response = await fetch(`http://localhost:${LINKITYLINK_PORT}/config`, {
50
+ method: 'GET',
51
+ timeout: 2000
52
+ });
53
+ return response.ok;
54
+ } catch (err) {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ // Function to launch linkitylink service
60
+ async function launchLinkitylink(wikiConfig) {
61
+ return new Promise((resolve, reject) => {
62
+ console.log('[wiki-plugin-linkitylink] 🚀 Launching linkitylink service...');
63
+ console.log(`[wiki-plugin-linkitylink] Path: ${LINKITYLINK_PATH}`);
64
+ console.log(`[wiki-plugin-linkitylink] Port: ${LINKITYLINK_PORT}`);
65
+
66
+ // Check if linkitylink directory exists
67
+ if (!fs.existsSync(LINKITYLINK_PATH)) {
68
+ console.error(`[wiki-plugin-linkitylink] ❌ Linkitylink not found at ${LINKITYLINK_PATH}`);
69
+ console.error('[wiki-plugin-linkitylink] Set LINKITYLINK_PATH environment variable to the correct location');
70
+ return reject(new Error('Linkitylink directory not found'));
71
+ }
72
+
73
+ // Check for server.js
74
+ const serverPath = path.join(LINKITYLINK_PATH, 'server.js');
75
+ if (!fs.existsSync(serverPath)) {
76
+ console.error(`[wiki-plugin-linkitylink] ❌ server.js not found at ${serverPath}`);
77
+ return reject(new Error('Linkitylink server.js not found'));
78
+ }
79
+
80
+ // Set environment variables for this linkitylink instance
81
+ const env = {
82
+ ...process.env,
83
+ PORT: LINKITYLINK_PORT.toString(),
84
+ FOUNT_BASE_URL: wikiConfig.fountURL,
85
+ BDO_BASE_URL: wikiConfig.bdoURL,
86
+ ADDIE_BASE_URL: wikiConfig.addieURL
87
+ };
88
+
89
+ // Spawn linkitylink process
90
+ linkitylinkProcess = spawn('node', ['server.js'], {
91
+ cwd: LINKITYLINK_PATH,
92
+ env: env,
93
+ stdio: ['ignore', 'pipe', 'pipe']
94
+ });
95
+
96
+ // Log stdout
97
+ linkitylinkProcess.stdout.on('data', (data) => {
98
+ const lines = data.toString().split('\n').filter(line => line.trim());
99
+ lines.forEach(line => {
100
+ console.log(`[linkitylink:${LINKITYLINK_PORT}] ${line}`);
101
+ });
102
+ });
103
+
104
+ // Log stderr
105
+ linkitylinkProcess.stderr.on('data', (data) => {
106
+ console.error(`[linkitylink:${LINKITYLINK_PORT}] ERROR: ${data.toString().trim()}`);
107
+ });
108
+
109
+ // Handle process exit
110
+ linkitylinkProcess.on('exit', (code, signal) => {
111
+ console.log(`[wiki-plugin-linkitylink] Linkitylink process exited (code: ${code}, signal: ${signal})`);
112
+ linkitylinkProcess = null;
113
+ });
114
+
115
+ linkitylinkProcess.on('error', (err) => {
116
+ console.error('[wiki-plugin-linkitylink] ❌ Failed to start linkitylink:', err);
117
+ reject(err);
118
+ });
119
+
120
+ // Wait a bit for the service to start
121
+ setTimeout(async () => {
122
+ const isRunning = await checkLinkitylinkRunning();
123
+ if (isRunning) {
124
+ console.log('[wiki-plugin-linkitylink] ✅ Linkitylink service started successfully');
125
+ resolve();
126
+ } else {
127
+ console.error('[wiki-plugin-linkitylink] ⚠️ Linkitylink may not have started correctly');
128
+ resolve(); // Don't reject, let it try to work anyway
129
+ }
130
+ }, 3000);
131
+ });
132
+ }
133
+
134
+ // Function to configure linkitylink with base URLs
135
+ async function configureLinkitylink(config) {
136
+ try {
137
+ const response = await fetch(`http://localhost:${LINKITYLINK_PORT}/config`, {
138
+ method: 'POST',
139
+ headers: {
140
+ 'Content-Type': 'application/json'
141
+ },
142
+ body: JSON.stringify(config)
143
+ });
144
+
145
+ const result = await response.json();
146
+ if (result.success) {
147
+ console.log('[wiki-plugin-linkitylink] ✅ Linkitylink configured with base URLs:');
148
+ console.log(` Fount: ${result.config.fountURL}`);
149
+ console.log(` BDO: ${result.config.bdoURL}`);
150
+ console.log(` Addie: ${result.config.addieURL}`);
151
+ } else {
152
+ console.error('[wiki-plugin-linkitylink] ❌ Failed to configure linkitylink:', result.error);
153
+ }
154
+ } catch (err) {
155
+ console.error('[wiki-plugin-linkitylink] ❌ Error configuring linkitylink:', err.message);
156
+ }
157
+ }
158
+
159
+ async function startServer(params) {
160
+ const app = params.app;
161
+
162
+ console.log('🔗 wiki-plugin-linkitylink starting...');
163
+ console.log(`📍 Linkitylink service on port ${LINKITYLINK_PORT}`);
164
+
165
+ // Load wiki configuration
166
+ const wikiConfig = loadWikiConfig();
167
+
168
+ // Check if linkitylink is already running
169
+ const isRunning = await checkLinkitylinkRunning();
170
+
171
+ if (!isRunning) {
172
+ console.log('[wiki-plugin-linkitylink] Linkitylink not detected, attempting to launch...');
173
+ try {
174
+ await launchLinkitylink(wikiConfig);
175
+ } catch (err) {
176
+ console.error('[wiki-plugin-linkitylink] ❌ Failed to launch linkitylink:', err.message);
177
+ console.error('[wiki-plugin-linkitylink] You may need to start linkitylink manually');
178
+ }
179
+ } else {
180
+ console.log('[wiki-plugin-linkitylink] ✅ Linkitylink already running, configuring...');
181
+ await configureLinkitylink(wikiConfig);
182
+ }
183
+
184
+ // Create proxy server
185
+ const proxy = httpProxy.createProxyServer({});
186
+
187
+ // Handle proxy errors
188
+ proxy.on('error', function(err, req, res) {
189
+ console.error('[LINKITYLINK PROXY ERROR]', err.message);
190
+
191
+ // Return JSON error response
192
+ res.writeHead(503, { 'Content-Type': 'application/json' });
193
+ res.end(JSON.stringify({
194
+ error: 'Linkitylink service not available',
195
+ message: err.message,
196
+ hint: 'Is linkitylink running on port ' + LINKITYLINK_PORT + '?'
197
+ }));
198
+ });
199
+
200
+ // Log proxy requests
201
+ proxy.on('proxyReq', function(proxyReq, req, res, options) {
202
+ console.log(`[LINKITYLINK PROXY] ${req.method} ${req.url} -> http://localhost:${LINKITYLINK_PORT}${req.url}`);
203
+
204
+ // If body was parsed by Express, restream it
205
+ if (req.body && Object.keys(req.body).length > 0) {
206
+ let bodyData = JSON.stringify(req.body);
207
+ proxyReq.setHeader('Content-Type', 'application/json');
208
+ proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
209
+ proxyReq.write(bodyData);
210
+ proxyReq.end();
211
+ }
212
+ });
213
+
214
+ // Proxy all linkitylink routes
215
+ // Maps /plugin/linkitylink/* -> http://localhost:3010/*
216
+ app.all('/plugin/linkitylink/*', function(req, res) {
217
+ // Remove /plugin/linkitylink prefix
218
+ const targetPath = req.url.replace('/plugin/linkitylink', '');
219
+ req.url = targetPath;
220
+
221
+ proxy.web(req, res, {
222
+ target: `http://localhost:${LINKITYLINK_PORT}`,
223
+ changeOrigin: true
224
+ });
225
+ });
226
+
227
+ // Also handle root plugin path -> linkitylink root
228
+ app.all('/plugin/linkitylink', function(req, res) {
229
+ req.url = '/';
230
+ proxy.web(req, res, {
231
+ target: `http://localhost:${LINKITYLINK_PORT}`,
232
+ changeOrigin: true
233
+ });
234
+ });
235
+
236
+ console.log('✅ wiki-plugin-linkitylink ready!');
237
+ console.log('📍 Routes:');
238
+ console.log(' /plugin/linkitylink/* -> http://localhost:' + LINKITYLINK_PORT + '/*');
239
+ }
240
+
241
+ module.exports = { startServer };
242
+ }).call(this);