[nextjs]SNS Server-28(myapp38)Β 

πŸ‘‰πŸ» νšŒμ›κ°€μž… 및 둜그인과 κ΄€λ ¨λœ κΈ°λŠ₯μž…λ‹ˆλ‹€.
These are features related to sign-up and login.

πŸ‘‰πŸ» myapp38μ—μ„œλŠ” μ•Œλ¦Ό κΈ°λŠ₯을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.
myapp38 implements a notification feature.

πŸ‘‰πŸ» μ•Œλ¦Ό κΈ°λŠ₯은 pinafore.social을 κΈ°μ€€μœΌλ‘œ ν•©λ‹ˆλ‹€.
The notification feature is based on pinafore.social.

πŸ‘‰πŸ» 전체 μ½”λ“œλŠ” κΉƒν—ˆλΈŒμ—μ„œ 확인 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
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


πŸ“ FOLLOWERS,FOLLOWING ν…Œμ΄λΈ” ꡬ쑰 λ³€κ²½
Modification of FOLLOWERS and FOLLOWING table structures

βœ”οΈ ν…Œμ΄λΈ” / Table

— ν…Œμ΄λΈ”μ€ μ•Œλ¦Ό ν™•μΈμ‹œ λ‚ μ§œκ°€ μ €μž₯λ˜μ§€ μ•Šμ•„ 무쑰건 now둜 ν‘œμ‹œλ˜μ–΄ followersν…Œμ΄λΈ” ν•„λ“œμ— λ‚ μ§œμž…λ ₯ μΆ”κ°€
The table always displays ‘now’ because the date is not saved when a notification is checked, so date input was added to the followers table field.

— ν…Œμ΄λΈ” ꡬ쑰λ₯Ό μˆ˜μ •ν•˜λ©΄ ν•„λ“œκ°€ κΈ°μ‘΄ ν•„λ“œλ§ˆμ§€λ§‰μ— μΆ”κ°€λ˜λ―€λ‘œ 관리와 μΌκ΄€μ„±λ¬Έμ œλ‘œ ν…Œμ΄λΈ” μƒˆλ‘œ 생성
Since modifying the table structure adds fields to the end of existing fields, a new table is created due to management and consistency issues.

— ν•œμ€„μ”© 싀행을 μΆ”μ²œν•©λ‹ˆλ‹€. ν•œλ²ˆμ— μ‹€ν–‰ν•˜λ©΄ 데이터 μ‚­μ œ 될 수 μžˆμŠ΅λ‹ˆλ‹€.
It is recommended to execute the commands one line at a time. Executing them all at once could result in data loss.

-- 1. λ°±μ—… / backup 
CREATE TABLE followers_backup AS SELECT * FROM followers;
CREATE TABLE following_backup AS SELECT * FROM following;

SELECT * FROM followers_backup LIMIT 2;
SELECT * FROM following_backup LIMIT 2;

-- 2. κΈ°μ‘΄ ν…Œμ΄λΈ” μ‚­μ œ / Delete existing table
DROP TABLE followers;
DROP TABLE following;

-- 3. λ™μΌν•œ μŠ€ν‚€λ§ˆλ‘œ μƒˆλ‘œ 생성 (ν•„λ“œμˆœμ„œ λ™μΌν•˜κ²Œ 맞좀)
-- Create new with the same schema (matching field order)
CREATE TABLE followers (
    id TEXT PRIMARY KEY,
    actor TEXT NOT NULL,
    inbox TEXT NOT NULL,
    username TEXT NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE following (
    id TEXT PRIMARY KEY,
    actor TEXT NOT NULL,
    inbox TEXT NOT NULL,
    username TEXT NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 4. μΈλ±μŠ€λŠ” κΈ°μ‘΄κ³Ό 동일함 / The index is the same as before.
CREATE INDEX idx_followers_username ON followers(username);
CREATE INDEX idx_followers_actor ON followers(actor);
CREATE INDEX idx_followers_created ON followers(created_at DESC);
CREATE UNIQUE INDEX idx_followers_actor_username ON followers(actor, username);

CREATE INDEX idx_following_username ON following(username);
CREATE INDEX idx_following_actor ON following(actor);
CREATE INDEX idx_following_inbox ON following(inbox);
CREATE INDEX idx_following_created ON following(created_at DESC);
CREATE UNIQUE INDEX idx_following_actor_username ON following(actor, username);

-- 5. 데이터 볡ꡬ (inbox μ—†μœΌλ©΄! actor + /inbox둜! μΆ”μ •!)
-- Data recovery! (If there's no 'inbox', assume it's 'actor' + '/inbox'!)
INSERT INTO followers (id, actor, inbox, username, created_at)
SELECT 
  id,
  actor,
  COALESCE(inbox, actor || '/inbox'),
  username,
  COALESCE(created_at, CURRENT_TIMESTAMP)
FROM followers_backup;

INSERT INTO following (id, actor, inbox, username, created_at)
SELECT 
  id,
  actor,
  COALESCE(inbox, actor || '/inbox'),
  username,
  COALESCE(created_at, CURRENT_TIMESTAMP)
FROM following_backup;

-- 6. 확인 / check
SELECT * FROM followers;
SELECT * FROM following;
.schema followers
.schema following

-- 7. λ°±μ—… ν…Œμ΄λΈ” μ‚­μ œ(확인 ν›„)
-- Delete backup table (after verification)
DROP TABLE followers_backup;
DROP TABLE following_backup;

βœ”οΈ 인덱슀 μΆ”κ°€ / Add Index

— ν…Œμ΄λΈ” κ΅¬μ‘°λ³€κ²½μœΌλ‘œ μΈλ±μŠ€λ„ λ™μΌν•˜κ²Œ λ§žμΆ”κΈ°
Adjusting indexes to match table structure changes

CREATE INDEX IF NOT EXISTS idx_followers_inbox ON followers(inbox);

πŸ“ μ½”λ“œμˆ˜μ • / Code Modification

βœ”οΈ ν…Œμ΄λΈ”μˆ˜μ •μœΌλ‘œ μ½”λ“œ μˆ˜μ •ν•œ λΆ€λΆ„
Parts of the code modified due to table changes

app/api/v1/accounts/[id]/following/route.ts
app/api/v1/accounts/[id]/followers/route.ts
app/users/[username]/inbox/route.ts
lib/db.ts

βœ”οΈ μ•Œλ¦ΌκΈ°λŠ₯ / Notification feature

— app/api/v1/notifications/route.ts

// βœ… myapp38 - app/api/v1/notifications/route.ts 

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) { ... }
function makeAccount(parsed: ReturnType<typeof parseActor>, idx: number, createdAt?: string) { ... }
export async function GET(req: Request) { ...
// 1. Follow μ•Œλ¦Ό / Follow Notification
try { ... } catch(e){ console.error('follow', e); }

// 2. Like μ•Œλ¦Ό / Like Notification
try { ... } catch(e){ console.error('likes notif', e); }

// 3. Reblog μ•Œλ¦Ό / Reblog(boost) Notification
try { ... }catch(e){}
}
... ...

πŸ“ ν…ŒμŠ€νŠΈ / Test

βœ”οΈ μ•Œλ¦Ό 라우트 터미널 ν…ŒμŠ€νŠΈ / Notification Route Terminal Test

curl "https://aloy-horizon.duckdns.org/api/v1/notifications?limit=20"
# 터미널 / Terminal
% curl "https://aloy-horizon.duckdns.org/api/v1/notifications?limit=20"

# 응닡 / Response

[{"id":"follow_0_https://mastodon.social/b7cc4c41-b22a-43b6-9df8-5bb46b2222fd","type":"follow","created_at":"2026-09-10T23:25:07.353Z","account":{"id":"notif_acct_mastodon_social_117193449750714993_0","username":"117193449750714993","acct":"117193449750714993@mastodon.social","display_name":"117193449750714993","avatar":"https://aloy-horizon.duckdns.org/icon.png", ... ... }]

βœ”οΈ νŒ”λ‘œμ›Œ μ•Œλ¦Ό / Followers Notification

βœ”οΈ λΆ€μŠ€νŠΈ μ•Œλ¦Ό / Boost Notification

Boost

βœ”οΈ μ’‹μ•„μš” μ•Œλ¦Ό / Likes Notification

likes

μš”ν•œλ³΅μŒ 8μž₯ 32절 / John 8:32

“그리고 λ„ˆν¬λŠ” 진리λ₯Ό μ•Œκ²Œ 될 것이며, 진리가 λ„ˆν¬λ₯Ό 자유둭게 ν•  것이닀.”

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

Leave a Reply