grain.social is a photo sharing platform built on atproto.

better handling of app labelers and running local infra, add sync.sh script for when using local-infra

+57 -55
+2 -2
deno.json
··· 2 "imports": { 3 "$lexicon/": "./__generated__/", 4 "@atproto/syntax": "npm:@atproto/syntax@^0.4.0", 5 - "@bigmoves/bff": "jsr:@bigmoves/bff@0.3.0-beta.37", 6 "@std/http": "jsr:@std/http@^1.0.17", 7 "@std/path": "jsr:@std/path@^1.0.9", 8 "@tailwindcss/cli": "npm:@tailwindcss/cli@^4.1.4", ··· 24 "build:tailwind": "deno run -A --node-modules-dir npm:@tailwindcss/cli -i ./src/input.css -o ./build/styles.css --minify", 25 "build:fonts": "rm -rf ./build/fonts && cp -r ./static/fonts/. ./build/fonts", 26 "dev:build": "DEV=true deno -A --watch=src/static/ jsr:@bigmoves/bff-cli@0.3.0-beta.37 build src/static/mod.ts", 27 - "dev:server": "deno run -A --watch ./src/main.tsx", 28 "dev:tailwind": "deno run -A --node-modules-dir npm:@tailwindcss/cli -i ./src/input.css -o ./build/styles.css --watch", 29 "dev:fonts": "rm -rf ./build/fonts && cp -r ./static/fonts/. ./build/fonts", 30 "sync": "deno run -A --env=.env jsr:@bigmoves/bff-cli@0.3.0-beta.37 sync --collections=social.grain.gallery,social.grain.actor.profile,social.grain.photo,social.grain.favorite,social.grain.gallery.item,social.grain.graph.follow,social.grain.photo.exif --external-collections=app.bsky.actor.profile,app.bsky.graph.follow,sh.tangled.graph.follow,sh.tangled.actor.profile",
··· 2 "imports": { 3 "$lexicon/": "./__generated__/", 4 "@atproto/syntax": "npm:@atproto/syntax@^0.4.0", 5 + "@bigmoves/bff": "jsr:@bigmoves/bff@0.3.0-beta.38", 6 "@std/http": "jsr:@std/http@^1.0.17", 7 "@std/path": "jsr:@std/path@^1.0.9", 8 "@tailwindcss/cli": "npm:@tailwindcss/cli@^4.1.4", ··· 24 "build:tailwind": "deno run -A --node-modules-dir npm:@tailwindcss/cli -i ./src/input.css -o ./build/styles.css --minify", 25 "build:fonts": "rm -rf ./build/fonts && cp -r ./static/fonts/. ./build/fonts", 26 "dev:build": "DEV=true deno -A --watch=src/static/ jsr:@bigmoves/bff-cli@0.3.0-beta.37 build src/static/mod.ts", 27 + "dev:server": "deno run -A --env-file --watch ./src/main.tsx", 28 "dev:tailwind": "deno run -A --node-modules-dir npm:@tailwindcss/cli -i ./src/input.css -o ./build/styles.css --watch", 29 "dev:fonts": "rm -rf ./build/fonts && cp -r ./static/fonts/. ./build/fonts", 30 "sync": "deno run -A --env=.env jsr:@bigmoves/bff-cli@0.3.0-beta.37 sync --collections=social.grain.gallery,social.grain.actor.profile,social.grain.photo,social.grain.favorite,social.grain.gallery.item,social.grain.graph.follow,social.grain.photo.exif --external-collections=app.bsky.actor.profile,app.bsky.graph.follow,sh.tangled.graph.follow,sh.tangled.actor.profile",
+21 -44
deno.lock
··· 2 "version": "5", 3 "specifiers": { 4 "jsr:@bigmoves/atproto-oauth-client@0.2": "0.2.0", 5 - "jsr:@bigmoves/bff@0.3.0-beta.37": "0.3.0-beta.37", 6 "jsr:@deno/gfm@0.10": "0.10.0", 7 "jsr:@denosaurs/emoji@0.3": "0.3.1", 8 "jsr:@luca/esbuild-deno-loader@~0.11.1": "0.11.1", ··· 14 "jsr:@std/cli@^1.0.16": "1.0.20", 15 "jsr:@std/cli@^1.0.20": "1.0.20", 16 "jsr:@std/data-structures@^1.0.6": "1.0.7", 17 "jsr:@std/encoding@^1.0.10": "1.0.10", 18 "jsr:@std/encoding@^1.0.5": "1.0.10", 19 "jsr:@std/fmt@^1.0.8": "1.0.8", ··· 32 "jsr:@std/streams@^1.0.10": "1.0.10", 33 "jsr:@std/testing@^1.0.11": "1.0.11", 34 "npm:@atproto-labs/handle-resolver-node@~0.1.14": "0.1.16", 35 - "npm:@atproto-labs/simple-store@~0.1.2": "0.1.2", 36 - "npm:@atproto/api@~0.15.7": "0.15.14", 37 "npm:@atproto/common@~0.4.10": "0.4.11", 38 "npm:@atproto/identity@~0.4.7": "0.4.8", 39 "npm:@atproto/jwk@0.1.4": "0.1.4", 40 "npm:@atproto/lexicon@*": "0.4.11", 41 "npm:@atproto/lexicon@0.4.11": "0.4.11", 42 "npm:@atproto/lexicon@~0.4.11": "0.4.11", 43 - "npm:@atproto/oauth-client@~0.3.13": "0.3.21", 44 - "npm:@atproto/oauth-types@~0.2.4": "0.2.8", 45 "npm:@atproto/syntax@0.4": "0.4.0", 46 "npm:@atproto/xrpc-server@*": "0.7.19", 47 - "npm:@atproto/xrpc-server@0.7.18": "0.7.18", 48 "npm:@tailwindcss/cli@*": "4.1.9", 49 "npm:@tailwindcss/cli@^4.0.12": "4.1.9", 50 "npm:@tailwindcss/cli@^4.1.3": "4.1.9", ··· 64 "npm:marked-footnote@^1.2.0": "1.2.4_marked@12.0.2", 65 "npm:marked-gfm-heading-id@^3.1.0": "3.2.0_marked@12.0.2", 66 "npm:marked@12": "12.0.2", 67 - "npm:multiformats@*": "13.3.7", 68 "npm:multiformats@^13.3.2": "13.3.7", 69 "npm:popmotion@^11.0.5": "11.0.5", 70 "npm:preact-render-to-string@^6.5.13": "6.5.13_preact@10.26.9", ··· 83 "integrity": "5c3ca124dd52eff51dace83790779ebe48c4b41559b799e16c8750bd415f2124", 84 "dependencies": [ 85 "npm:@atproto-labs/handle-resolver-node", 86 - "npm:@atproto-labs/simple-store", 87 "npm:@atproto/jwk", 88 "npm:@atproto/oauth-client", 89 - "npm:@atproto/oauth-types", 90 "npm:jose" 91 ] 92 }, 93 - "@bigmoves/bff@0.3.0-beta.37": { 94 - "integrity": "0b6203729c667642bfaa7481bf0fdddb55050b77db9dc6d3857f5a96548a6e3f", 95 "dependencies": [ 96 "jsr:@bigmoves/atproto-oauth-client", 97 "jsr:@std/assert@^1.0.13", ··· 102 "npm:@atproto/api", 103 "npm:@atproto/common", 104 "npm:@atproto/identity", 105 - "npm:@atproto/lexicon@0.4.11", 106 "npm:@atproto/lexicon@~0.4.11", 107 "npm:@atproto/oauth-client", 108 "npm:@atproto/syntax", 109 - "npm:@atproto/xrpc-server@0.7.18", 110 "npm:clsx", 111 "npm:multiformats@^13.3.2", 112 "npm:preact", ··· 161 "@std/data-structures@1.0.7": { 162 "integrity": "16932d2c8d281f65eaaa2209af2473209881e33b1ced54cd1b015e7b4cdbb0d2" 163 }, 164 "@std/encoding@1.0.10": { 165 "integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1" 166 }, ··· 230 "dependencies": [ 231 "@atproto-labs/fetch", 232 "@atproto-labs/pipe", 233 - "@atproto-labs/simple-store@0.2.0", 234 "@atproto-labs/simple-store-memory", 235 "@atproto/did", 236 "zod" ··· 262 "@atproto-labs/handle-resolver@0.1.8": { 263 "integrity": "sha512-Y0ckccoCGDo/3g4thPkgp9QcORmc+qqEaCBCYCZYtfLIQp4775u22wd+4fyEyJP4DqoReKacninkICgRGfs3dQ==", 264 "dependencies": [ 265 - "@atproto-labs/simple-store@0.2.0", 266 "@atproto-labs/simple-store-memory", 267 "@atproto/did", 268 "zod" 269 ] 270 }, 271 - "@atproto-labs/identity-resolver@0.1.17": { 272 - "integrity": "sha512-EaH9Lm8M85IKRx+oWZ4tppYRVH8u+MYpEz1kjzYeM3ttZ2xcqKVmYHiOIgd5YPCVV2EIfXKnlM4soHQ+rZ1c6A==", 273 "dependencies": [ 274 "@atproto-labs/did-resolver", 275 "@atproto-labs/handle-resolver", ··· 282 "@atproto-labs/simple-store-memory@0.1.3": { 283 "integrity": "sha512-jkitT9+AtU+0b28DoN92iURLaCt/q/q4yX8q6V+9LSwYlUTqKoj/5NFKvF7x6EBuG+gpUdlcycbH7e60gjOhRQ==", 284 "dependencies": [ 285 - "@atproto-labs/simple-store@0.2.0", 286 "lru-cache" 287 ] 288 - }, 289 - "@atproto-labs/simple-store@0.1.2": { 290 - "integrity": "sha512-9vTNvyPPBs44tKVFht16wGlilW8u4wpEtKwLkWbuNEh3h9TTQ8zjVhEoGZh/v73G4Otr9JUOSIq+/5+8OZD2mQ==" 291 }, 292 "@atproto-labs/simple-store@0.2.0": { 293 "integrity": "sha512-0bRbAlI8Ayh03wRwncAMEAyUKtZ+AuTS1jgPrfym1WVOAOiottI/ZmgccqLl6w5MbxVcClNQF7WYGKvGwGoIhA==" 294 }, 295 - "@atproto/api@0.15.14": { 296 - "integrity": "sha512-FHEMAdscG+r2OFcZUIzPyTDpwzRAyinRsIIaTcuqe0MgZWF4CEGNAKPos0IbecBzMxTOzUHE18dQDKhoXMdgvg==", 297 "dependencies": [ 298 "@atproto/common-web", 299 "@atproto/lexicon", ··· 370 "zod" 371 ] 372 }, 373 - "@atproto/oauth-client@0.3.21": { 374 - "integrity": "sha512-a+YM3aaOAY8/otlAnYmXC0XO+KLOo2cZGHwVvudTSnzwpps8sxhuELdPFl3JN16fS45TUnkYUIaqBgb9MLpx8w==", 375 "dependencies": [ 376 "@atproto-labs/did-resolver", 377 "@atproto-labs/fetch", 378 "@atproto-labs/handle-resolver", 379 "@atproto-labs/identity-resolver", 380 - "@atproto-labs/simple-store@0.2.0", 381 "@atproto-labs/simple-store-memory", 382 "@atproto/did", 383 "@atproto/jwk@0.2.0", ··· 396 }, 397 "@atproto/syntax@0.4.0": { 398 "integrity": "sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA==" 399 - }, 400 - "@atproto/xrpc-server@0.7.18": { 401 - "integrity": "sha512-kjlAsI+UNbbm6AK3Y5Hb4BJ7VQHNKiYYu2kX5vhZJZHO8qfO40GPYYb/2TknZV8IG6fDPBQhUpcDRolI86sgag==", 402 - "dependencies": [ 403 - "@atproto/common", 404 - "@atproto/crypto", 405 - "@atproto/lexicon", 406 - "@atproto/xrpc", 407 - "cbor-x", 408 - "express", 409 - "http-errors", 410 - "mime-types", 411 - "rate-limiter-flexible", 412 - "uint8arrays", 413 - "ws", 414 - "zod" 415 - ] 416 }, 417 "@atproto/xrpc-server@0.7.19": { 418 "integrity": "sha512-YSCl/tU2NDykgDYslFSOYCr96esUgDwncFiADKL59/fyIFPLoT0qY8Uq/budpxUh0qPzjow4HHgVWESOaOpUmA==", ··· 1906 }, 1907 "workspace": { 1908 "dependencies": [ 1909 - "jsr:@bigmoves/bff@0.3.0-beta.37", 1910 "jsr:@std/http@^1.0.17", 1911 "jsr:@std/path@^1.0.9", 1912 "npm:@atproto/syntax@0.4",
··· 2 "version": "5", 3 "specifiers": { 4 "jsr:@bigmoves/atproto-oauth-client@0.2": "0.2.0", 5 + "jsr:@bigmoves/bff@0.3.0-beta.38": "0.3.0-beta.38", 6 "jsr:@deno/gfm@0.10": "0.10.0", 7 "jsr:@denosaurs/emoji@0.3": "0.3.1", 8 "jsr:@luca/esbuild-deno-loader@~0.11.1": "0.11.1", ··· 14 "jsr:@std/cli@^1.0.16": "1.0.20", 15 "jsr:@std/cli@^1.0.20": "1.0.20", 16 "jsr:@std/data-structures@^1.0.6": "1.0.7", 17 + "jsr:@std/dotenv@*": "0.225.5", 18 "jsr:@std/encoding@^1.0.10": "1.0.10", 19 "jsr:@std/encoding@^1.0.5": "1.0.10", 20 "jsr:@std/fmt@^1.0.8": "1.0.8", ··· 33 "jsr:@std/streams@^1.0.10": "1.0.10", 34 "jsr:@std/testing@^1.0.11": "1.0.11", 35 "npm:@atproto-labs/handle-resolver-node@~0.1.14": "0.1.16", 36 + "npm:@atproto/api@~0.15.7": "0.15.15", 37 "npm:@atproto/common@~0.4.10": "0.4.11", 38 "npm:@atproto/identity@~0.4.7": "0.4.8", 39 "npm:@atproto/jwk@0.1.4": "0.1.4", 40 "npm:@atproto/lexicon@*": "0.4.11", 41 "npm:@atproto/lexicon@0.4.11": "0.4.11", 42 "npm:@atproto/lexicon@~0.4.11": "0.4.11", 43 + "npm:@atproto/oauth-client@~0.3.13": "0.3.22", 44 "npm:@atproto/syntax@0.4": "0.4.0", 45 "npm:@atproto/xrpc-server@*": "0.7.19", 46 "npm:@tailwindcss/cli@*": "4.1.9", 47 "npm:@tailwindcss/cli@^4.0.12": "4.1.9", 48 "npm:@tailwindcss/cli@^4.1.3": "4.1.9", ··· 62 "npm:marked-footnote@^1.2.0": "1.2.4_marked@12.0.2", 63 "npm:marked-gfm-heading-id@^3.1.0": "3.2.0_marked@12.0.2", 64 "npm:marked@12": "12.0.2", 65 + "npm:multiformats@*": "9.9.0", 66 "npm:multiformats@^13.3.2": "13.3.7", 67 "npm:popmotion@^11.0.5": "11.0.5", 68 "npm:preact-render-to-string@^6.5.13": "6.5.13_preact@10.26.9", ··· 81 "integrity": "5c3ca124dd52eff51dace83790779ebe48c4b41559b799e16c8750bd415f2124", 82 "dependencies": [ 83 "npm:@atproto-labs/handle-resolver-node", 84 "npm:@atproto/jwk", 85 "npm:@atproto/oauth-client", 86 "npm:jose" 87 ] 88 }, 89 + "@bigmoves/bff@0.3.0-beta.38": { 90 + "integrity": "74b8b77f451a00b16c9a43ce776bdac87753429bf2d15bf6c81b5eda1e2f9e0e", 91 "dependencies": [ 92 "jsr:@bigmoves/atproto-oauth-client", 93 "jsr:@std/assert@^1.0.13", ··· 98 "npm:@atproto/api", 99 "npm:@atproto/common", 100 "npm:@atproto/identity", 101 "npm:@atproto/lexicon@~0.4.11", 102 "npm:@atproto/oauth-client", 103 "npm:@atproto/syntax", 104 "npm:clsx", 105 "npm:multiformats@^13.3.2", 106 "npm:preact", ··· 155 "@std/data-structures@1.0.7": { 156 "integrity": "16932d2c8d281f65eaaa2209af2473209881e33b1ced54cd1b015e7b4cdbb0d2" 157 }, 158 + "@std/dotenv@0.225.5": { 159 + "integrity": "9ce6f9d0ec3311f74a32535aa1b8c62ed88b1ab91b7f0815797d77a6f60c922f" 160 + }, 161 "@std/encoding@1.0.10": { 162 "integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1" 163 }, ··· 227 "dependencies": [ 228 "@atproto-labs/fetch", 229 "@atproto-labs/pipe", 230 + "@atproto-labs/simple-store", 231 "@atproto-labs/simple-store-memory", 232 "@atproto/did", 233 "zod" ··· 259 "@atproto-labs/handle-resolver@0.1.8": { 260 "integrity": "sha512-Y0ckccoCGDo/3g4thPkgp9QcORmc+qqEaCBCYCZYtfLIQp4775u22wd+4fyEyJP4DqoReKacninkICgRGfs3dQ==", 261 "dependencies": [ 262 + "@atproto-labs/simple-store", 263 "@atproto-labs/simple-store-memory", 264 "@atproto/did", 265 "zod" 266 ] 267 }, 268 + "@atproto-labs/identity-resolver@0.1.18": { 269 + "integrity": "sha512-DArYXP1hzZJIBcojun0CWEF+TjAhlGKcVq/RwLiGfY1mKq2yPjCiXyHj+5L0+z9jBSZiAB7L65JgcjI2+MFiRg==", 270 "dependencies": [ 271 "@atproto-labs/did-resolver", 272 "@atproto-labs/handle-resolver", ··· 279 "@atproto-labs/simple-store-memory@0.1.3": { 280 "integrity": "sha512-jkitT9+AtU+0b28DoN92iURLaCt/q/q4yX8q6V+9LSwYlUTqKoj/5NFKvF7x6EBuG+gpUdlcycbH7e60gjOhRQ==", 281 "dependencies": [ 282 + "@atproto-labs/simple-store", 283 "lru-cache" 284 ] 285 }, 286 "@atproto-labs/simple-store@0.2.0": { 287 "integrity": "sha512-0bRbAlI8Ayh03wRwncAMEAyUKtZ+AuTS1jgPrfym1WVOAOiottI/ZmgccqLl6w5MbxVcClNQF7WYGKvGwGoIhA==" 288 }, 289 + "@atproto/api@0.15.15": { 290 + "integrity": "sha512-Wn8jv76pCvffnkNj68w0CGZ3PT4DJGM8DUZnYq9kEW2im6jbRBYI0yYrHNhSiE92A5Ox0HjL2jMhalsI2p9VlQ==", 291 "dependencies": [ 292 "@atproto/common-web", 293 "@atproto/lexicon", ··· 364 "zod" 365 ] 366 }, 367 + "@atproto/oauth-client@0.3.22": { 368 + "integrity": "sha512-IJYkUSGGklV7tQ0S2+5smh8Xmu5MwfxBUNXMtqiooeU2nj+UcNk3/b0nE4MS05JNfwh2BXgHv3P8hrhVG2+RAA==", 369 "dependencies": [ 370 "@atproto-labs/did-resolver", 371 "@atproto-labs/fetch", 372 "@atproto-labs/handle-resolver", 373 "@atproto-labs/identity-resolver", 374 + "@atproto-labs/simple-store", 375 "@atproto-labs/simple-store-memory", 376 "@atproto/did", 377 "@atproto/jwk@0.2.0", ··· 390 }, 391 "@atproto/syntax@0.4.0": { 392 "integrity": "sha512-b9y5ceHS8YKOfP3mdKmwAx5yVj9294UN7FG2XzP6V5aKUdFazEYRnR9m5n5ZQFKa3GNvz7de9guZCJ/sUTcOAA==" 393 }, 394 "@atproto/xrpc-server@0.7.19": { 395 "integrity": "sha512-YSCl/tU2NDykgDYslFSOYCr96esUgDwncFiADKL59/fyIFPLoT0qY8Uq/budpxUh0qPzjow4HHgVWESOaOpUmA==", ··· 1883 }, 1884 "workspace": { 1885 "dependencies": [ 1886 + "jsr:@bigmoves/bff@0.3.0-beta.38", 1887 "jsr:@std/http@^1.0.17", 1888 "jsr:@std/path@^1.0.9", 1889 "npm:@atproto/syntax@0.4",
+6 -1
src/app.tsx
··· 55 <Layout id="layout"> 56 <Layout.Nav 57 heading={ 58 - <h1 class="font-['Jersey_20'] text-4xl text-zinc-900 dark:text-white"> 59 grain 60 <sub class="bottom-[0.75rem] text-[1rem]">beta</sub> 61 </h1>
··· 55 <Layout id="layout"> 56 <Layout.Nav 57 heading={ 58 + <h1 59 + class="text-4xl text-zinc-900 dark:text-white" 60 + style={{ 61 + fontFamily: "'Jersey 20', sans-serif", 62 + }} 63 + > 64 grain 65 <sub class="bottom-[0.75rem] text-[1rem]">beta</sub> 66 </h1>
+1
src/lib/errors.ts
··· 40 }, 41 ); 42 } 43 return errorResponse("Internal Server Error", 500); 44 } 45
··· 40 }, 41 ); 42 } 43 + console.error("Unhandled error:", err); 44 return errorResponse("Internal Server Error", 500); 45 } 46
+13 -2
src/lib/moderation.ts
··· 22 23 for (const label of labels ?? []) { 24 const labelSubject = new AtUri(label.uri).hostname; 25 - const labelerAtpData = await ctx.didResolver.resolveAtprotoData(label.src); 26 // Try labelDefinitions first, then fallback to atprotoLabelValueDefinitions 27 let defs = labelDefinitions[label.src]?.labelValueDefinitions?.filter(( 28 def, ··· 38 return { 39 name: enLocale.name, 40 description: enLocale.description, 41 - labeledBy: labelerAtpData.handle ?? label.src, 42 blurs: defs[0].blurs ?? "", 43 isMe: labelSubject === did, 44 src: label.src,
··· 22 23 for (const label of labels ?? []) { 24 const labelSubject = new AtUri(label.uri).hostname; 25 + let labelerHandle: string | undefined = undefined; 26 + try { 27 + const labelerAtpData = await ctx.didResolver.resolveAtprotoData( 28 + label.src, 29 + ); 30 + labelerHandle = labelerAtpData.handle; 31 + } catch (e) { 32 + console.error( 33 + `Failed to resolve labeler atproto data for ${label.src}: ${e}`, 34 + ); 35 + continue; 36 + } 37 // Try labelDefinitions first, then fallback to atprotoLabelValueDefinitions 38 let defs = labelDefinitions[label.src]?.labelValueDefinitions?.filter(( 39 def, ··· 49 return { 50 name: enLocale.name, 51 description: enLocale.description, 52 + labeledBy: labelerHandle ?? label.src, 53 blurs: defs[0].blurs ?? "", 54 isMe: labelSubject === did, 55 src: label.src,
+2 -6
src/main.tsx
··· 1 import { lexicons } from "$lexicon/lexicons.ts"; 2 - import { bff, BffContext, JETSTREAM, oauth, route } from "@bigmoves/bff"; 3 import { Root } from "./app.tsx"; 4 import { LoginPage } from "./components/LoginPage.tsx"; 5 import { PDS_HOST_URL } from "./env.ts"; ··· 19 import { handler as supportHandler } from "./routes/support.tsx"; 20 import { handler as timelineHandler } from "./routes/timeline.tsx"; 21 import { handler as uploadHandler } from "./routes/upload.tsx"; 22 - import { appStateMiddleware, type State } from "./state.ts"; 23 import { onSignedIn } from "./utils.ts"; 24 25 bff({ ··· 41 "sh.tangled.actor.profile", 42 "sh.tangled.graph.follow", 43 ], 44 - jetstreamUrl: JETSTREAM.WEST_1, 45 lexicons, 46 rootElement: Root, 47 onError, 48 middlewares: [ 49 - (_req, ctx: BffContext<State>) => { 50 - return ctx.next(); 51 - }, 52 appStateMiddleware, 53 oauth({ 54 onSignedIn,
··· 1 import { lexicons } from "$lexicon/lexicons.ts"; 2 + import { bff, oauth, route } from "@bigmoves/bff"; 3 import { Root } from "./app.tsx"; 4 import { LoginPage } from "./components/LoginPage.tsx"; 5 import { PDS_HOST_URL } from "./env.ts"; ··· 19 import { handler as supportHandler } from "./routes/support.tsx"; 20 import { handler as timelineHandler } from "./routes/timeline.tsx"; 21 import { handler as uploadHandler } from "./routes/upload.tsx"; 22 + import { appStateMiddleware } from "./state.ts"; 23 import { onSignedIn } from "./utils.ts"; 24 25 bff({ ··· 41 "sh.tangled.actor.profile", 42 "sh.tangled.graph.follow", 43 ], 44 lexicons, 45 rootElement: Root, 46 onError, 47 middlewares: [ 48 appStateMiddleware, 49 oauth({ 50 onSignedIn,
+12
sync.sh
···
··· 1 + #!/usr/bin/env bash 2 + 3 + # Helpful when running local-infra. Specify the repos you've created on a local pds instance. 4 + 5 + REPOS="did:plc:gdvspmipkels2qp43m4czqhp" 6 + COLLECTIONS="social.grain.gallery,social.grain.actor.profile,social.grain.photo,social.grain.favorite,social.grain.gallery.item,social.grain.graph.follow,social.grain.photo.exif" 7 + EXTERNAL_COLLECTIONS="app.bsky.actor.profile,app.bsky.graph.follow,sh.tangled.graph.follow,sh.tangled.actor.profile" 8 + 9 + deno run -A --env=.env jsr:@bigmoves/bff-cli@0.3.0-beta.37 sync \ 10 + --repos="$REPOS" \ 11 + --collections="$COLLECTIONS" \ 12 + --external-collections="$EXTERNAL_COLLECTIONS"