{"id":11463,"date":"2026-10-03T08:25:14","date_gmt":"2026-10-02T23:25:14","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=11463"},"modified":"2026-10-03T14:21:18","modified_gmt":"2026-10-03T05:21:18","slug":"nextjs-sns-server-45","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/10\/03\/nextjs-sns-server-45\/","title":{"rendered":"[nextjs]SNS Server-45(myapp55)\u00a0"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb myapp45\uc5d0\uc11c\ub294 Tursky\ub85c\uadf8\uc778 \ubd80\ubd84\uc744 \uad6c\ud604\ud569\ub2c8\ub2e4.<br>In myapp45, the Tursky login functionality is implemented.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \uc6f9\uc774\ub098 pinafore\uc5d0\uc11c \uc815\uc0c1\uc801\uc73c\ub85c \ubaa8\ub4e0\ub370\uc774\ud130\uac00 \ud45c\uc2dc\ub429\ub2c8\ub2e4. <br>All data is displayed correctly on the web or in Pinafore.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ud558\uc9c0\ub9cc Tusky\uc571 \ud0c0\uc784\ub77c\uc778\uc5d0 \ub370\uc774\ud130\uac00 \uc815\uc0c1\uc801\uc73c\ub85c \ud45c\uc2dc\ub418\uc9c0 \uc54a\ub294 \ubb38\uc81c\uac00 \uc788\uc2b5\ub2c8\ub2e4.<br>However, there is an issue where data is not displaying correctly on the Tusky app&#8217;s timeline.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ucd94\ud6c4 \uc0b4\ud3b4 \ubcf4\ub3c4\ub85d \ud558\uace0 \uc5ec\uae30\uc11c\ub294 \uc571\uc5d0 \ub85c\uadf8\uc778 \uc644\ub8cc\ud558\uace0 404\uc624\ub958 \uc5c6\uc560\ub294 \ubd80\ubd84\uae4c\uc9c0\ub9cc \uc644\ub8cc\ud558\uaca0\uc2b5\ub2c8\ub2e4.<br>We will look into that later; for now, we will just complete the steps of logging into the app and resolving the 404 error.<\/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\" style=\"font-size:14px\"><code>myapp project\/  \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\/follow\/route.ts    -&gt; Follow API(temporary)\n\u2502   \u251c\u2500\u2500 api\/announce\/route.ts  -&gt; Boost(Announcement)\n\u2502   \u251c\u2500\u2500 api\/like\/route.ts.     -&gt; Like API\n\u2502   \u251c\u2500\u2500 api\/posts\/route.ts     -&gt; Writing API(deprecated)\n\u2502   \u251c\u2500\u2500 api\/timeline\/route.ts  -&gt; Timeline API\n\u2502   \u251c\u2500\u2500 api\/followinglist\/route.ts  -&gt; followinglist API\n\u2502   \u251c\u2500\u2500 api\/followerslist\/route.ts  -&gt; followerslist 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 -&gt; auth\n\u2502   \u251c\u2500\u2500 api\/v1\/apps\/route.ts.    -&gt; auth\n\u2502   \u251c\u2500\u2500 api\/v1\/accounts\/verify_credentials\/route.ts -&gt; auth\n\u2502   \u251c\u2500\u2500 api\/v1\/statuses\/route.ts -&gt; 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> -&gt; timeline\n\u2502   \u251c\u2500\u2500 api\/v1\/media\/route.ts -&gt; Image upload\n\u2502   \u251c\u2500\u2500 api\/v1\/media\/&#91;id]\/route.ts  -&gt; Image Delete \n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/instance\/rules\/route.ts<\/strong>\n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/instance\/domain_blocks\/route.ts <\/strong>\n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/filters\/route.ts<\/strong>\n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/lists\/route.ts <\/strong>\n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/announcements\/route.ts<\/strong>\n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/timelines\/public\/route.ts <\/strong>\n\u2502   \u251c\u2500\u2500 <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">oauth\/authorize\/route.ts<\/span> -&gt; auth\n\u2502   \u251c\u2500\u2500 <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">oauth\/token\/route.ts<\/span> -&gt; auth\n\u2502   \u251c\u2500\u2500 <strong><span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">oauth\/login-and-authorize\/route.ts<\/span> -&gt; Tursky,Pinafore login processing<\/strong>\n\u2502   \u251c\u2500\u2500 <strong>oauth\/revoke\/route.ts  <\/strong>  \n\u2502   \u251c\u2500\u2500 <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">api\/v1\/search\/home\/route.ts<\/span> -&gt; search\n\u2502   \u251c\u2500\u2500 <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">api\/v2\/search\/home\/route.ts<\/span> -&gt; search\n\u2502   \u251c\u2500\u2500 <strong>api\/v2\/notifications\/policy\/route.ts<\/strong>\n\u2502   \u251c\u2500\u2500 api\/v1\/accounts\/&#91;id]\/followers\/route.ts -&gt; followers\n\u2502   \u251c\u2500\u2500 api\/v1\/accounts\/&#91;id]\/following\/route.ts -&gt; following\n\u2502   \u251c\u2500\u2500 api\/v1\/statuses\/&#91;id]\/route.ts -&gt; -&gt; Post delete(pinafore)\n\u2502   \u251c\u2500\u2500 api\/auth\/signup\/route.ts        -&gt; Website Signup\n\u2502   \u251c\u2500\u2500 api\/auth\/login\/route.ts         -&gt; Website Login\n\u2502   \u251c\u2500\u2500<strong> <\/strong>api\/auth\/logout\/route.ts        -&gt; Website Logout\n\u2502   \u251c\u2500\u2500<strong> <\/strong>api\/auth\/refresh\/route.ts       -&gt; Refresh Token\n\u2502   \u251c\u2500\u2500<strong> <\/strong>api\/cleanup-orphan\/route.ts     -&gt; post,outbox clean up\n\u2502   \u251c\u2500\u2500<strong> <\/strong>api\/auth\/me\/route.tsx           -&gt; Login Check<strong>\n<\/strong>\u2502   \u251c\u2500\u2500 auth\/signup\/page.tsx            -&gt; Signup UI\n\u2502   \u251c\u2500\u2500 auth\/signup\/page.tsx            -&gt; Login UI        \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]\/\n\u2502   \u2502   \u251c\u2500\u2500 page.tsx               -&gt; Timeline UI(Server, 404)\n\u2502   \u2502   \u251c\u2500\u2500 ClientPage.tsx.        -&gt; Timeline UI(Client,Call Active Themes)\n\u2502   \u2502   \u2514\u2500\u2500 _components\/themes\/\n\u2502   \u2502       \u251c\u2500\u2500 themeex\/ThemeexTheme.tsx   -&gt; Example Theme\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/PinaforeTheme.tsx -&gt; Theme 1\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/FollowersList.tsx -&gt; Theme 1, FollowersList\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/FollowingList.tsx -&gt; Theme 1, FollowingList\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/NotificationsList.tsx -&gt; Theme 1, NotificationsList\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/TimelineList.tsx  -&gt; Theme 1, TimelineList\n\u2502   \u2502       \u251c\u2500\u2500 mastodon\/MastodonTheme.tsx -&gt; Theme 2\n\u2502   \u2502       \u2514\u2500\u2500 minimal\/MinimaltTheme.tsx  -&gt; 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              -&gt; Theme Provider\n\u2502   \u251c\u2500\u2500 watchThemes.ts         -&gt; Check real-time theme changes\n\u2502   \u251c\u2500\u2500 auth.ts                -&gt; Authentication, User Management\n\u2502   \u251c\u2500\u2500 ap.ts                  -&gt; Follow,Undo,Create,Likes,Announce\n\u2502   \u251c\u2500\u2500 visibility.ts          -&gt; Visibility(Public,Unlisted,Private,Direct)\n\u2502   \u2514\u2500\u2500 db.ts                  -&gt; DB connection\n\u251c\u2500\u2500 data\/\n\u2502   \u251c\u2500\u2500 keys\/userIDs\/          -&gt; private.pem, public.pem(New)\n\u2502   \u2514\u2500\u2500 keys\/                  -&gt; private.pem, public.pem(legacy)\n\u251c\u2500\u2500 data.sqlite                -&gt; Database(1\/3)\n\u251c\u2500\u2500 data.sqlite-wal            -&gt; Database(2\/3)\n\u251c\u2500\u2500 data.sqlite-shm            -&gt; Database(3\/3)\n\u251c\u2500\u2500 Caddyfile                  -&gt; https \n\u251c\u2500\u2500 instrumentation.ts         -&gt; 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=\"9Fbrd67nwk\"><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=V6wQy8vnTf#?secret=9Fbrd67nwk\" data-secret=\"9Fbrd67nwk\" 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 \ucf54\ub4dc\uc218\uc815 \/ Code Modification<\/p>\n\n\n\n<p>\u2714\ufe0f Tusky\uc571\uc5d0 \ub85c\uadf8\uc778 \uad6c\ud604 \/ Implementing login for the Tusky app<\/p>\n\n\n\n<p>1) <code>app\/oauth\/authorize\/route.ts<\/code>  <\/p>\n\n\n\n<p>A. \ud604\uc7ac \ubaa8\ubc14\uc77c\uc571\uacfc \uc6f9\uc0ac\uc774\ud2b8\uc778\uacbd\uc6b0 \ub3d9\uc77c\ud55c \ucf54\ub4dc\ub85c \uc791\ub3d9\ud558\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 \ubd84\ub9ac\uc801\uc6a9<br>Currently, the mobile app and the website do not operate on the same code, so they are implemented separately.<\/p>\n\n\n\n<p>B. fetch\ubc29\uc2dd\uc774 \uc544\ub2cc methd=POST \ubc29\uc2dd \uc801\uc6a9<br>Applied the POST method instead of the fetch method.<\/p>\n\n\n\n<p>&#8212; \ub85c\uadf8\uc778 \ud398\uc774\uc9c0\ub85c(\/auth\/login\/page.tsx)\uc774\ub3d9\ud558\uc9c0 \uc54a\uace0 \uc774 \ub77c\uc6b0\ud2b8\uc5d0\uc11c \ub85c\uadf8\uc778 \ud398\uc774\uc9c0 \ubcf4\uc5ec\uc8fc\uae30<br>Display the login page on this route without navigating to the login page (\/auth\/login\/page.tsx).<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>\n  \/\/ \u2705 for CORS error\n  \/\/ if (!username) {\n  \/\/     const loginUrl = new URL('\/auth\/login', ORIGIN); \/\/ \u2705 \ub3c4\uba54\uc778, not loacalhost \/ Domain, not localhost\n  \/\/     const realNextUrl = `${ORIGIN}${url.pathname}${url.search}`; \/\/ \u2705 Use the domain for Next.js, not localhost.\n  \/\/     loginUrl.searchParams.set('next', realNextUrl);\n  \/\/     return NextResponse.redirect(loginUrl.toString());\n  \/\/ }\n\n  \/\/ \u2705 myapp55 \uc218\uc815 - \ub85c\uadf8\uc778 \uc548 \ub410\uc73c\uba74 \/auth\/login\uc73c\ub85c \uc774\ub3d9\ud558\uc9c0 \ub9d0\uace0 \uc5ec\uae30\uc11c \ub85c\uadf8\uc778 \ud3fc \ubcf4\uc5ec\uc8fc\uae30\n  \/\/  If not logged in, do not redirect to \/auth\/login, but show the login form here.\n  if (!username) {\n    const html = `\n    &lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;meta charset=\"utf-8\"&gt;&lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;\n    &lt;title&gt;Login&lt;\/title&gt;\n    &lt;style&gt;body{font-family:sans-serif;max-width:400px;margin:50px auto;padding:20px}input{width:90%;padding:10px;margin:8px 0;font-size:16px}button{width:100%;padding:12px;background:#6364ff;color:white;border:0;border-radius:8px;font-size:16px}&lt;\/style&gt;\n    &lt;\/head&gt;&lt;body&gt;\n      &lt;h2&gt;\ud83d\udd10 \ub85c\uadf8\uc778 \ud544\uc694(OAuth)&lt;\/h2&gt;\n      &lt;p&gt;${clientId} \uc571 \uc2b9\uc778 \uc704\ud574 \ub85c\uadf8\uc778&lt;\/p&gt;\n      &lt;form <strong>method=\"POST\"<\/strong> action=\"\/oauth\/login-and-authorize\"&gt;\n        &lt;input type=\"hidden\" name=\"client_id\" value=\"${clientId}\"&gt;\n        &lt;input type=\"hidden\" name=\"redirect_uri\" value=\"${redirectUri}\"&gt;\n        &lt;input type=\"hidden\" name=\"scope\" value=\"${scope}\"&gt;\n        &lt;input name=\"username\" placeholder=\"username\" required&gt;\n        &lt;input name=\"password\" type=\"password\" placeholder=\"password\" required&gt;\n        &lt;button type=\"submit\"&gt;\ub85c\uadf8\uc778 &amp; \uc2b9\uc778&lt;\/button&gt;\n      &lt;\/form&gt;\n    &lt;\/body&gt;&lt;\/html&gt;`;\n    return new NextResponse(html, { headers: { 'Content-Type': 'text\/html; charset=utf-8' } });\n  }<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0fapp\/oauth\/login-and-authorize\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<p>&#8212;  Tursky\uc571\uc5d0 \ub85c\uadf8\uc778\uc744 \uc704\ud55c \ub77c\uc6b0\ud2b8,\ub85c\uadf8\uc778 \ucc98\ub9ac<br>Route and login handling for the Tursky app<\/p>\n\n\n\n<p>&#8212; api\/auth\/login\/route.ts\ub294 \uc6f9\uc0ac\uc774\ud2b8\uc6a9 \ub85c\uadf8\uc778 \ucc98\ub9ac<br>api\/auth\/login\/route.ts handles login for the website.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>... ...\nexport async function POST(req: Request) {\n... ...\n  \/\/ - Tusky\ub294 HTML+JS\ub85c, \uadf8 \uc678\ub294 302 \/ Tusky uses HTML+JS, others use 302\n  let html: string;\n  if (finalUrl.startsWith('oauth2redirect:\/\/') || finalUrl.includes('tusky')) {\n    html = `&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;body style=\"text-align:center;padding:50px\"&gt;&lt;h2&gt;\u2705 ${user.username} \uc2b9\uc778\ub428&lt;\/h2&gt;&lt;p&gt;&lt;a href=\"${finalUrl}\"&gt;${finalUrl}&lt;\/a&gt;&lt;\/p&gt;&lt;script&gt;setTimeout(()=&gt;{window.location.href=\"${finalUrl}\"},300)&lt;\/script&gt;&lt;\/body&gt;&lt;\/html&gt;`;\n  } else {\n    html = `&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;body&gt;&lt;script&gt;window.location.href=\"${finalUrl}\"&lt;\/script&gt;&lt;a href=\"${finalUrl}\"&gt;${finalUrl}&lt;\/a&gt;&lt;\/body&gt;&lt;\/html&gt;`;\n  }\n\n  const res = new NextResponse(html, { headers: { 'Content-Type': 'text\/html; charset=utf-8' } });\n  res.cookies.set(<strong>'token<\/strong>', accessToken, { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 60*15, path: '\/' });\n  res.cookies.set('<strong>refresh_token<\/strong>', refreshToken, { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 60*60*24*7, path: '\/' });\n  return res;\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\u2714\ufe0f 404 \uc624\ub958 \uba54\uc138\uc9c0 \ucc98\ub9ac<\/p>\n\n\n\n<p>1)app\/api\/v1\/instance\/rules\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>import { NextResponse } from 'next\/server';\nexport async function GET() {\n  return NextResponse.json(&#91;], { headers: { 'Access-Control-Allow-Origin': '*' } });\n}<\/code><\/pre>\n\n\n\n<p>2)app\/api\/v1\/instance\/domain_blocks\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>app\/api\/v1\/instance\/rules\/route.ts \ucf54\ub4dc\uc640 \ub3d9\uc77c\nIdentical to the code in app\/api\/v1\/instance\/rules\/route.ts<\/code><\/pre>\n\n\n\n<p>3)app\/oauth\/revoke\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<p>A.Tusky \ub85c\uadf8\uc544\uc6c3 \ub54c \ud638\ucd9c \/ Add route (called upon Tusky logout)<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>import { NextResponse } from 'next\/server';\nimport db from '@\/lib\/db';\n\nexport async function POST(req: Request) {\n  try {\n    const body = await req.json().catch(() =&gt; ({})) as any;\n    const token = body.token;\n    if (token) {\n      try {\n        db.prepare(`DELETE FROM oauth_tokens WHERE access_token=?`).run(token);\n        db.prepare(`DELETE FROM sessions WHERE access_token=?`).run(token);\n      } catch {}\n    }\n  } catch {}\n  return NextResponse.json({});\n}\nexport async function GET() { return NextResponse.json({}); }<\/code><\/pre>\n\n\n\n<p>4)app\/api\/v2\/notifications\/policy\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>import { NextResponse } from 'next\/server';\nexport async function GET() {\n  return NextResponse.json({ for_not_following: 'accept', for_not_followers: 'accept', for_new_accounts: 'accept', for_private_mentions: 'accept', for_limited_accounts: 'accept' });\n}<\/code><\/pre>\n\n\n\n<p>5)public\/icon.png &#8211; \ud30c\uc77c\ucd94\uac00 \/ add file<\/p>\n\n\n\n<p>6) app\/api\/v1\/filters\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>import { NextResponse } from 'next\/server';\nexport async function GET() {\n  return NextResponse.json(&#91;], { headers: { 'Access-Control-Allow-Origin': '*' } });\n}<\/code><\/pre>\n\n\n\n<p>7)app\/api\/v1\/lists\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>app\/api\/v1\/instance\/rules\/route.ts \ucf54\ub4dc\uc640 \ub3d9\uc77c\nIdentical to the code in app\/api\/v1\/instance\/rules\/route.ts<\/code><\/pre>\n\n\n\n<p>8)app\/api\/v1\/announcements\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app\/api\/v1\/instance\/rules\/route.ts \ucf54\ub4dc\uc640 \ub3d9\uc77c\nIdentical to the code in app\/api\/v1\/instance\/rules\/route.ts<\/code><\/pre>\n\n\n\n<p>9)app\/api\/v1\/timelines\/public\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app\/api\/v1\/instance\/rules\/route.ts \ucf54\ub4dc\uc640 \ub3d9\uc77c\nIdentical to the code in app\/api\/v1\/instance\/rules\/route.ts<\/code><\/pre>\n\n\n\n<p>10)app\/api\/v1\/notifications\/route.ts &#8211; \ucf54\ub4dc\uc218\uc815 \/ Code Modification<\/p>\n\n\n\n<p>A.\ub77c\uc6b0\ud2b8 \ubb34\ud55c \ud638\ucd9c \uba48\ucd94\uae30 \/ Stop infinite route calls<\/p>\n\n\n\n<p>B. \/api\/v1\/notifications  \ubb34\ud55c \ud638\ucd9c \uc5d0\ub7ec \uba54\uc138\uc9c0<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>likes table rows=0 for user1\n\ud83d\udd14 notifications return=2 follow=2 fav=0 reblog=0\n GET \/api\/v1\/notifications?max_id=follow_1_https%3A%2F%2Ffreelifemakers.com%2Fusers%2Fuser1%2Ffollow%2F01HW187GFP316GG213ASB2DG3T&amp;limit=30 200 in 16ms (next.js: 13ms, proxy.ts: 0.8ms, application-code: 3ms)\n GET \/icon.png 404 in 25ms (next.js: 16ms, application-code: 10ms)\n\u2764 likes table rows=0 for user1\n\ud83d\udd14 notifications return=2 follow=2 fav=0 reblog=0\n GET \/api\/v1\/notifications?limit=30 200 in 4ms (next.js: 0.9ms, proxy.ts: 1.0ms, application-code: 1.7ms)\n\u2764 likes table rows=0 for user1<\/code><\/pre>\n\n\n\n<p>C. \ucf54\ub4dc \/ Code<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>... ...\nexport async function GET(req: Request) {\n... ...\n    \/\/ \u2705 myapp55 - Tusky\uac00 max_id\ub85c \ub2e4\uc74c \ud398\uc774\uc9c0 \ub2ec\ub77c\uace0 \ud558\uba74 \ube48 \ubc30\uc5f4 \ub9ac\ud134 = \ub05d\n    \/\/ Tusky\uac00 max_id\ub85c \ub2e4\uc74c \ud398\uc774\uc9c0 \ub2ec\ub77c\uace0 \ud558\uba74 \ube48 \ubc30\uc5f4 \ub9ac\ud134 = \ub05d\n    \/\/ Tursky requests the next page with max_id, return an empty array = end\n    const max_id = searchParams.get('max_id'); \n    if (max_id) {\n      return NextResponse.json(&#91;], {\n        headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application\/json' }\n      });\n    }\n... ...\n}\n... ...<\/code><\/pre>\n\n\n\n<p>11) app\/api\/v1\/markers\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>import { NextResponse } from 'next\/server';\n\nexport async function GET() {\n  return NextResponse.json({\n    notifications: { last_read_id: \"0\", version: 1, updated_at: new Date().toISOString() },\n    home: { last_read_id: \"0\", version: 1, updated_at: new Date().toISOString() }\n  }, { headers: { 'Access-Control-Allow-Origin': '*' } });\n}\n\nexport async function POST(req: Request) {\n  const body = await req.json().catch(()=&gt;({}));\n  return NextResponse.json(body, { headers: { 'Access-Control-Allow-Origin': '*' } });\n}<\/code><\/pre>\n\n\n\n<p>12)app\/api\/v1\/timelines\/home\/route.ts &#8211; \ucf54\ub4dc\uc218\uc815 \/ code modification<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>    \/\/ \u2705 myapp55 - \uacc4\uc815 \uc815\ubcf4 \uad6c\uc131 \/ Account information configuration\n    const baseAccount = isMy\n    ? { \n        id: '1', username, acct: `${username}@${DOMAIN}`, display_name: username,\n        avatar: `https:\/\/${DOMAIN}\/icon.png`,\n        avatar_static: `https:\/\/${DOMAIN}\/icon.png`,\n        header: `https:\/\/${DOMAIN}\/header.png`,\n        header_static: `https:\/\/${DOMAIN}\/header.png`,\n      }\n    : (() =&gt; {\n        let uname = 'user1', dom = 'freelifemakers.com', display = 'user1';\n        if (row.actor?.includes('mastodon.social')) { uname = 'freelifemakers'; dom = 'mastodon.social'; display = 'freelifemakers'; }\n        return { \n          id: `remote_${dom.replace(\/\\.\/g,'_')}_${uname}`, \n          username: uname, acct: `${uname}@${dom}`, display_name: display,\n          avatar: `https:\/\/${DOMAIN}\/icon.png`,\n          avatar_static: `https:\/\/${DOMAIN}\/icon.png`,\n          header: `https:\/\/${DOMAIN}\/header.png`,\n          header_static: `https:\/\/${DOMAIN}\/header.png`,\n        };\n      })();\n\n    return {\n      id: String(row.id),\n      uri: longId,\n      url: longId,\n      account: baseAccount,\n      content: content.startsWith('&lt;')? content : `&lt;p&gt;${content}&lt;\/p&gt;`,\n      created_at: new Date(parseDate(row.created_at)).toISOString(),\n      visibility: 'public',\n      reblogs_count,      \/\/ \uc804\uccb4 \ubd80\uc2a4\ud2b8 \uce74\uc6b4\ud2b8 \/ Full boost count\n      favourites_count,   \/\/ \uc804\uccb4 \uc88b\uc544\uc694 \uce74\uc6b4\ud2b8 \/ Full like count \n      replies_count: 0,\n      favourited: !!favourited, \/\/ \uc804\uccb4 \uc88b\uc544\uc694 \/ Full like\n      reblogged: !!reblogged,   \/\/ \uc804\uccb4 \ubd80\uc2a4\ud2b8 \/ Full Boost\n      muted: false, bookmarked: false, pinned: false,\n      \/\/ \u2705 myapp55 - Tusky \ud544\uc218 \/ Required for Tusky \n      sensitive: false, spoiler_text: '',\n      media_attachments: &#91;],\n      mentions: &#91;],\n      tags: &#91;],\n      emojis: &#91;],\n      card: null,\n      poll: null,\n      reblog: null,\n    };<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<p>\u2714\ufe0f \uc11c\ubc84\ub85c\uadf8\uc5d0\uc11c 404 \uc5d0\ub7ec \ud655\uc778 \/ Check for 404 errors in the server logs.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"710\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-serverlog-jpg-1024x710.jpg\" alt=\"\" class=\"wp-image-11548\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-serverlog-jpg-1024x710.jpg 1024w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-serverlog-jpg-300x208.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-serverlog-jpg-768x532.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-serverlog-jpg-400x277.jpg 400w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-serverlog-jpg-800x555.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-serverlog-jpg.jpg 1278w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Server log<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f \uc678\ubd80 \uc571 \ub85c\uadf8\uc778\ud3fc \/ External App Login Form<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"461\" height=\"1024\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform-jpg-461x1024.jpg\" alt=\"\" class=\"wp-image-11549\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform-jpg-461x1024.jpg 461w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform-jpg-135x300.jpg 135w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform-jpg-400x889.jpg 400w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform-jpg.jpg 500w\" sizes=\"auto, (max-width: 461px) 100vw, 461px\" \/><\/figure>\n\n\n\n<p>\u2714\ufe0f \uc678\ubd80 \uc571 \ub85c\uadf8\uc778 \ud3fc &#8211; \ube44\ubc00\ubc88\ud638 \ud2c0\ub9b0\uacbd\uc6b0 \/ External App Login Form \u2013 Wrong Password<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"461\" height=\"1024\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform2-jpg-461x1024.jpg\" alt=\"\" class=\"wp-image-11550\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform2-jpg-461x1024.jpg 461w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform2-jpg-135x300.jpg 135w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform2-jpg-400x889.jpg 400w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-loginform2-jpg.jpg 500w\" sizes=\"auto, (max-width: 461px) 100vw, 461px\" \/><\/figure>\n\n\n\n<p>\u2714\ufe0f Tusky\uc571 \ub85c\uadf8\uc778 \ubc0f \ud504\ub85c\ud544 \ud655\uc778 \/ Logging into the Tusky app and checking your profile<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"461\" height=\"1024\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-avatar-jpg-461x1024.jpg\" alt=\"\" class=\"wp-image-11552\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-avatar-jpg-461x1024.jpg 461w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-avatar-jpg-135x300.jpg 135w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-avatar-jpg-400x889.jpg 400w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp55-Tusky-avatar-jpg.jpg 500w\" sizes=\"auto, (max-width: 461px) 100vw, 461px\" \/><\/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 myapp45\uc5d0\uc11c\ub294 Tursky\ub85c\uadf8\uc778 \ubd80\ubd84\uc744 \uad6c\ud604\ud569\ub2c8\ub2e4.In myapp45, the Tursky login functionality is implemented. \ud83d\udc49\ud83c\udffb \uc6f9\uc774\ub098 pinafore\uc5d0\uc11c \uc815\uc0c1\uc801\uc73c\ub85c \ubaa8\ub4e0\ub370\uc774\ud130\uac00 \ud45c\uc2dc\ub429\ub2c8\ub2e4. All data is displayed correctly on the web or in Pinafore. \ud83d\udc49\ud83c\udffb \ud558\uc9c0\ub9cc Tusky\uc571 \ud0c0\uc784\ub77c\uc778\uc5d0 \ub370\uc774\ud130\uac00 \uc815\uc0c1\uc801\uc73c\ub85c \ud45c\uc2dc\ub418\uc9c0 \uc54a\ub294 \ubb38\uc81c\uac00 \uc788\uc2b5\ub2c8\ub2e4.However, there is an issue where data is not displaying correctly on the Tusky app&#8217;s timeline. \ud83d\udc49\ud83c\udffb [&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-11463","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\/11463","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=11463"}],"version-history":[{"count":86,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/11463\/revisions"}],"predecessor-version":[{"id":11555,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/11463\/revisions\/11555"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=11463"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=11463"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=11463"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}