👉🏻 회원가입 및 로그인과 관련된 기능입니다.
These are features related to sign-up and login.
👉🏻 pinafore화면에서 표시 것들을 구현 중입니다.
I am currently implementing the elements displayed on the pinafore screen.
👉🏻 홈타임라인에서 좋아요와 부스트를 표시합니다.
Displays likes and boosts on the home timeline.
👉🏻 전체 코드는 깃허브에서 확인 할 수 있습니다.
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
│ ├── 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
📁 코드 수정 / Code Modification
✔️ pinafore 홈 타임라인에 내가 팔로우 하는 사람 모든 글 뜨게 하기
Make posts from everyone I follow appear on my Pinafore home timeline.
— app/api/v1/timelines/home/route.ts
import { NextResponse } from 'next/server';
import db from '@/lib/db';
const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';
function parseDate(v: any): number {
if (!v) return 0;
if (/^\d{13}$/.test(String(v))) return parseInt(v);
const d = new Date(v).getTime();
return isNaN(d)? 0 : d;
}
export async function GET(req: Request) { ...
// ✅ myapp36-전체 검색 / Full search
const reblogs_count = announcesByObject.get(longId) || announcesByObject.get(originalId) || announcesByObject.get(shortId) || announcesByObject.get(short8) || 0;
const favourites_count = likesByObject.get(longId) || likesByObject.get(originalId) || likesByObject.get(shortId) || likesByObject.get(short8) || 0;
const favourited = likedSet.has(longId) || likedSet.has(originalId) || likedSet.has(shortId) || likedSet.has(short8);
const reblogged = reblogs_count > 0;
let content = row.content || '';
try { if (content.startsWith('{')) { const o = JSON.parse(content); content = o.object?.content || o.content || content; } } catch {}
const baseAccount = isMy
? { id: '1', username, acct: `${username}@${DOMAIN}`, display_name: username, avatar: `https://${DOMAIN}/icon.png` }
: (() => {
let uname = 'user1', dom = 'freelifemakers.com', display = 'user1';
if (row.actor?.includes('mastodon.social')) { uname = 'freelifemakers'; dom = 'mastodon.social'; display = 'freelifemakers'; }
return { id: `remote_${dom.replace(/\./g,'_')}_${uname}`, username: uname, acct: `${uname}@${dom}`, display_name: display, avatar: `https://${DOMAIN}/icon.png` };
})();
return {
id: String(row.id),
uri: longId,
url: longId,
account: baseAccount,
content: content.startsWith('<')? content : `<p>${content}</p>`,
created_at: new Date(parseDate(row.created_at)).toISOString(),
visibility: 'public',
reblogs_count, // 전체 부스트 카운트 / Full boost count
favourites_count, // 전체 좋아요 카운트 / Full like count
replies_count: 0,
favourited: !!favourited, // 전체 좋아요 / Full like
reblogged: !!reblogged, // 전체 부스트 / Full Boost
muted: false, bookmarked: false, pinned: false,
};
... ...
}
✔️ announce 로직 수정 – DB에서 announce(boost) 저장하기 않기
Modify ‘announce’ logic – do not save ‘announce’ (boost) to the database.
— announce에 저장된 글과 일치하는 inbox_posts 글에 부스트 표시
Display a boost indicator on inbox_posts entries that match posts stored in announce.
–app/users/[username]/inbox/route.ts
// Announce 수신 - actor 포함! / Receive Announce - include actor!
if (body.type === 'Announce') {
try {
const announceId = body.id;
let objectId = typeof body.object === 'string' ? body.object : body.object?.id;
if (!objectId) return new Response('', { status: 202 });
objectId = objectId.replace('/posts/', '/statuses/');
console.log(`🔁 [${username}] Announce 도착: ${actorId} -> ${objectId}`);
// actor 컬럼 포함
db.prepare('INSERT OR IGNORE INTO announces (id, actor, object, username) VALUES (?,?,?,?)')
.run(announceId, actorId, objectId, username);
// ✅ myapp36
// - announce(boost)는 inbox_posts에 저장 취소,announce테이블 값과 같은것 검색하기
// For `announce(boost)`, remove the entry from `inbox_posts` and search for records matching the values in the `announce` table.
// try {
// const noteRes = await fetch(objectId, { headers: { Accept: 'application/activity+json' } });
// if (noteRes.ok) {
// const note = await noteRes.json();
// const longId = note.id || objectId;
// const shortId = `${longId.split('/').pop()}_boost_${Date.now()}`;
// const content = note.content || '';
// db.prepare(`INSERT OR IGNORE INTO inbox_posts (id, actor, content, username, original_id, created_at) VALUES (?,?,?,?,?,?)`)
// .run(shortId, actorId, content, username, longId, body.published || new Date().toISOString());
// }
// } catch {}
console.log(`✅ announces 저장 완료 / announces saved: ${announceId}`);
} catch (e) {
console.error(`❌ Announce 저장 실패 / Failed to save announce`, e);
}
return new Response('', { status: 202 });
}
📁 테스트 / Test
✔️ pinafore.social
— inbox_posts + posts의 글을 확인합니다.

✔️ aloy-horizon.duckdns.org/@user1
— 모든 데이터와 카운트는 여기서 확인 할 수 있습니다.
You can check all the data and counts here.

요한복음 8장 32절 / John 8:32
“그리고 너희는 진리를 알게 될 것이며, 진리가 너희를 자유롭게 할 것이다.”
“Then you will know the truth ,and the truth will set you free”