ππ» νμκ°μ
λ° λ‘κ·ΈμΈκ³Ό κ΄λ ¨λ κΈ°λ₯μ
λλ€.
These are features related to sign-up and login.
ππ» myapp27μ νλ‘μ°,νλ‘μ 리μ€νΈ κΈ°λ₯μ ꡬνν©λλ€.
myapp27 implements features for the following and followers lists.
ππ» νλ‘μ°μ νλ‘μ 리μ€νΈλ νλ‘νμ ν΄λ¦νλ©΄ λ³Ό μ μμ΅λλ€.
You can view the lists of people you follow and your followers by clicking on the profile.
ππ» μ 체 μ½λλ κΉνλΈμμ νμΈ ν μ μμ΅λλ€.
You can find the full code on GitHub.
https://github.com/gideonslife01/flm-nextjs
π μ 체 νλ‘μ νΈ κ΅¬μ‘° / Overall Project Structure
myapp project/
βββ app/ (Next.js App Router)
β βββ .well-known/webfinger/route.ts -> webfinger
β βββ api/follow/route.ts -> Follow API(temporary)
β βββ api/announce/route.ts -> Boost(Announcement)
β βββ api/like/route.ts. -> Like API
β βββ api/posts/route.ts -> Writing API
β βββ api/timeline/route.ts -> Timeline API
β βββ api/v1/instance/route.ts -> auth
β βββ api/v1/apps/route.ts. -> auth
β βββ api/v1/accounts/verify_credentials/route.ts -> auth
β βββ api/v1/statuses/route.ts -> Write Post
β βββ api/v1/timelines/home/route.ts -> timeline
β βββ oauth/authorize/route.ts -> auth
β βββ oauth/token/route.ts -> auth
β βββ api/v1/search/home/route.ts -> search
β βββ api/v2/search/home/route.ts -> search
β βββ api/v1/accounts/[id]/followers/route.ts -> followers
β βββ api/v1/accounts/[id]/following/route.ts -> following
β βββ users/[username]/
β β βββ statuses/[id]/route.ts -> Indivisual Post
β β βββ route.ts -> Acotr Information
β β βββ followers/route.ts -> Followers List
β β βββ following/route.ts -> Following List
β β βββ inbox/route.ts -> Inbox
β β βββ outbox/route.ts -> outbox
β βββ usersui/[username]/
β β βββ page.tsx -> Timeline UI
β β βββ _components/themes/
β β βββ themeex/ThemeexTheme.tsx -> Example Theme
β β βββ pinafore/PinaforeTheme.tsx -> Theme 1
β β βββ mastodon/MastodonTheme.tsx -> Theme 2
β β βββ minimal/MinimaltTheme.tsx -> Theme 3
β βββ layout.tsx, page.tsx, globals.css
β βββ favicon.ico
βββ lib/
β βββ theme.tsx -> Theme Provider
β βββ watchThemes.ts -> Check real-time theme changes
β βββ auth.ts -> Authentication, User Management
β βββ ap.ts -> Follow,Undo,Create,Likes,Announce
β βββ db.ts -> DB connection
βββ data/
β βββ keys/userIDs/ -> private.pem, public.pem(New)
β βββ keys/ -> private.pem, public.pem(legacy)
βββ data.sqlite -> Database(1/3)
βββ data.sqlite-wal -> Database(2/3)
βββ data.sqlite-shm -> Database(3/3)
βββ Caddyfile -> https
βββ instrumentation.ts -> Background Server
βββ package.json
π νλ‘μ νΈ μμ / Project Start
π λΌμ°νΈ μΆκ° λ° μ½λ μμ / Add routes and modify code
βοΈ νλ‘μ / follower
— μ κ· μΆκ°λ λΌμ°νΈ / Newly added routes
— app/api/v1/accounts/[id]/followers/route.ts
/ β
myapp37 - followers API
// /api/v1/accounts/:id/followers
import { NextResponse } from 'next/server';
import db from '@/lib/db';
const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';
function parseActor(actor: string, idx: number) { ... }
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) { ... }
export async function OPTIONS() { ... }
βοΈ νλ‘μ / following
— μ κ· μΆκ°λ λΌμ°νΈ / Newly added routes
— app/api/v1/accounts/[id]/following/route.ts
// β
myapp37 - following API
// /api/v1/accounts/:id/following
import { NextResponse } from 'next/server';
import db from '@/lib/db';
const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';
function parseActor(actor: string, idx: number) { ... }
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) { ... }
export async function OPTIONS() { ... }
βοΈ pinaforeμμ νλ‘μ,νλ‘μ λͺ©λ‘μ urlencodeλΌμ μ½λ μμ
Modified the code because the follower and following lists in Pinafore are URL-encoded.
— app/api/v1/apps/route.ts
import { NextResponse } from 'next/server';
import db from '@/lib/db';
import { randomUUID } from 'crypto';
const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';
export async function POST(req: Request) {
try {
... ...
const contentType = req.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
const body = await req.json();
clientName = body.client_name || 'Unknown';
redirectUris = body.redirect_uris || body.redirect_uri || '';
scopes = body.scopes || 'read write follow';
website = body.website || '';
}else{
// form-urlencoded! Pinafore!κ°! μ΄κ±Έλ‘! 보λ!
//
const text = await req.text();
const params = new URLSearchParams(text);
clientName = params.get('client_name') || 'Unknown';
redirectUris = params.get('redirect_uris') || params.get('redirect_uri') || '';
scopes = params.get('scopes') || 'read write follow';
website = params.get('website') || '';
}
... ...
// β
Pinaforeκ° κΈ°λνλ μλ΅!
// Pinafore expects this response!
return NextResponse.json({
id: clientId, // Pinaforeλ idλ νμΈ / Pinafore also checks id
name: clientName,
website: website,
redirect_uri: redirectUris,
client_id: clientId,
client_secret: clientSecret,
vapid_key: '' // pushμ©, μμ΄λ λ¨ / for push, not required
});
} catch (e) {
console.error('apps μλ¬ / apps error:', e);
//return NextResponse.json({ error: String(e) }, { status: 500 });
// β
myapp37 - μλ¬λλ μΌλ¨ μ±κ³΅μΌλ‘ λ¦¬ν΄ / Return a success status for now, even if an error occurs.
return NextResponse.json({
id: '1',
name: 'Pinafore',
website: null,
redirect_uri: 'https://pinafore.social/settings/instances/add',
client_id: 'pinafore_client_id',
client_secret: 'pinafore_client_secret',
vapid_key: null,
}, {
headers: { 'Access-Control-Allow-Origin': '*' }
});
}
}
βοΈ Notifications
— μ κ· μΆκ°λ λΌμ°νΈ / Newly added routes
— app/api/v1/notifications/route.ts
// β
myapp37 - myapp/api/notifications/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
// μμ§! ꡬν μ ν¨! λΉ λ°°μ΄! / Not yet! Not implemented! Empty array!
return NextResponse.json([], {
headers: { 'Access-Control-Allow-Origin': '*' }
});
}
export async function OPTIONS() {
return new NextResponse(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
}
});
}
π ν μ€νΈ / Test
βοΈ λ‘κ·ΈμΈ ν νλ‘νμμ FOLLOWSμ FOLLOWλ₯Ό ν΄λ¦νλ©΄ 리μ€νΈλ₯Ό λ³Ό μ μμ΅λλ€.
After logging in, you can view the lists by clicking on “FOLLOWS” and “FOLLOW” in your profile.
βοΈ νλ‘μ° / Follows

βοΈ νλ‘μ / Followers

μν볡μ 8μ₯ 32μ / John 8:32
“κ·Έλ¦¬κ³ λν¬λ μ§λ¦¬λ₯Ό μκ² λ κ²μ΄λ©°, μ§λ¦¬κ° λν¬λ₯Ό μμ λ‘κ² ν κ²μ΄λ€.”
“Then you will know the truth ,and the truth will set you free”