๐๐ป ์ด๋ฒ์๋ @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"
]
}
}

์ํ๋ณต์ 8์ฅ 32์ / John 8:32
“๊ทธ๋ฆฌ๊ณ ๋ํฌ๋ ์ง๋ฆฌ๋ฅผ ์๊ฒ ๋ ๊ฒ์ด๋ฉฐ, ์ง๋ฆฌ๊ฐ ๋ํฌ๋ฅผ ์์ ๋กญ๊ฒ ํ ๊ฒ์ด๋ค.”
“Then you will know the truth ,and the truth will set you free”