{"id":9778,"date":"2026-09-07T07:17:45","date_gmt":"2026-09-06T22:17:45","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=9778"},"modified":"2026-09-07T12:48:30","modified_gmt":"2026-09-07T03:48:30","slug":"nextjs-sns-server-24","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/09\/07\/nextjs-sns-server-24\/","title":{"rendered":"[nextjs]SNS Server-24(myapp34)\u00a0"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb \ud68c\uc6d0\uac00\uc785 \ubc0f \ub85c\uadf8\uc778 \uae30\ub2a5\uc785\ub2c8\ub2e4.<br>These are the sign-up and login functions.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ub85c\uadf8\uc778 \ud6c4 pinafore\uc5d0\uc11c \ub0b4 \uc11c\ubc84(aloy-horizon.duckdns.org)\uac00 \uac80\uc0c9\ub418\ub3c4\ub85d \ud569\ub2c8\ub2e4.<br>After logging in, ensure that my server (aloy-horizon.duckdns.org) is discoverable in Pinafore.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ub85c\uadf8\uc778 \ud6c4 pinafore \ud0c0\uc784\ub77c\uc778\uc5d0\uc11c \ub0b4 \uc11c\ubc84\uc758 \uae00\uc744 \ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4.<br>After logging in, you can view posts from your server on the Pinafore timeline.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \uc804\uccb4 \ucf54\ub4dc\ub294 \uae43\ud5c8\ube0c\uc5d0\uc11c \ud655\uc778 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<br>You can find the full code on GitHub.<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/gideonslife01\/flm-nextjs\">https:\/\/github.com\/gideonslife01\/flm-nextjs<\/a><\/p>\n\n\n\n<p>\ud83d\udcc1 \uc804\uccb4 \ud504\ub85c\uc81d\ud2b8 \uad6c\uc870 \/ Overall Project Structure<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>myapp project\/  \n\u251c\u2500\u2500 app\/  (Next.js App Router)\n\u2502   \u251c\u2500\u2500 .well-known\/webfinger\/route.ts  -> webfinger\n\u2502   \u251c\u2500\u2500 api\/follow\/route.ts    -> Follow API(temporary)\n\u2502   \u251c\u2500\u2500 api\/announce\/route.ts  -> Boost(Announcement)\n\u2502   \u251c\u2500\u2500 api\/like\/route.ts.     -> Like API\n\u2502   \u251c\u2500\u2500 api\/posts\/route.ts     -> Writing API\n\u2502   \u251c\u2500\u2500 api\/timeline\/route.ts  -> Timeline API\n\u2502   \u251c\u2500\u2500 <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">api\/v1\/instance<\/span>\/route.ts -> auth\n\u2502   \u251c\u2500\u2500 api\/v1\/apps\/route.ts.    -> auth\n\u2502   \u251c\u2500\u2500 api\/v1\/accounts\/verify_credentials\/route.ts -> auth\n\u2502   \u251c\u2500\u2500 api\/v1\/statuses\/route.ts -> Write Post\n\u2502   \u251c\u2500\u2500 <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">api\/v1\/timelines\/home\/route.ts<\/span> -> timeline\n\u2502   \u251c\u2500\u2500 <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">oauth\/authorize\/route.ts<\/span> -> auth\n\u2502   \u251c\u2500\u2500 <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">oauth\/token\/route.ts<\/span> -> auth\n\u2502   \u251c\u2500\u2500<strong> <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">api\/v1\/search\/home\/route.ts<\/span> -> search\n<\/strong>\u2502   \u251c\u2500\u2500 <strong><span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">api\/v2\/search\/home\/route.ts<\/span> -> search<\/strong>\n\u2502   \u251c\u2500\u2500 users\/&#91;username]\/\n\u2502   \u2502   \u251c\u2500\u2500 statuses\/&#91;id]\/route.ts -> Indivisual Post\n\u2502   \u2502   \u251c\u2500\u2500 route.ts               -> Acotr Information\n\u2502   \u2502   \u251c\u2500\u2500 followers\/route.ts     -> Followers List\n\u2502   \u2502   \u251c\u2500\u2500 following\/route.ts     -> Following List\n\u2502   \u2502   \u251c\u2500\u2500 inbox\/route.ts         -> Inbox\n\u2502   \u2502   \u2514\u2500\u2500 outbox\/route.ts        -> outbox\n\u2502   \u251c\u2500\u2500 usersui\/&#91;username]\/\n\u2502   \u2502   \u251c\u2500\u2500 page.tsx               -> Timeline UI\n\u2502   \u2502   \u2514\u2500\u2500 _components\/themes\/\n\u2502   \u2502       \u251c\u2500\u2500 themeex\/ThemeexTheme.tsx   -> Example Theme\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/PinaforeTheme.tsx -> Theme 1\n\u2502   \u2502       \u251c\u2500\u2500 mastodon\/MastodonTheme.tsx -> Theme 2\n\u2502   \u2502       \u2514\u2500\u2500 minimal\/MinimaltTheme.tsx  -> Theme 3\n\u2502   \u251c\u2500\u2500 layout.tsx, page.tsx, globals.css\n\u2502   \u2514\u2500\u2500 favicon.ico\n\u251c\u2500\u2500 lib\/\n\u2502   \u251c\u2500\u2500 theme.tsx              -> Theme Provider\n\u2502   \u251c\u2500\u2500 watchThemes.ts         -> Check real-time theme changes\n\u2502   \u251c\u2500\u2500 auth.ts                -> Authentication, User Management\n\u2502   \u251c\u2500\u2500 ap.ts                  -> Follow,Undo,Create,Likes,Announce\n\u2502   \u2514\u2500\u2500 db.ts                  -> DB connection\n\u251c\u2500\u2500 data\/\n\u2502   \u251c\u2500\u2500 keys\/userIDs\/          -> private.pem, public.pem(New)\n\u2502   \u2514\u2500\u2500 keys\/                  -> private.pem, public.pem(legacy)\n\u251c\u2500\u2500 data.sqlite                -> Database(1\/3)\n\u251c\u2500\u2500 data.sqlite-wal            -> Database(2\/3)\n\u251c\u2500\u2500 data.sqlite-shm            -> Database(3\/3)\n\u251c\u2500\u2500 Caddyfile                  -> https \n\u251c\u2500\u2500 instrumentation.ts         -> Background Server\n\u2514\u2500\u2500 package.json<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \ud504\ub85c\uc81d\ud2b8 \uc2dc\uc791 \/ Project Start<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-freelifemakers-org wp-block-embed-freelifemakers-org\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"4LmCZGLk1R\"><a href=\"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/09\/03\/nextjs-project-summary\/\">[nextjs]\ud504\ub85c\uc81d\ud2b8\uc694\uc57d\/project summary(myapp30+)<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"&#8220;[nextjs]\ud504\ub85c\uc81d\ud2b8\uc694\uc57d\/project summary(myapp30+)&#8221; &#8212; freelifemakers.org\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/09\/03\/nextjs-project-summary\/embed\/#?secret=GlXegIozmc#?secret=4LmCZGLk1R\" data-secret=\"4LmCZGLk1R\" width=\"560\" height=\"315\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udcc1 \uc790\ub3d9\uc0dd\uc131\ub41c \ud14c\uc774\ube14 \/ Automatically generated table<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE outbox (\n          id TEXT PRIMARY KEY,\n          type TEXT,\n          actor TEXT,\n          object TEXT,\n          created_at INTEGER\n        );<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \uac80\uc0c9\uae30\ub2a5 \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add search function route<\/p>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/search\/route.ts &#8211; \ud558\uc704 \ud638\ud658\uc6a9 \/ For backward compatibility<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># v2 \ucf54\ub4dc\uc640 \ub3d9\uc77c\ud568 \/ Same as the v2 code.<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v2\/search\/route.ts &#8211; \uc2e4\uc81c \uc0ac\uc6a9\ub418\ub294 \ub77c\uc6b0\ud2b8 \/ Routes actually used<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u2705 myapp34\nimport { NextResponse } from 'next\/server';\nimport db from '@\/lib\/db';\n\nconst DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';\n\nexport async function GET(req: Request) { ... }<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ub85c\uceec \uc11c\ubc84 \ud14c\uc2a4\ud2b8 \/ Local Server Test(aloy-horizon.duckdns.org)<\/p>\n\n\n\n<p>&#8212; V1, V2 \ubaa8\ub450 \ub3d9\uc77c\ud55c \ucf54\ub4dc \uc0ac\uc6a9\ud558\uba74 \ub428<br>You can use the same code for both V1 and V2.<\/p>\n\n\n\n<p>&#8212; V1<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"https:\/\/aloy-horizon.duckdns.org\/api\/v1\/search?q=user1\" -H \"Authorization: Bearer YOUR_TOKEN\"<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"https:\/\/aloy-horizon.duckdns.org\/api\/v1\/search?q=user1\" -H \"4847d5c9-efdd-450f-b724-01c28676d6ea433c1b89-a4ac-437b-a41a-39fc2e6bdfa6\"\n{\"accounts\":&#91;{\"id\":\"1\",\"username\":\"user1\",\"acct\":\"user1@aloy-horizon.duckdns.org\",\"display_name\":\"user1\",\"url\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\",\"avatar\":\"https:\/\/aloy-horizon.duckdns.org\/icon.png\"}],\"statuses\":&#91;],\"hashtags\":&#91;]}% <\/code><\/pre>\n\n\n\n<p>&#8212; V2<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"https:\/\/aloy-horizon.duckdns.org\/api\/v2\/search?q=user1\" -H \"Authorization: Bearer YOUR_TOKEN\"<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>gimdaegyeong@gimdaegyeong-ui-MacBookAir ~ % curl \"https:\/\/aloy-horizon.duckdns.org\/api\/v2\/search?q=user1\" -H \"Authorization: Bearer 4847d5c9-efdd-450f-b724-01c28676d6ea433c1b89-a4ac-437b-a41a-39fc2e6bdfa6\"\n\n{\"accounts\":&#91;{\"id\":\"1\",\"username\":\"user1\",\"acct\":\"user1@aloy-horizon.duckdns.org\",\"display_name\":\"user1\",\"url\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\",\"avatar\":\"https:\/\/aloy-horizon.duckdns.org\/icon.png\"}],\"statuses\":&#91;],\"hashtags\":&#91;]}%  <\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ub9ac\ubaa8\ud2b8 \uc11c\ubc84 \ud14c\uc2a4\ud2b8 \/ Remote Server Test <\/p>\n\n\n\n<p>&#8212; pinafore.social\uc5d0\uc11c \uc9c1\uc811 \uac80\uc0c9  \/ Search directly on pinafore.social<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"429\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-mastodon-jpg.jpg\" alt=\"\" class=\"wp-image-9838\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-mastodon-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-mastodon-jpg-300x161.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-mastodon-jpg-768x412.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-mastodon-jpg-400x215.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Mastodon<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"458\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-gts-user1-jpg.jpg\" alt=\"\" class=\"wp-image-9839\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-gts-user1-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-gts-user1-jpg-300x172.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-gts-user1-jpg-768x440.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-search-gts-user1-jpg-400x229.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">GTS(freelifemakers.com)<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/accounts\/[id]\/route.ts &#8211; \ud504\ub85c\ud544 \uc815\ubcf4 \/ Profile information<\/p>\n\n\n\n<p>&#8212; \ub9ac\ubaa8\ud2b8 \uc11c\ubc84\ub294 pinafore.social\uc5d0\uc11c \uac80\uc0c9\ud55c \uc720\uc800\ub97c \uc758\ubbf8\ud569\ub2c8\ub2e4.<br>A remote server refers to a user found on pinafore.social.<\/p>\n\n\n\n<p>&#8212; \ub85c\uceec\uc11c\ubc84(aloy-horizon),\ub9ac\ubaa8\ud2b8\uc11c\ubc84(\ub2e4\ub978 \uc11c\ubc84\uc758 \uc720\uc800)<br>Local server (aloy-horizon), remote server (user on another server)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/\u2705 myapp34 - Profile info.\n\nimport { NextResponse } from 'next\/server';\nconst DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';\n\nexport async function GET(req: Request, { params }: { params: Promise&lt;{ id: string }&gt; }) { ... ...\n\n    \/\/ \u2705 \ub9ac\ubaa8\ud2b8\/remote!\n    if (id.includes('mastodon.social') || id.includes('https') || id.startsWith('remote_')) { ... ...}\n... ...\n\n    \/\/ \u2705 \ub85c\uceec\/local\n    const username = rawId === '1'? 'user1' : rawId.replace('user', '')? `user${rawId}` : 'user1';\n    const headerUrl = `https:\/\/picsum.photos\/seed\/${username}_${DOMAIN}\/1200\/400`;\n\n    return NextResponse.json({ ... ... })\n\n}<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/accounts\/[id]\/statuses\/route.ts &#8211; \uc720\uc800\uac00 \uc791\uc131\ud55c \uae00   \/ User-written post <\/p>\n\n\n\n<p>&#8212; \ub9ac\ubaa8\ud2b8 \uc11c\ubc84\ub294 pinafore.social\uc5d0\uc11c \uac80\uc0c9\ud55c \uc720\uc800\ub97c \uc758\ubbf8\ud569\ub2c8\ub2e4.<br>A remote server refers to a user found on pinafore.social.<\/p>\n\n\n\n<p>&#8212; \ub85c\uceec\uc11c\ubc84(aloy-horizon),\ub9ac\ubaa8\ud2b8\uc11c\ubc84(\ub2e4\ub978 \uc11c\ubc84\uc758 \uc720\uc800)<br>Local server (aloy-horizon), remote server (user on another server)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u2705 myapp34 - User-written post \nimport { NextResponse } from 'next\/server';\nimport db from '@\/lib\/db';\n\nconst DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';\n\nexport async function GET(req: Request, { params }: { params: Promise&lt;{ id: string }&gt; }) { \n... ...\n\n \/\/ \u2705 \ub9ac\ubaa8\ud2b8\/remote (https:\/\/ \ub610\ub294 remote_ \ub610\ub294 mastodon \ud3ec\ud568!) \/ \n    if (id.startsWith('https:\/\/') || id.startsWith('remote_') || id.includes('mastodon.social')) { ... ...}\n\n  \/\/ \u2705 \ub85c\uceec\/local\n    const username = rawId === '1'? 'user1' : `user${rawId}`;\n    console.log(`\ud83c\udfe0 \ub85c\uceec! username=${username}`);\n... ...\n\n}<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"https:\/\/aloy-horizon.duckdns.org\/api\/v1\/accounts\/1\/statuses\" -H \"Authorization: Bearer TOKEN\"<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># \ud130\ubbf8\ub110\uc5d0\uc11c \uc2e4\ud589 \/ Run in the terminal\ncurl \"https:\/\/aloy-horizon.duckdns.org\/api\/v1\/accounts\/1\/statuses\" -H \"Authorization: Bearer 4847d5c9-efdd-450f-b724-016d6eac2867a433c1b89-a4ac-437b-a41a-39fc2e6bdfa6\"\n\n# \uc751\ub2f5 \/ response\n&#91;{\"id\":\"73a7e43b-20000-40dc-3hj4kac-1aa123fda326\",\"uri\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\/statuses\/73a7e43b-24b4-40dc-8ebc-1aa123fda326\",\"url\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\/statuses\/73a7e43b-24b4-40dc-8ebc-1aa123fda326\",\"account\":{\"id\":\"1\",\"username\":\"user1\",\"acct\":\"user1@aloy-horizon.duckdns.org\",\"display_name\":\"user1\",\"avatar\":\"https:\/\/aloy-horizon.duckdns.org\/icon.png\"},\"content\":\"&lt;p&gt;aloy first pinafore.social post test&lt;\/p&gt;\",\"created_at\":\"2026-09-06T23:06:23.998Z\",\"visibility\":\"public\",\"reblogs_count\":0,\"favourites_count\":0},{\"id\":\"4ace7a75-3a40-494f-8dea-bf3f688b6cfb\", ... ...\n\n# \ub098\uba38\uc9c0 \ub370\uc774\ud130 \uc0dd\ub7b5\nThe remaining data is omitted.<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"594\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-user1-aloy-profile-jpg.jpg\" alt=\"\" class=\"wp-image-9841\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-user1-aloy-profile-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-user1-aloy-profile-jpg-300x223.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-user1-aloy-profile-jpg-768x570.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-user1-aloy-profile-jpg-400x297.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">local server profile<\/figcaption><\/figure>\n\n\n\n<p>\ud83d\udcc1 \ucf54\ub4dc\uc218\uc815 \/ Code Modification<\/p>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/statuses\/route.ts<\/p>\n\n\n\n<p>&#8212; \uae00\uc4f0\uae30 \ub77c\uc6b0\ud2b8(DB\uc800\uc7a5 \uae30\ub2a5 \ucd94\uac00)<br>Writing route (added DB save functionality)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ app\/api\/v1\/statuses\/route.ts - \uae00\uc4f0\uae30 DB\uc800\uc7a5 \/ Creating status and saving to DB\n\/\/ \u2705 myapp34-2\n\nimport { NextResponse } from 'next\/server';\nimport db from '@\/lib\/db';\nimport { randomUUID } from 'crypto';\n\nconst DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';\n\nexport async function POST(req: Request) {\n... ...\n\/\/ posts table\n      db.prepare(`INSERT INTO posts (id, username, content, created_at) VALUES (?,?,?,?)`)\n        .run(id, username, content, now);\n    } catch (e) { console.error('posts \uc800\uc7a5 \uc5d0\ub7ec\/posts save error', e); }\n\n... ...\n\n\/\/ outbox table\n      db.prepare(`INSERT INTO outbox (id, type, actor, object, created_at) VALUES (?,?,?,?,?)`)\n        .run(id, 'Create', `https:\/\/${DOMAIN}\/users\/${username}`, JSON.stringify(note), now);\n    } catch (e) { console.error('outbox \uc800\uc7a5 \uc5d0\ub7ec \/ outbox save error', e); }\n\n}<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ud14c\uc2a4\ud2b8 \/  Test<\/p>\n\n\n\n<p>&#8212; pinafore.social\uc5d0\uc11c \uae00\uc744 \uc791\uc131\ud55c \uacbd\uc6b0 \uc544\ub798 \ucc98\ub7fc DB\uc5d0 \uc800\uc7a5\ub429\ub2c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sqlite&gt; select * from posts;\ne6c99652-572f-491a-9e61-05e67b0d58fc|Hello! my SNS!!|2026-08-14 04:59:51|user1\ndcece9ee-8d4a-4f63-91e1-22fc60513f62|Hello! my SNS!!|2026-08-14 05:00:18|user1\ncc30330e-7040-4a1b-9e60-731066b8817e|Hello! my SNS3!!|2026-08-17 23:08:05|user1\ne6d532c8-974b-4a01-89f6-6e1b2c4cff32|Hello Fediverse! #test|2026-08-20 02:16:04|user1\naea0ec89-a368-44a9-8493-b2bd82a797a8|post test! #test|2026-08-20 03:08:18|user1\n9b2ead85-4b15-4df9-9de4-04c4a7fbcab4|Hello! Mastodon!!|2026-09-01 02:50:10|user1\naefc7ff8-5b38-4fb7-829d-9b14343cec6a|Hello local UI #apiposts|2026-09-05 03:39:37|user1\n4ace7a75-3a40-494f-8dea-bf3f688b6cfb|Hello Activity UI #activityposts|2026-09-05 03:39:48|user1\n<strong>73a7e43b-24b4-40dc-8ebc-1aa123fda326|aloy first pinafore.social post test|1788735983998|user1<\/strong>\nsqlite&gt; \nsqlite&gt; select * from outbox;\n73a7e43b-24b4-40dc-8ebc-1aa123fda326|Create|https:\/\/aloy-horizon.duckdns.org\/users\/user1|{\"id\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\/statuses\/73a7e43b-24b4-40dc-8ebc-1aa123fda326\",\"type\":\"Note\",<strong>\"content\":\"&lt;p&gt;aloy first pinafore.social post test&lt;\/p&gt;\"<\/strong>,\"attributedTo\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\",\"published\":\"2026-09-06T23:06:23.998Z\",\"to\":&#91;\"https:\/\/www.w3.org\/ns\/activitystreams#Public\"]}|1788735983998\nsqlite&gt; <\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"547\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-local-statuses-jpg.jpg\" alt=\"\" class=\"wp-image-9846\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-local-statuses-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-local-statuses-jpg-300x205.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-local-statuses-jpg-768x525.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-pinafore-local-statuses-jpg-400x274.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">local server posts<\/figcaption><\/figure>\n\n\n\n<p>&nbsp;\u2714\ufe0f <code>app\/api\/v1\/timelines\/home\/route.ts<\/code><\/p>\n\n\n\n<p>&#8212; pinafore\uc5d0 \ubcf4\uc5ec\uc904 \ud0c0\uc784\ub77c\uc778 \/ Timeline to display on Pinafore<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u2705 myapp34 - app\/api\/v1\/timelines\/home\/route.ts \n\/\/ - pinafore \ud648 \ud0c0\uc784\ub77c\uc778 API \/ pinafore Home Timeline API\n\nimport { NextResponse } from 'next\/server';\nimport db from '@\/lib\/db';\n\nconst DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';\n\nexport async function GET(req: Request) { ... }\nexport async function OPTIONS() { ... }<\/code><\/pre>\n\n\n\n<p>&#8212;  \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"https:\/\/aloy-horizon.duckdns.org\/api\/v1\/timelines\/home\" -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\"<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># \ud130\ubbf8\ub110\uc5d0\uc11c \uc2e4\ud589 \/  Run in the terminal\n% curl \"https:\/\/aloy-horizon.duckdns.org\/api\/v1\/timelines\/home\" -H \"Authorization: Bearer 4847d5c9-efdd-450f-b724-01c28676d6ea433c1b89-a4ac-437b-a41a-39fc2e6bdfa6\"\n\n# \uc751\ub2f5 \/ response\n&#91;{\"id\":\"73a7e43b-0000-1234-0000-1000000\",\"uri\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\/statuses\/73a7e43b-24b4-40dc-8ebc-1aa123fda326\",\"url\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\/statuses\/73a7e43b-24b4-40dc-8ebc-1aa123fda326\",\"account\":{\"id\":\"1\",\"username\":\"user1\",\"acct\":\"user1@aloy-horizon.duckdns.org\",\"display_name\":\"user1\",\"avatar\":\"https:\/\/aloy-horizon.duckdns.org\/icon.png\"},\"content\":\"&lt;p&gt;aloy first pinafore.social post test&lt;\/p&gt;\",\"created_at\":\"2026-09-06T23:06:23.998Z\",\"visibility\":\"public\",\"reblogs_count\":0,\"favourites_count\":0},{\"id\":\"4ace7a75-3a40-494f-8dea-bf3f688b6cfb\", ... ...\n\n# \ub098\uba38\uc9c0 \ub370\uc774\ud130 \uc0dd\ub7b5\nThe remaining data is omitted.<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"838\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-hometimeline-jpg.jpg\" alt=\"\" class=\"wp-image-9823\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-hometimeline-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-hometimeline-jpg-286x300.jpg 286w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-hometimeline-jpg-768x804.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-hometimeline-jpg-400x419.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Timeline<\/figcaption><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udcc1 \ub9ac\ubaa8\ud2b8 \uc11c\ubc84  \uc0c1\uc138 \ud398\uc774\uc9c0 \uc784\uc2dc\ub370\uc774\ud130<br>Remote Server Detail Page Placeholder Data<\/p>\n\n\n\n<p>\u2714\ufe0f \uc9c0\uae08 pinafore.social\uc5d0\uc11c \ub2e4\ub978\uc11c\ubc84(\ub9ac\ubaa8\ud2b8\uc11c\ubc84)\ub97c \uac80\uc0c9\ud574\uc11c \uc0c1\uc138\ud398\uc774\uc9c0\ub97c \ubcf4\uba74 \ub2e4\uc74c\ucc98\ub7fc \uc784\uc2dc\ub370\uc774\ud130\uac00 \ub4e4\uc5b4\uac00 \uc788\uc2b5\ub2c8\ub2e4.<br>If you search for another server (remote server) on pinafore.social right now and view its details page, you will see placeholder data like this.<\/p>\n\n\n\n<p>\u2714\ufe0f \ub2e4\uc74c \ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c \uc218\uc815 \uc608\uc815\uc785\ub2c8\ub2e4.<br>This is scheduled to be modified in the next project.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"934\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-search-fakedata-remote-mastodon-jpg.jpg\" alt=\"\" class=\"wp-image-9854\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-search-fakedata-remote-mastodon-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-search-fakedata-remote-mastodon-jpg-257x300.jpg 257w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-search-fakedata-remote-mastodon-jpg-768x897.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp34-search-fakedata-remote-mastodon-jpg-400x467.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">remote mastodon<\/figcaption><\/figure>\n\n\n\n<p>\ud83d\udcc1 \uc804\uccb4 \uacfc\uc815 \uc694\uc57d \/ Summary of the entire process<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1. \uae00\uc4f0\uae30 \/ Write Post\n- Pinafore\uc5d0\uc11c \uae00\uc4f0\uba74 \ub0b4 DB\uc5d0 \uc800\uc7a5\ub428.\nWhen you write on Pinafore, it gets saved to my database.\n\n- pinafore\uc5d0\uc11c POST \/api\/v1\/statuses \nPOST \/api\/v1\/statuses in pinafore\n\n- app\/api\/v1\/statuses\/route.ts\n\n\n2. \ud648 \ud0c0\uc784\ub77c\uc778 \/ Home Timeline\n- \ub0b4 \uae00\uacfc inbox\uae00(\ubc1b\uc740 \uae00\uc744 DB\uc5d0\uc11c \uaebc\ub0b4\uc11c \ubcf4\uc5ec \uc90c.\nMy posts and inbox posts (retrieved from the database and displayed).\n\n- Pinafore\uc5d0\uc11c GET \/api\/v1\/timelines\/home\nGET \/api\/v1\/timelines\/home in pinafore\n\n- app\/api\/v1\/timelines\/home\/route.ts\n\n3. \uac80\uc0c9 \/ Searh\n- pinafore\uc5d0\uc11c \uac80\uc0c9\nSearch in pinafore\n\n- Pinafore\uc5d0\uc11c GET \/api\/v2\/search?q=david@freelifemakers.com\nGET \/api\/v2\/search?q=david@freelifemakers.com in pinafore\n\n- webfinger + actor fetch! id=remote_freelifemakers_com_david \ub9ac\ud134\/return\n\n- app\/api\/v2\/search\/route.ts\n\n\n4. \uac80\uc0c9\ud55c \uc720\uc800 \ud504\ub85c\ud544 \/ Searched user profile\n- \uac80\uc0c9\ud55c \uc720\uc800 \uc0c1\uc138 \ud398\uc774\uc9c0 \ud504\ub85c\ud544\nSearched User Detail Page Profile\n\n- Pinafore\uc608\uc11c GET \/api\/v1\/accounts\/remote_freelifemakers_com_david\n\n- app\/api\/v1\/accounts\/&#91;id]\/route.ts \n\n5. \uac80\uc0c9\ud55c \uc720\uc800 \uae00 \/ Posts by the searched user\n\n- \uac80\uc0c9\ud55c \uc720\uc800 \uc0c1\uc138 \ud398\uc774\uc9c0 \uae00\nSearched User Detail Page Profile\n\n- Pinafore\uc5d0\uc11c GET \/api\/v1\/accounts\/remote_freelifemakers_com_david\/statuses\n\n- app\/api\/v1\/accounts\/&#91;id]\/statuses\/route.ts \n<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\uc694\ud55c\ubcf5\uc74c 8\uc7a5 32\uc808 \/ John 8:32<\/p>\n\n\n\n<p>&#8220;\uadf8\ub9ac\uace0 \ub108\ud76c\ub294 \uc9c4\ub9ac\ub97c \uc54c\uac8c \ub420 \uac83\uc774\uba70, \uc9c4\ub9ac\uac00 \ub108\ud76c\ub97c \uc790\uc720\ub86d\uac8c \ud560 \uac83\uc774\ub2e4.&#8221;<\/p>\n\n\n\n<p>&#8220;Then you will know the truth ,and the truth will set you free&#8221;<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udc49\ud83c\udffb \ud68c\uc6d0\uac00\uc785 \ubc0f \ub85c\uadf8\uc778 \uae30\ub2a5\uc785\ub2c8\ub2e4.These are the sign-up and login functions. \ud83d\udc49\ud83c\udffb \ub85c\uadf8\uc778 \ud6c4 pinafore\uc5d0\uc11c \ub0b4 \uc11c\ubc84(aloy-horizon.duckdns.org)\uac00 \uac80\uc0c9\ub418\ub3c4\ub85d \ud569\ub2c8\ub2e4.After logging in, ensure that my server (aloy-horizon.duckdns.org) is discoverable in Pinafore. \ud83d\udc49\ud83c\udffb \ub85c\uadf8\uc778 \ud6c4 pinafore \ud0c0\uc784\ub77c\uc778\uc5d0\uc11c \ub0b4 \uc11c\ubc84\uc758 \uae00\uc744 \ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4.After logging in, you can view posts from your server on the Pinafore timeline. \ud83d\udc49\ud83c\udffb [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":9823,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,27,1,7],"tags":[],"class_list":["post-9778","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-news","category-nextjs","category-uncategorized","category-website","has-thumbnail"],"_links":{"self":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/9778","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/comments?post=9778"}],"version-history":[{"count":83,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/9778\/revisions"}],"predecessor-version":[{"id":9870,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/9778\/revisions\/9870"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media\/9823"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=9778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=9778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=9778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}