bring back yahoo pipes!
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: init

+232 -11
+9
.env.example
··· 1 + ORIGIN=https://pipes.yourdomain.com 2 + PORT=3000 3 + NODE_ENV=production 4 + DATABASE_URL=data/pipes.db 5 + 6 + # Indiko OAuth Configuration 7 + INDIKO_CLIENT_ID=ikc_xxxxxxxxxxxxxxxxxxxxx 8 + INDIKO_CLIENT_SECRET=iks_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 9 + INDIKO_ORIGIN=https://indiko.dunkirk.sh
+12
.gitignore
··· 1 + node_modules/ 2 + .wrangler/ 3 + dist/ 4 + .dev.vars 5 + .env 6 + bun.lockb 7 + data/ 8 + *.db 9 + *.db-shm 10 + *.db-wal 11 + 12 + .DS_Store
+20 -11
README.md
··· 1 - # Indiko 1 + # Pipes 2 + 3 + This is my interperitation of yahoo pipes from back in the day! It is designed to allow you to string together pipelines of data and do cool stuff! 2 4 3 5 The canonical repo for this is hosted on tangled over at [`dunkirk.sh/pipes`](https://tangled.org/@dunkirk.sh/pipes) 4 6 ··· 7 9 1. Clone the repository: 8 10 9 11 ```bash 10 - git clone https://github.com/taciturnaxolotl/indiko.git 11 - cd indiko 12 + git clone https://github.com/taciturnaxolotl/pipes.git 13 + cd pipes 12 14 ``` 13 15 14 16 2. Install dependencies: ··· 26 28 Configure the following environment variables: 27 29 28 30 ```env 29 - ORIGIN=https://your-indiko-domain.com 30 - RP_ID=your-indiko-domain.com 31 + ORIGIN=https://pipes.yourdomain.com 31 32 PORT=3000 32 33 NODE_ENV=production 34 + DATABASE_URL=data/pipes.db 35 + 36 + # Indiko OAuth Configuration 37 + INDIKO_CLIENT_ID=ikc_xxxxxxxxxxxxxxxxxxxxx 38 + INDIKO_CLIENT_SECRET=iks_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 39 + INDIKO_ORIGIN=https://indiko.dunkirk.sh 40 + INDIKO_REDIRECT_URI=https://pipes.yourdomain.com/auth/callback 33 41 ``` 34 42 35 - - `ORIGIN` - Full URL where Indiko is hosted (must match RP_ID) 36 - - `RP_ID` - Domain for WebAuthn (no protocol, matches ORIGIN domain) 37 - - `PORT` - Port to run the server on 38 - - `NODE_ENV` - Environment (dev/production) 43 + The database will be automatically created at `./data/pipes.db` on first run. 39 44 40 - The database will be automatically created at `./indiko.db` on first run. 45 + 4. Set up Indiko OAuth: 46 + - Go to your Indiko instance 47 + - Navigate to Admin → OAuth Clients 48 + - Create a new client with the redirect URI matching your `INDIKO_REDIRECT_URI` 49 + - Copy the Client ID and Secret to your `.env` file 41 50 42 - 4. Start the server: 51 + 5. Start the server: 43 52 44 53 ```bash 45 54 # Development (with hot reload)
+40
bun.lock
··· 1 + { 2 + "lockfileVersion": 1, 3 + "configVersion": 1, 4 + "workspaces": { 5 + "": { 6 + "name": "pipes", 7 + "dependencies": { 8 + "bun-sqlite-migrations": "^1.0.2", 9 + "kysely": "^0.28.9", 10 + "kysely-bun-sqlite": "^0.4.0", 11 + "nanoid": "^5.1.6", 12 + }, 13 + "devDependencies": { 14 + "@types/bun": "latest", 15 + }, 16 + "peerDependencies": { 17 + "typescript": "^5", 18 + }, 19 + }, 20 + }, 21 + "packages": { 22 + "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], 23 + 24 + "@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="], 25 + 26 + "bun-sqlite-migrations": ["bun-sqlite-migrations@1.0.2", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-WLw8q67KM+1RN7o4DqVVhmJASypuBp8fygrfA8QD5HZEjiP+E5hD1SV2dpyB7A4tFqLdUF8cdln7+Ptj5+Hz1Q=="], 27 + 28 + "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], 29 + 30 + "kysely": ["kysely@0.28.9", "", {}, "sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA=="], 31 + 32 + "kysely-bun-sqlite": ["kysely-bun-sqlite@0.4.0", "", { "dependencies": { "bun-types": "^1.1.31" }, "peerDependencies": { "kysely": "^0.28.2" } }, "sha512-2EkQE5sT4ewiw7IWfJsAkpxJ/QPVKXKO5sRYI/xjjJIJlECuOdtG+ssYM0twZJySrdrmuildNPFYVreyu1EdZg=="], 33 + 34 + "nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], 35 + 36 + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 37 + 38 + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 39 + } 40 + }
+13
crush.json
··· 1 + { 2 + "$schema": "https://charm.land/crush.json", 3 + "lsp": { 4 + "biome": { 5 + "command": "bunx", 6 + "args": ["biome", "lsp-proxy"] 7 + }, 8 + "typescript": { 9 + "command": "bunx", 10 + "args": ["typescript-language-server", "--stdio"] 11 + } 12 + } 13 + }
+23
package.json
··· 1 + { 2 + "name": "pipes", 3 + "module": "index.ts", 4 + "type": "module", 5 + "private": true, 6 + "scripts": { 7 + "dev": "bun run --hot src/index.ts", 8 + "start": "bun run src/index.ts", 9 + "format": "bun run --bun biome check --write ." 10 + }, 11 + "devDependencies": { 12 + "@types/bun": "latest" 13 + }, 14 + "peerDependencies": { 15 + "typescript": "^5" 16 + }, 17 + "dependencies": { 18 + "bun-sqlite-migrations": "^1.0.2", 19 + "kysely": "^0.28.9", 20 + "kysely-bun-sqlite": "^0.4.0", 21 + "nanoid": "^5.1.6" 22 + } 23 + }
+6
public/favicon.svg
··· 1 + <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 + <rect width="512" height="512" fill="#CADAEA"/> 3 + <rect x="246" y="96" width="266" height="319" fill="#FF9606"/> 4 + <rect x="175" y="70" width="81" height="371" fill="#FF9606"/> 5 + <rect x="256" y="96" width="15" height="319" fill="#EF900D"/> 6 + </svg>
+54
src/index.ts
··· 1 + import { env } from "bun"; 2 + import indexHTML from "./pages/index.html"; 3 + 4 + (() => { 5 + const required = ["ORIGIN"]; 6 + 7 + const missing = required.filter((key) => !process.env[key]); 8 + 9 + if (missing.length > 0) { 10 + console.warn( 11 + `[Startup] Missing required environment variables: ${missing.join(", ")}`, 12 + ); 13 + process.exit(1); 14 + } 15 + 16 + // Validate ORIGIN is HTTPS in production 17 + const origin = process.env.ORIGIN as string; 18 + const nodeEnv = process.env.NODE_ENV || "development"; 19 + 20 + if (nodeEnv === "production" && !origin.startsWith("https://")) { 21 + console.error( 22 + `[Startup] ORIGIN must use HTTPS in production (got: ${origin})`, 23 + ); 24 + process.exit(1); 25 + } 26 + 27 + console.log(`[Startup] Environment validated (${nodeEnv} mode)`); 28 + })(); 29 + 30 + const server = Bun.serve({ 31 + port: env.PORT ? Number.parseInt(env.PORT, 10) : 3000, 32 + routes: { 33 + "/": indexHTML, 34 + }, 35 + development: process.env.NODE_ENV !== "production", 36 + }); 37 + 38 + console.log(`Pipes running on ${env.ORIGIN}`) 39 + 40 + let is_shutting_down = false; 41 + function shutdown(sig: string) { 42 + if (is_shutting_down) return; 43 + is_shutting_down = true; 44 + 45 + console.log(`[Shutdown] triggering shutdown due to ${sig}`); 46 + 47 + server.stop(); 48 + console.log("[Shutdown] stopped server"); 49 + 50 + process.exit(0); 51 + } 52 + 53 + process.on("SIGTERM", () => shutdown("SIGTERM")); 54 + process.on("SIGINT", () => shutdown("SIGINT"));
+15
src/pages/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + 4 + <head> 5 + <meta charset="UTF-8"> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 + <title>Pipes</title> 8 + <link rel="icon" type="image/svg+xml" href="../../public/favicon.svg"> 9 + </head> 10 + 11 + <body> 12 + <h1>Pipes</h1> 13 + </body> 14 + 15 + </html>
+7
src/types/env.d.ts
··· 1 + declare module "bun" { 2 + interface Env { 3 + ORIGIN: string; 4 + NODE_ENV?: "dev" | "production"; 5 + PORT?: string; 6 + } 7 + }
+33
tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + // Environment setup & latest features 4 + "lib": ["ESNext", "DOM", "DOM.Iterable"], 5 + "target": "ESNext", 6 + "module": "Preserve", 7 + "moduleDetection": "force", 8 + "jsx": "preserve", 9 + "allowJs": true, 10 + 11 + // Bundler mode 12 + "moduleResolution": "bundler", 13 + "allowImportingTsExtensions": true, 14 + "verbatimModuleSyntax": true, 15 + "noEmit": true, 16 + 17 + // Decorators 18 + "experimentalDecorators": true, 19 + "useDefineForClassFields": false, 20 + 21 + // Best practices 22 + "strict": true, 23 + "skipLibCheck": true, 24 + "noFallthroughCasesInSwitch": true, 25 + "noUncheckedIndexedAccess": true, 26 + "noImplicitOverride": true, 27 + 28 + // Some stricter flags (disabled by default) 29 + "noUnusedLocals": false, 30 + "noUnusedParameters": false, 31 + "noPropertyAccessFromIndexSignature": false 32 + } 33 + }