{"id":8485,"date":"2026-08-22T10:08:15","date_gmt":"2026-08-22T01:08:15","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=8485"},"modified":"2026-08-22T12:29:43","modified_gmt":"2026-08-22T03:29:43","slug":"nextjssns-server-9","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/08\/22\/nextjssns-server-9\/","title":{"rendered":"[nextjs]SNS Server-9(myapp19)"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb inbox_posts \ud14c\uc774\ube14\uc5d0 actor\uc640 original_id\ud544\ub4dc\ub97c \ucd94\uac00 \ud569\ub2c8\ub2e4.<br>Add the <code>actor<\/code> and <code>original_id<\/code> fields to the <code>inbox_posts<\/code> table.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb original_id\ud544\ub4dc\ub294 \ub098\uc911\uc5d0 id(API\uc758 id \ubd80\ubd84)\ub97c \uc9e7\uac8c \ubcc0\ud658\ud558\uae30 \uc704\ud574\uc11c \ub9cc\ub4e0 \ud544\ub4dc\uc785\ub2c8\ub2e4.<br>The <code>original_id<\/code> field was created to store a shortened version of the <code>id<\/code> (the ID portion of the API) for later use.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb Actor\ub294 \uae00 \ubcf4\ub0b8 \uc0ac\ub78c\uc758 \uc804\uccb4 URL\uc785\ub2c8\ub2e4.<br>&#8220;Actor&#8221; is the full URL of the person who sent the post.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \uae30\uc874\uc758 \ud0c0\uc784\ub77c\uc778 UI\ubd80\ubd84\uc758 \uc8fc\uc18c\uc5d0 @userid\ub97c \uc0ac\uc6a9\ud569\ub2c8\ub2e4.<br>Use @userid in the URL for the existing timeline UI section.<\/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>myapp19\/  \n\u251c\u2500\u2500 app\/  (Next.js App Router)\n\u2502   \u251c\u2500\u2500 .well-known\/webfinger\/route.ts  -&gt; webfinger\n\u2502   \u251c\u2500\u2500 api\/posts\/route.ts     -&gt; Writing API\n\u2502   \u251c\u2500\u2500 api\/follow\/route.ts    -&gt; Follow API(temporary)\n\u2502   \u251c\u2500\u2500 api\/timeline\/route.ts  -&gt; Timeline API\n\u2502   \u251c\u2500\u2500 users\/&#91;username]\/\n\u2502   \u2502   \u251c\u2500\u2500 statuses\/&#91;id]\/route.ts -&gt; Indivisual Post\n\u2502   \u2502   \u251c\u2500\u2500 route.ts               -&gt; Acotr Information\n\u2502   \u2502   \u251c\u2500\u2500 followers\/route.ts     -&gt; Followers List\n\u2502   \u2502   \u251c\u2500\u2500 following\/route.ts     -&gt; Following List\n\u2502   \u2502   \u251c\u2500\u2500 inbox\/route.ts         -&gt; Inbox\n\u2502   \u2502   \u2514\u2500\u2500 outbox\/route.ts        -&gt; outbox\n\u2502   \u251c\u2500\u2500 usersui\/&#91;username]\/page.tsx  -&gt; Timeline UI\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 ap.ts                  -&gt; Follow Accept\n\u2502   \u2514\u2500\u2500 db.ts                  -&gt; DB connection\n\u251c\u2500\u2500 data\/\n\u2502   \u2514\u2500\u2500 keys\/                  -&gt; private.pem, public.pem\n\u251c\u2500\u2500 data.sqlite                -&gt; Database\n\u251c\u2500\u2500 Caddyfile                  -&gt; https \n\u2514\u2500\u2500 package.json<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \ud504\ub85c\uc81d\ud2b8 \uc2dc\uc791(myapp19)<br>Project Start (myapp19)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npx create-next-app@latest<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 SQLite\uc124\uce58 \/ Installing SQLite<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd ~\/myapp19\nnpm install better-sqlite3\nnpm install -D @types\/better-sqlite3<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 DDNS,https\uc124\uc815 \/ DDNS,https settings<\/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=\"mY0IIiLK3d\"><a href=\"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/08\/10\/nextjs-dns-caddy\/\">[nextjs]nextjs+domain(ddns)+https(caddy)<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"&#8220;[nextjs]nextjs+domain(ddns)+https(caddy)&#8221; &#8212; freelifemakers.org\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/08\/10\/nextjs-dns-caddy\/embed\/#?secret=QJoiASakzf#?secret=mY0IIiLK3d\" data-secret=\"mY0IIiLK3d\" width=\"560\" height=\"315\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>\ud83d\udcc1 \ub370\uc774\ud130\ubca0\uc774\uc2a4 \ud544\ub4dc \ucd94\uac00<br>Add database field<\/p>\n\n\n\n<p>\u2714\ufe0f inbox_posts\ud14c\uc774\ube14\uc5d0 actor\uac00 \uc5c6\ub2e4\uba74 actor\ud544\ub4dc\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4.<br>If the <code>actor<\/code> field does not exist in the <code>inbox_posts<\/code> table, add it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALTER TABLE inbox_posts ADD COLUMN actor TEXT;\nALTER TABLE inbox_posts ADD COLUMN original_id TEXT;<\/code><\/pre>\n\n\n\n<p>1)inbox_posts\ud14c\uc774\ube14\uc5d0 actor\ud544\ub4dc\ub97c \ucd94\uac00\ud558\uace0 posts\ud14c\uc774\ube14\uc5d0\ub294 \ucd94\uac00\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.<br>Add the <code>actor<\/code> field to the <code>inbox_posts<\/code> table, but do not add it to the <code>posts<\/code> table.<\/p>\n\n\n\n<p>&#8212; \uc804\uccb4 DB \uc2a4\ud0a4\ub9c8 \/ Entire DB schema<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sqlite&gt; .schema\nCREATE TABLE posts (\n    id TEXT PRIMARY KEY,\n    content TEXT NOT NULL,\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n  , username TEXT DEFAULT 'user1');\nCREATE TABLE followers (\n    id TEXT PRIMARY KEY,\n    actor TEXT NOT NULL,\n    inbox TEXT NOT NULL\n  , username TEXT);\nCREATE TABLE users (\n  username TEXT PRIMARY KEY,\n  display_name TEXT DEFAULT '',\n  summary TEXT DEFAULT '',\n  private_key TEXT,\n  public_key TEXT,\n  created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n);\nCREATE INDEX idx_followers_username ON followers(username);\nCREATE INDEX idx_followers_actor ON followers(actor);\nCREATE TABLE following (\n  id TEXT PRIMARY KEY,\n  actor TEXT NOT NULL,\n  username TEXT NOT NULL,\n  created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n);\nCREATE INDEX idx_following_username ON following(username);\nCREATE INDEX idx_following_actor ON following(actor);\n<strong>CREATE TABLE inbox_posts (\n  id TEXT PRIMARY KEY,\n  actor TEXT,\n  content TEXT,\n  username TEXT,\n  created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n, original_id TEXT);<\/strong>\nCREATE INDEX idx_posts_username_created ON posts(username, created_at DESC);\nCREATE INDEX idx_inbox_username_created ON inbox_posts(username, created_at DESC);\nCREATE INDEX idx_inbox_actor ON inbox_posts(actor);\nsqlite&gt; <\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ud0c0\uc784\ub77c\uc778 \ub77c\uc6b0\ud2b8 \ucf54\ub4dc \uc218\uc815<br>Modify timeline route code<\/p>\n\n\n\n<p>&#8212; myapp19\/app\/api\/timeline\/route.ts<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ myapp19 \u2705\n  const timeline = db.prepare(`\n    SELECT id, content, username, username as actor, created_at, 'mine' as source \n    FROM posts WHERE username = ?\n    UNION ALL\n    SELECT id, content, username, actor, created_at, 'inbox' as source \n    FROM inbox_posts WHERE username = ?\n    ORDER BY created_at DESC\n    LIMIT 50\n`).all(username, username);<\/code><\/pre>\n\n\n\n<p>1)\ud0c0\uc784\ub77c\uc778 API\ub85c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc758 Actor\ub97c \uc870\ud68c\ud569\ub2c8\ub2e4.<br>Query the database for the Actor using the Timeline API.<\/p>\n\n\n\n<p>\u2714\ufe0f \ud0c0\uc784\ub77c\uc778 UI \ub77c\uc6b0\ud2b8 \ucf54\ub4dc \uc218\uc815<br>Modify timeline UI route code.<\/p>\n\n\n\n<p>&#8212; myapp19\/app\/usersui\/[username]\/page.tsx<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>... ...\n\n  const timeline = db.prepare(`\n    SELECT id, content, username, <strong>username as actor<\/strong>, created_at, 'mine' as source \n    FROM posts WHERE username = ?\n    UNION ALL\n    SELECT id, content, username, <strong>actor<\/strong>, created_at, 'inbox' as source \n    FROM inbox_posts WHERE username = ?\n    ORDER BY created_at DESC LIMIT 50\n  `).all(username, username) as any&#91;];\n\n... ...\n\n  return (\n    &lt;div style={{ padding: 20 }}&gt;\n      &lt;h1&gt;{username} \ud0c0\uc784\ub77c\uc778 \/ Timeline&lt;\/h1&gt;\n      &lt;ul&gt;\n        {timeline.map((p:any) =&gt; (\n          &lt;li key={`${p.source}-${p.id}`}&gt;\n            &#91;{p.source}] &lt;b&gt;{p.actor}&lt;\/b&gt;: {p.content} - {p.created_at}\n          &lt;\/li&gt;\n        ))}\n      &lt;\/ul&gt;\n    &lt;\/div&gt;\n  );<\/code><\/pre>\n\n\n\n<p>1)\ud0c0\uc784\ub77c\uc778UI\uc5d0\uc11c Actor\ub97c \ubcf4\uc5ec\uc90d\ub2c8\ub2e4.<br>Displays the Actor in the timeline UI.<\/p>\n\n\n\n<p>\u2714\ufe0f \uc778\ubc15\uc2a4 \ub77c\uc6b0\ud2b8 \ucf54\ub4dc \uc218\uc815<br>Modify inbox route code<\/p>\n\n\n\n<p>&#8212; myapp19\/app\/users\/[username]\/inbox\/route.ts<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>db.prepare(`\nINSERT OR IGNORE INTO inbox_posts (id, <strong>actor<\/strong>, content, username, <strong>original_id<\/strong>, created_at) VALUES (?, ?, ?, ?, ?, ?)`).run(postId, actorId, content, username, note.id, note.published || new Date().toISOString());<\/code><\/pre>\n\n\n\n<p>1)DB\uc5d0 Actor\uc640 orginal_id\ub97c \uc800\uc7a5\ud558\ub294 \ubd80\ubd84\uc785\ub2c8\ub2e4.<br>This is the part that saves the Actor and original_id to the database.<\/p>\n\n\n\n<p>\u2714\ufe0f \ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \uc544\ub798\uc758 \uc8fc\uc18c\ub85c \ud14c\uc2a4\ud2b8 \ud574\ubd05\ub2c8\ub2e4.<br>Try testing it in your browser using the address below.<\/p>\n\n\n\n<p>&#8212; Timeline API<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;aloy-horizon.duckdns.org\/api\/timeline?username=user1<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"550\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-api-jpg.jpg\" alt=\"\" class=\"wp-image-8526\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-api-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-api-jpg-300x206.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-api-jpg-768x528.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-api-jpg-400x275.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Timeline API<\/figcaption><\/figure>\n\n\n\n<p>&#8212; Timeline UI<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;aloy-horizon.duckdns.org\/usersui\/user1<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"349\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-ui-jpg.jpg\" alt=\"\" class=\"wp-image-8528\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-ui-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-ui-jpg-300x131.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-ui-jpg-768x335.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-timeline-ui-jpg-400x175.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">TImeline UI<\/figcaption><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udcc1 \ud0c0\uc784\ub77c\uc778 \uc8fc\uc18c\uc5d0 @userid \uc0ac\uc6a9\ud558\uae30<br>Using @userid in the timeline URL<\/p>\n\n\n\n<p>\u2714\ufe0f myapp19\/next.config.ts \ucf54\ub4dc \uc218\uc815<br>Modify myapp19\/next.config.ts code<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import type { NextConfig } from \"next\";\n\nconst nextConfig: NextConfig = {\n  \/* allow domain*\/\n  allowedDevOrigins: &#91;'aloy-horizon.duckdns.org', '*.duckdns.org'],\n\n  \/* host.domain.org\/@userid *\/\n<strong>  async rewrites() {\n    return &#91;\n      {\n        source: '\/@:username',\n        destination: '\/usersui\/:username',\n      },\n    ]\n  }<\/strong>\n};\n\n\nexport default nextConfig;\n<\/code><\/pre>\n\n\n\n<p>1)rewrites()\ud568\uc218\ub294 nextjs\uc608\uc57d\ud568\uc218\ub77c \uc790\ub3d9\uc2e4\ud589\ub429\ub2c8\ub2e4.<br>The <code>rewrites()<\/code> function is a reserved Next.js function, so it executes automatically.<\/p>\n\n\n\n<p>\u2714\ufe0f \uc11c\ubc84\ub97c \uc7ac\uc2dc\uc791\ud569\ub2c8\ub2e4.<br>Restarting the server.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ctrl + c\nnpm run dev<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c \ub2e4\uc74c\uacfc \uac19\uc774 \uc811\uc18d\ud574 \ubd05\ub2c8\ub2e4.<br>Try accessing the following in your browser.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;aloy-horizon.duckdns.org\/@user1<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"388\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-at-jpg.jpg\" alt=\"\" class=\"wp-image-8531\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-at-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-at-jpg-300x146.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-at-jpg-768x372.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp19-at-jpg-400x194.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">@userid<\/figcaption><\/figure>\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 inbox_posts \ud14c\uc774\ube14\uc5d0 actor\uc640 original_id\ud544\ub4dc\ub97c \ucd94\uac00 \ud569\ub2c8\ub2e4.Add the actor and original_id fields to the inbox_posts table. \ud83d\udc49\ud83c\udffb original_id\ud544\ub4dc\ub294 \ub098\uc911\uc5d0 id(API\uc758 id \ubd80\ubd84)\ub97c \uc9e7\uac8c \ubcc0\ud658\ud558\uae30 \uc704\ud574\uc11c \ub9cc\ub4e0 \ud544\ub4dc\uc785\ub2c8\ub2e4.The original_id field was created to store a shortened version of the id (the ID portion of the API) for later use. \ud83d\udc49\ud83c\udffb Actor\ub294 \uae00 \ubcf4\ub0b8 \uc0ac\ub78c\uc758 \uc804\uccb4 URL\uc785\ub2c8\ub2e4.&#8220;Actor&#8221; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,27,1,7],"tags":[],"class_list":["post-8485","post","type-post","status-publish","format-standard","hentry","category-news","category-nextjs","category-uncategorized","category-website","missing-thumbnail"],"_links":{"self":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/8485","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=8485"}],"version-history":[{"count":53,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/8485\/revisions"}],"predecessor-version":[{"id":8546,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/8485\/revisions\/8546"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=8485"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=8485"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=8485"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}