this repo has no description
atproto bluesky typescript express

3rd party PDS login

+56 -38
-3
.gitignore
··· 1 1 node_modules/* 2 2 dist/* 3 3 *.env 4 - *.db 5 - *.lock 6 - package-lock.json 7 4 docker-compose.yml
+2 -1
src/global.d.ts
··· 2 2 3 3 declare module "express-session" { 4 4 interface SessionData { 5 - handle?: string; 5 + atp?: any; 6 6 accessJwt?: string; 7 7 refreshJwt?: string; 8 8 pds?: string; 9 + handle?: string; 9 10 } 10 11 }
+27 -1
src/index.ts
··· 1 1 import path from "path"; 2 2 import { fileURLToPath } from "url"; 3 - import express, { Express } from "express"; 3 + import express, { Express, Request, Response, NextFunction } from "express"; 4 + import { AtpAgent, AtpSessionEvent, AtpSessionData } from "@atproto/api"; 4 5 import { engine } from "express-handlebars"; 5 6 import router from "./routes/main.js"; 6 7 import mobile from "./routes/mobile.js"; ··· 69 70 genid: () => crypto.randomUUID(), 70 71 }), 71 72 ); 73 + 74 + app.use((req: Request, res: Response, next: NextFunction) => { 75 + const agent = new AtpAgent({ 76 + service: req.session.pds || "https://bsky.social", 77 + persistSession: (e: AtpSessionEvent, s?: AtpSessionData) => { 78 + if (s) { 79 + req.session.atp = s; 80 + } else { 81 + delete req.session.atp; 82 + } 83 + } 84 + }); 85 + 86 + if (req.session.atp) { 87 + try { 88 + agent.resumeSession(req.session.atp); 89 + } catch (error) { 90 + console.error(error); 91 + delete req.session.atp; 92 + } 93 + } 94 + 95 + (req as any).agent = agent; 96 + next(); 97 + }); 72 98 73 99 app.use(express.static("public")); 74 100 app.use(express.json());
+5 -10
src/routes/account.ts
··· 1 1 import { Router, Request, Response } from "express"; 2 - import { agent } from "../agent.js"; 3 2 import { getActor, getActorDid } from "../lib/actor.js"; 4 3 import { getTheme, putTheme } from "../lib/theme.js"; 5 4 import multer from "multer"; 6 - import auth from "../lib/auth.js"; 7 5 8 6 const account = Router(); 9 7 const upload = multer({ storage: multer.memoryStorage() }); ··· 17 15 }); 18 16 19 17 account.get("/design", async (req: Request, res: Response) => { 20 - const curuser = await getActorDid(agent, req.cookies.handle); 18 + const agent = (req as any).agent; 19 + const curuser = await getActorDid(agent, (req as any).session.handle); 21 20 const existingusertheme = await getTheme(agent, curuser); 22 21 res.render("account/design", { 23 22 layout: null, ··· 25 24 }); 26 25 }); 27 26 28 - account.post( 29 - "/design", 30 - auth, 31 - upload.single("background"), 32 - async (req: Request, res: Response) => { 33 - const { bg_color, text_color, link_color, side_color, side_border } = 34 - req.body; 27 + account.post("/design", upload.single("background"), async (req: Request, res: Response) => { 28 + const { bg_color, text_color, link_color, side_color, side_border } = req.body; 29 + const agent = (req as any).agent; 35 30 const did = await getActorDid(agent, req.cookies.handle); 36 31 const theme = { 37 32 bg_color: bg_color,
+10 -5
src/routes/main.ts
··· 32 32 }); 33 33 34 34 router.get("/login", (req: Request, res: Response) => { 35 - if (!req.cookies.handle) { 35 + if (!req.session.handle) { 36 36 res.render("login", { 37 37 layout: "main", 38 38 title: "Bluesky", ··· 44 44 }); 45 45 46 46 router.get("/home", auth, async (req: Request, res: Response) => { 47 + const agent = (req as any).agent; 47 48 const cursor = (req.query.cursor as string) || ""; 48 - const handle = req.cookies.handle; 49 + const handle = req.session.handle as string; 49 50 const did = await getActorDid(agent, handle); 50 51 const actor = await getActor(agent, did); 51 52 const feed = await getTimeline(agent, cursor); ··· 230 231 231 232 router.post("/status", auth, async (req: Request, res: Response) => { 232 233 const { mobile_text_content, layout } = req.body; 234 + const agent = (req as any).agent; 233 235 234 236 const rt = new RichText({ 235 237 text: mobile_text_content, ··· 264 266 265 267 router.get("/like", auth, async (req: Request, res: Response) => { 266 268 const { postUri, cid } = req.params; 269 + const agent = (req as any).agent; 267 270 268 271 try { 269 272 await agent.like(postUri, cid); ··· 276 279 277 280 router.post("/follow", auth, async (req: Request, res: Response) => { 278 281 const { user } = req.body; 282 + const agent = (req as any).agent; 279 283 const targetDid = await getActorDid(agent, user); 280 284 281 285 try { ··· 289 293 290 294 router.post("/unfollow", auth, async (req: Request, res: Response) => { 291 295 const { user } = req.body; 296 + const agent = (req as any).agent; 292 297 const targetDid = await getActorDid(agent, user); 293 298 294 299 try { ··· 302 307 303 308 router.post("/sessions", async (req: Request, res: Response) => { 304 309 const { username_or_email, password, layout, pds_url } = req.body; 310 + req.session.pds = pds_url; 311 + const agent = (req as any).agent; 305 312 306 313 try { 307 314 await agent.login({ ··· 309 316 password: password, 310 317 }); 311 318 312 - res.cookie("handle", agent.session?.handle, { httpOnly: true }); 313 - res.cookie("accessJwt", agent.session?.accessJwt, { httpOnly: true }); 314 - res.cookie("refreshJwt", agent.session?.refreshJwt, { httpOnly: true }); 319 + req.session.handle = username_or_email; 315 320 316 321 if (layout === "mobile") { 317 322 res.redirect("/m/home");
+12 -18
src/routes/mobile.ts
··· 1 1 import { Router, Request, Response } from "express"; 2 - import { agent, pubagent } from "../agent.js"; 2 + import { pubagent } from "../agent.js"; 3 3 import { 4 4 getActor, 5 5 getActorDid, ··· 9 9 } from "../lib/actor.js"; 10 10 import { getFeed, getFeedData } from "../lib/feed.js"; 11 11 import { getTimeline } from "../lib/misc.js"; 12 - import auth from "../lib/auth.js"; 13 12 14 13 const mobile = Router(); 15 14 ··· 18 17 }); 19 18 20 19 mobile.get("/login", (req: Request, res: Response) => { 21 - if (!req.cookies.handle) { 20 + if (!req.session.handle) { 22 21 res.render("mobile/login", { 23 22 layout: "mobile", 24 23 title: "Bluesky", ··· 29 28 } 30 29 }); 31 30 32 - mobile.get("/home", auth, async (req: Request, res: Response) => { 31 + mobile.get("/home", async (req: Request, res: Response) => { 32 + const agent = (req as any).agent; 33 33 const cursor = (req.query.cursor as string) || ""; 34 34 const feed = await getTimeline(agent, cursor); 35 35 ··· 38 38 title: "Bluesky", 39 39 feed: feed.feed, 40 40 cursor: feed.cursor, 41 - curuser: req.cookies.handle, 41 + curuser: req.session.handle, 42 42 year: new Date().getFullYear(), 43 43 }); 44 44 }); ··· 55 55 actor: actor, 56 56 feed: feed.feed, 57 57 cursor: feed.cursor, 58 - curuser: req.cookies.handle, 58 + curuser: req.session.handle, 59 59 year: new Date().getFullYear(), 60 60 }); 61 61 }); 62 62 63 - mobile.get( 64 - "/profile/:handle/following", 65 - async (req: Request, res: Response) => { 63 + mobile.get("/profile/:handle/following", async (req: Request, res: Response) => { 66 64 const did = await getActorDid(pubagent, req.params.handle); 67 65 const actor = await getActor(pubagent, did); 68 66 const follows = await getActorFollows(pubagent, did); ··· 72 70 title: "Bluesky / " + actor.handle, 73 71 actor: actor, 74 72 follows: follows, 75 - curuser: req.cookies.handle, 73 + curuser: req.session.handle, 76 74 year: new Date().getFullYear(), 77 75 }); 78 76 }, 79 77 ); 80 78 81 - mobile.get( 82 - "/profile/:handle/followers", 83 - async (req: Request, res: Response) => { 79 + mobile.get("/profile/:handle/followers", async (req: Request, res: Response) => { 84 80 const did = await getActorDid(pubagent, req.params.handle); 85 81 const actor = await getActor(pubagent, did); 86 82 const followers = await getActorFollowers(pubagent, did); ··· 90 86 title: "Bluesky / " + actor.handle, 91 87 actor: actor, 92 88 followers: followers, 93 - curuser: req.cookies.handle, 89 + curuser: req.session.handle, 94 90 year: new Date().getFullYear(), 95 91 }); 96 92 }, 97 93 ); 98 94 99 - mobile.get( 100 - "/profile/:handle/feed/:record", 101 - async (req: Request, res: Response) => { 95 + mobile.get("/profile/:handle/feed/:record", async (req: Request, res: Response) => { 102 96 const cursor = (req.query.cursor as string) || ""; 103 97 const did = await getActorDid(pubagent, req.params.handle); 104 98 const feed = await getFeed(pubagent, did, req.params.record, cursor); ··· 110 104 feed: feed.feed, 111 105 cursor: feed.cursor, 112 106 feedData: feedData, 113 - curuser: req.cookies.handle, 107 + curuser: req.session.handle, 114 108 year: new Date().getFullYear(), 115 109 }); 116 110 },