[nextjs]SNS Server-5(myapp15)

๐Ÿ‘‰๐Ÿป ์ด๋ฒˆ์—๋Š” @user1@yourhost.domain.org์˜ ํŒ”๋กœ์›Œ ๋ชฉ๋ก์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
Next, create a list of followers for @user1@yourhost.domain.org.

๐Ÿ‘‰๐Ÿป pinafore.socialํ”„๋กœํ•„์˜ FOLLOWERS 1์„ ํด๋ฆญํ• ๊ฒฝ์šฐ ๋ชฉ๋ก์— ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
This feature displays a list when you click “FOLLOWERS 1” on the pinafore.social profile.

๐Ÿ‘‰๐Ÿป gotosocial์€ ์ •์ฑ…์ƒ remote ๊ณ„์ •(๋‹ค๋ฅธ ์„œ๋ฒ„ ๊ณ„์ •์˜ ํŒ”๋กœ์›Œ) ์ •๋ณด๋Š” ๋ณด์—ฌ์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
Due to its policy, GoToSocial does not display information about remote accounts (followers from other servers).

๐Ÿ‘‰๐Ÿป ๊ทธ๋ž˜์„œ ๋ผ์šฐํ„ฐ๋ฅผ ๋งŒ๋“ค๊ณ  API ํ™•์ธ๋งŒ ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
So, I plan to create the router and just verify the API.

๐Ÿ“ ํ”„๋กœ์ ํŠธ ์„ค์น˜,๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜,HTTPS์„ค์ •
Project setup, library installation, HTTPS configuration

โœ”๏ธ ์•„๋ž˜์˜ ์ด์ „ ํฌ์ŠคํŠธ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”
Please refer to the previous post below.

๐Ÿ“ ์ „์ฒด ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ / Overall Project Structure

myapp15/  
โ”œโ”€โ”€ app/  (Next.js App Router)
โ”‚   โ”œโ”€โ”€ .well-known/webfinger/route.ts     -> webfinger
โ”‚   โ”œโ”€โ”€ api/posts/route.ts     -> ๊ธ€ ์“ฐ๊ธฐ API / Writing API
โ”‚   โ”œโ”€โ”€ api/follow/route.ts    -> ํŒ”๋กœ์šฐ API(์ž„์‹œ) / Follow API(temporary)
โ”‚   โ”œโ”€โ”€ users/[username]/
โ”‚   โ”‚   โ”œโ”€โ”€ route.ts           -> Actor์ •๋ณด / Acotr Information
โ”‚   โ”‚   โ”œโ”€โ”€ followers/route.ts -> Followers List
โ”‚   โ”‚   โ”œโ”€โ”€ inbox/route.ts     -> Inbox
โ”‚   โ”‚   โ””โ”€โ”€ outbox/route.ts    -> outbox
โ”‚   โ”œโ”€โ”€ layout.tsx, page.tsx, globals.css
โ”‚   โ””โ”€โ”€ favicon.ico
โ”œโ”€โ”€ lib/
โ”‚   โ”œโ”€โ”€ ap.ts                  -> Follow Accept
โ”‚   โ””โ”€โ”€ db.ts                  -> DB connection
โ”œโ”€โ”€ data/
โ”‚   โ””โ”€โ”€ keys/                  -> private.pem, public.pem
โ”œโ”€โ”€ data.sqlite                -> Database
โ”œโ”€โ”€ Caddyfile                  -> https 
โ””โ”€โ”€ package.json

๐Ÿ“ followers ๋ผ์šฐํ„ฐ ๋งŒ๋“ค๊ธฐ
Creating a followers router

โœ”๏ธ followers๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐํ•˜๊ธฐ ์œ„ํ•œ ๋ชฉ๋ก์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
Create a list to display followers.

โœ”๏ธ myapp15/app/users/[username]/followers/route.ts

// myapp14/app/api/follow/route.ts
import { sendFollow, signedFetch } from '@/lib/ap';

//const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';

// myapp14  โœ… - Follow ๋ณด๋‚ด๊ธฐ API / Send Follow API
// POST /api/follow
export async function POST(req: Request) {
  try {
    const { username = 'user1', target } = await req.json();
    if (!target) return Response.json({ error: 'target ํ•„์š”' }, { status: 400 });

    console.log(`โžก๏ธ ํŒ”๋กœ์šฐ ์‹œ๋„ / Follow attempt: ${username} -> ${target}`);

    // 1. ์ƒ๋Œ€๋ฐฉ inbox ์ฐพ๊ธฐ - ์„œ๋ช…๋œ GET์œผ๋กœ! / Find the inbox with a signed GET request
    const actorRes = await signedFetch(target, username);
    
    if (!actorRes.ok) {
      const t = await actorRes.text();
      return Response.json({ error: `์ƒ๋Œ€๋ฐฉ ์กฐํšŒ ์‹คํŒจ / Failed to look up the other party. ${actorRes.status}`, body: t }, { status: 400 });
    }

    const actor = await actorRes.json();
    const inbox = actor.inbox;
    console.log(`๐Ÿ“ฌ inbox ์ฐพ์Œ / Found inbox: ${inbox}`);

    // 2. Follow ์ „์†ก / Send Follow
    const result = await sendFollow(inbox, target, username);

    return Response.json({ 
      ok: result.ok, 
      inbox, 
      target,
      result: result.text,
      follow: result.followDoc
    });

  } catch (e: any) {
    console.error('follow ์—๋Ÿฌ / Follow error:', e);
    return Response.json({ error: e.message }, { status: 500 });
  }
}

๐Ÿ“ ์ฝ”๋“œ ์ˆ˜์ • / Code modification

โœ”๏ธ pinafore์—์„œ ํŒ”๋กœ์šฐ ์‹ ์ฒญ์„ ํ•˜๋ฉด aloy-horizon.duckdns.org๋กœ ํŒ”๋กœ์šฐ ์š”์ฒญ์ด ์˜ต๋‹ˆ๋‹ค.
When you send a follow request from Pinafore, the request is sent to aloy-horizon.duckdns.org.

โœ”๏ธ ์ด๋–„ ํŒ”๋กœ์šฐ ์š”์ฒญํ•œ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„์„ aloy-horizon.duckdns.org์— ์ €์žฅํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
At this point, you need to save the name of the user who sent the follow request to aloy-horizon.duckdns.org.

โœ”๏ธ ์ด์ „์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— username์ปฌ๋Ÿผ์ด ์—†๋‹ค๋ฉด ์ถ”๊ฐ€ ํ•ฉ๋‹ˆ๋‹ค.
If the username column does not already exist in the database, add it.

ALTER TABLE followers ADD COLUMN username TEXT;

โœ”๏ธ username์— ์‚ฌ์šฉ์ž ์•„์ด๋””๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๋ถ€๋ถ„์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.
Modify the code for the section where the user ID is entered into ‘username’.

— myapp15/app/users/[username]/followers/route.ts

if (body.type === 'Undo') {
  db.prepare('DELETE FROM followers WHERE actor = ? AND username = ?').run(unfollowActorId, username);
}

if (body.type === 'Follow') {
  db.prepare('INSERT OR IGNORE INTO followers (id, actor, inbox, username) VALUES (?,?,?,?)').run(body.id, actorId, inboxUrl, username);
}

๐Ÿ“ ํŒ”๋กœ์›Œ ๋ฐ์ดํ„ฐ ํ™•์ธํ•˜๊ธฐ
Check follower data

โœ”๏ธ pinafore.social์—์„œ ์ƒˆ๋กœ ํŒ”๋กœ์šฐ๋ฅผ ํ•ด๋ด…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ API์— ์ •์ƒ์ ์œผ๋กœ ์œ ์ €๊ฐ€ ํ™•์ธ ๊ฐ€๋Šฅํ•œ์ง€ ์ฒดํฌํ•ด๋ด…๋‹ˆ๋‹ค.
Iโ€™m trying out a new follow on pinafore.social, so Iโ€™m checking to see if the user is correctly recognized by the database and API.

โœ”๏ธ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์ €์žฅํ™•์ธํ•˜๊ธฐ
Verify database storage

sqlite> select * from followers;
https://freelifemakers.com/users/user1/follow/01S310S4A222J6FN8QDQXJR2221|https://freelifemakers.com/users/user1|https://freelifemakers.com/users/user1/inbox|user1
sqlite> 

โœ”๏ธ API ์ถœ๋ ฅํ™•์ธํ•˜๊ธฐ
Check API Output

— https://aloy-horizon.duckdns.org/users/user1/followers

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://aloy-horizon.duckdns.org/users/user1/followers",
  "type": "OrderedCollection",
  "totalItems": 1,
  "orderedItems": [
    "https://freelifemakers.com/users/user1"
  ],
  "first": {
    "id": "https://aloy-horizon.duckdns.org/users/user1/followers?page=1",
    "type": "OrderedCollectionPage",
    "totalItems": 1,
    "orderedItems": [
      "https://freelifemakers.com/users/user1"
    ]
  }
}
API

์š”ํ•œ๋ณต์Œ 8์žฅ 32์ ˆ / John 8:32

“๊ทธ๋ฆฌ๊ณ  ๋„ˆํฌ๋Š” ์ง„๋ฆฌ๋ฅผ ์•Œ๊ฒŒ ๋  ๊ฒƒ์ด๋ฉฐ, ์ง„๋ฆฌ๊ฐ€ ๋„ˆํฌ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ํ•  ๊ฒƒ์ด๋‹ค.”

“Then you will know the truth ,and the truth will set you free”

Leave a Reply