{"id":11651,"date":"2026-10-06T07:53:35","date_gmt":"2026-10-05T22:53:35","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=11651"},"modified":"2026-10-06T14:17:42","modified_gmt":"2026-10-06T05:17:42","slug":"nextjs-sns-server-47","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/10\/06\/nextjs-sns-server-47\/","title":{"rendered":"[nextjs]SNS Server-47(myapp57)\u00a0"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb myapp57 \uc740 \uae30\uc874 sns\ud3ec\uc2a4\ud2b8\uc5d0 \ub313\uae00 \uae30\ub2a5\uc744 \ucd94\uac00\ud569\ub2c8\ub2e4.<br>myapp57 adds a commenting feature to existing social media posts.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ud2b8\ub9ac \ud615\ud0dc\ub85c \uae00 \ud558\ub2e8\uc5d0 \ubb36\uc774\ub294 \uad6c\uc870\uac00 \uc544\ub2c8\ub77c \uc0c8\uae00 \ucc98\ub7fc \ub728\uace0 \uc6d0\ubcf8\uae00\uc5d0 \ucd94\uac00\ub418\ub294 \uae00\uc744 \ud45c\uc2dc\ud558\uae30 \uc704\ud574\uc11c @userid post\ud615\uc2dd\uc744 \ud45c\uc2dc\ub418\ub294 \uae30\ub2a5\uc785\ub2c8\ub2e4.<br>Instead of being grouped in a tree structure at the bottom of the post, this feature displays the content in the <code>@userid post<\/code> format, making it appear like a new post while indicating that it is an addition to the original post.<\/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  -> 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(deprecated)\n\u2502   \u251c\u2500\u2500 api\/timeline\/route.ts  -> Timeline API\n\u2502   \u251c\u2500\u2500 api\/followinglist\/route.ts  -> followinglist API\n\u2502   \u251c\u2500\u2500 api\/followerslist\/route.ts  -> 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 -> 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 api\/v1\/media\/route.ts -> Image upload\n\u2502   \u251c\u2500\u2500 api\/v1\/media\/&#91;id]\/route.ts  -> Image Delete \n\u2502   \u251c\u2500\u2500 api\/v1\/instance\/rules\/route.ts -> Feature not implemented.\n\u2502   \u251c\u2500\u2500 api\/v1\/instance\/domain_blocks\/route.ts -> Feature not implemented.\n\u2502   \u251c\u2500\u2500 api\/v1\/filters\/route.ts -> Feature not implemented.\n\u2502   \u251c\u2500\u2500 api\/v1\/lists\/route.ts -> Feature not implemented.\n\u2502   \u251c\u2500\u2500 api\/v1\/announcements\/route.ts -> Feature not implemented.\n\u2502   \u251c\u2500\u2500 api\/v1\/timelines\/public\/route.ts -> Feature not implemented.\n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/statuses\/&#91;id]\/context\/route.ts -> Post Check,Delete<\/strong>\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 <span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">oauth\/login-and-authorize\/route.ts<\/span> -> Tursky,Pinafore login processing\n\u2502   \u251c\u2500\u2500 oauth\/revoke\/route.ts -> Feature not implemented.\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> -> 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> -> search\n\u2502   \u251c\u2500\u2500 api\/v2\/notifications\/policy\/route.ts-> Feature not implemented.\n\u2502   \u251c\u2500\u2500 api\/v1\/accounts\/&#91;id]\/followers\/route.ts -> followers\n\u2502   \u251c\u2500\u2500 api\/v1\/accounts\/&#91;id]\/following\/route.ts -> following\n\u2502   \u251c\u2500\u2500 api\/v1\/statuses\/&#91;id]\/route.ts -> -> Post delete(pinafore)\n\u2502   \u251c\u2500\u2500 api\/auth\/signup\/route.ts        -> Website Signup\n\u2502   \u251c\u2500\u2500 api\/auth\/login\/route.ts         -> Website Login\n\u2502   \u251c\u2500\u2500<strong> <\/strong>api\/auth\/logout\/route.ts        -> Website Logout\n\u2502   \u251c\u2500\u2500<strong> <\/strong>api\/auth\/refresh\/route.ts       -> Refresh Token\n\u2502   \u251c\u2500\u2500<strong> <\/strong>api\/cleanup-orphan\/route.ts     -> post,outbox clean up\n\u2502   \u251c\u2500\u2500<strong> <\/strong>api\/auth\/me\/route.tsx           -> Login Check<strong>\n<\/strong>\u2502   \u251c\u2500\u2500 auth\/signup\/page.tsx            -> Signup UI\n\u2502   \u251c\u2500\u2500 auth\/signup\/page.tsx            -> Login UI        \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(Server, 404)\n\u2502   \u2502   \u251c\u2500\u2500 ClientPage.tsx.        -> Timeline UI(Client,Call Active Themes)\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 pinafore\/FollowersList.tsx -> Theme 1, FollowersList\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/FollowingList.tsx -> Theme 1, FollowingList\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/NotificationsList.tsx -> Theme 1, NotificationsList\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/TimelineList.tsx  -> Theme 1, TimelineList\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   \u251c\u2500\u2500 visibility.ts          -> Visibility(Public,Unlisted,Private,Direct)\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=\"YFxHMMC8Zw\"><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=aN5rcD5iDg#?secret=YFxHMMC8Zw\" data-secret=\"YFxHMMC8Zw\" 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 \ud14c\uc774\ube14 \uc218\uc815 \/ Modify Table<\/p>\n\n\n\n<p>\u2714\ufe0f user \ud14c\uc774\ube14 &#8211; \ubc84\uadf8\uc218\uc815\uc6a9<br>user table &#8211; for bug fixes<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code># \uceec\ub7fc\ucd94\uac00\nALTER TABLE users ADD COLUMN mastodon_id TEXT;\nUPDATE users SET mastodon_id = lower(hex(randomblob(4))) WHERE mastodon_id IS NULL;\n\n# \uc2a4\ud0a4\ub9c8\nsqlite&gt; .schema users\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  email TEXT UNIQUE,\n  password_hash TEXT,\n  email_verified INTEGER DEFAULT 0,\n  verification_token TEXT,\n  created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n, <strong>mastodon_id TEXT<\/strong>);\nsqlite&gt; \n<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f posts, inbox_posts \ud14c\uc774\ube14\uc5d0 reply \uae30\ub2a5 \ucd94\uac00 \/ Added reply functionality to the <code>posts<\/code> and <code>inbox_posts<\/code> tables.<\/p>\n\n\n\n<p> &#8212; in_reply_to_id , in_reply_to_username<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code># \ud14c\uc774\ube14 \uc218\uc815 - \ud130\ubbf8\ub110\uc5d0\uc11c \uc2e4\ud589 \/ Modify table \u2013 Run in the terminal\n\nALTER TABLE posts ADD COLUMN in_reply_to_id TEXT;\nALTER TABLE posts ADD COLUMN in_reply_to_username TEXT;\n\nALTER TABLE inbox_posts ADD COLUMN in_reply_to_id TEXT;\nALTER TABLE inbox_posts ADD COLUMN in_reply_to_username TEXT;\n\nALTER TABLE posts ADD COLUMN in_reply_to_account_id TEXT;\nALTER TABLE inbox_posts ADD COLUMN in_reply_to_account_id TEXT;\n\nCREATE INDEX idx_posts_reply ON posts(in_reply_to_id);\nCREATE INDEX idx_inbox_reply ON inbox_posts(in_reply_to_id);\n\nCREATE INDEX idx_posts_reply_account ON posts(in_reply_to_account_id);\nCREATE INDEX idx_inbox_reply_account ON inbox_posts(in_reply_to_account_id);\n\n.schema posts\n\n#----------------\n\n# \uc2e4\ud589\uacb0\uacfc \/ Execution Result\n\n<strong>sqlite> .schema posts<\/strong>\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', visibility TEXT DEFAULT 'public' CHECK(visibility IN ('public','unlisted','private','direct')), original_id TEXT, sensitive INTEGER DEFAULT 0, in_reply_to_id TEXT, in_reply_to_username TEXT);\nCREATE INDEX idx_posts_username_created ON posts(username, created_at DESC);\nCREATE INDEX idx_posts_visibility ON posts(visibility);\nCREATE INDEX idx_posts_reply ON posts(in_reply_to_id);\n\n<strong>sqlite> .schema inbox_posts<\/strong>\nCREATE 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, visibility TEXT DEFAULT 'public' CHECK(visibility IN ('public','unlisted','private','direct')), type TEXT DEFAULT 'Note', sensitive INTEGER DEFAULT 0, in_reply_to_id TEXT, in_reply_to_username TEXT);\nCREATE INDEX idx_inbox_username_created ON inbox_posts(username, created_at DESC);\nCREATE INDEX idx_inbox_actor ON inbox_posts(actor);\nCREATE INDEX idx_inbox_visibility ON inbox_posts(visibility);\nCREATE INDEX idx_inbox_reply ON inbox_posts(in_reply_to_id);\n<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \ubc84\uadf8\uc218\uc815 \/ Bug fix<\/p>\n\n\n\n<p>\u2714\ufe0f api\/v1\/statuses\/route.ts<\/p>\n\n\n\n<p>&#8212; <code>API\uc5d0\uc11c account : { id: '1' ? '2'<\/code> &#8230; } \ud558\ub4dc\ucf54\ub529 \ubd80\ubd84 \uc81c\uac70 -> mastodon_id\ub85c \uc218\uc815<br>Removed the hardcoded <code>account: { id: '1' ? '2' ... }<\/code> section in the API and updated it to use <code>mastodon_id<\/code>.<\/p>\n\n\n\n<p>&#8212; \ubaa8\ub4e0 \uae00\uc758 id\uac00 1\ubc88\uacfc 2\ubc88 \ubc16\uc5d0 \uc5c6\ub294 \ubb38\uc81c \ubc1c\uc0dd<br>An issue arose where the IDs for all posts were limited to 1 and 2.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>      account: {\n      <strong>  \/\/id: username === 'user1' ? '1' : '2',\n        id: mastodonId, \/\/ \u2705 myapp57<\/strong>\n        username: username,\n        acct: `${username}@${DOMAIN}`,\n        display_name: username,\n        avatar: `https:\/\/${DOMAIN}\/icon.png`\n      },<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f lib\/auth.ts<\/p>\n\n\n\n<p>&#8212; mastodon_id \uceec\ub7fc \ucd94\uac00 \/ Add the mastodon_id column.<\/p>\n\n\n\n<p>&#8212; randomUUID().slice(0,8) \ub85c \uace0\uc720 id \uc0dd\uc131<br>Generate a unique ID using randomUUID().slice(0,8)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export function createUser(username: string, email: string, passwordHash: string, displayName: string = '') {\n  const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {\n    modulusLength: 2048,\n    publicKeyEncoding: { type: 'spki', format: 'pem' },\n    privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n  });\n\n  const userKeyDir = path.join(process.cwd(), `data\/keys\/${username}`);\n  if (!fs.existsSync(userKeyDir)) fs.mkdirSync(userKeyDir, { recursive: true });\n\n  fs.writeFileSync(path.join(userKeyDir, 'private.pem'), privateKey);\n  fs.writeFileSync(path.join(userKeyDir, 'public.pem'), publicKey);\n  console.log(`\u2705 \ud0a4 \ud30c\uc77c \uc800\uc7a5\/save key file: data\/keys\/${username}\/`);\n\n  \/\/ return db.prepare(`\n  \/\/   INSERT INTO users (username, display_name, summary, private_key, public_key, email, password_hash, email_verified, created_at)\n  \/\/   VALUES (?,?, '',?,?,?,?, 1, CURRENT_TIMESTAMP)\n  \/\/ `).run(username, displayName || username, privateKey, publicKey, email, passwordHash);\n\n  \/\/ \u2705 myapp58 - mastodon_id \uc0dd\uc131 \/ create mastodon_id\n  <strong>const mastodonId = crypto.randomUUID().slice(0, 8); <\/strong>\n\n  return db.prepare(`\n    INSERT INTO users (username, display_name, summary, private_key, public_key, email, password_hash, email_verified, mastodon_id, created_at)\n    VALUES (?,?, '',?,?,?,?, 1, ?, CURRENT_TIMESTAMP)\n  `).run(username, displayName || username, privateKey, publicKey, email, passwordHash, <strong>mastodonId<\/strong>);\n\n}<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/timelines\/home\/route.ts<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>export function createUser(username: string, email: string, passwordHash: string, displayName: string = '') {\n  const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {\n    modulusLength: 2048,\n    publicKeyEncoding: { type: 'spki', format: 'pem' },\n    privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n  });\n\n  const userKeyDir = path.join(process.cwd(), `data\/keys\/${username}`);\n  if (!fs.existsSync(userKeyDir)) fs.mkdirSync(userKeyDir, { recursive: true });\n\n  fs.writeFileSync(path.join(userKeyDir, 'private.pem'), privateKey);\n  fs.writeFileSync(path.join(userKeyDir, 'public.pem'), publicKey);\n  console.log(`\u2705 \ud0a4 \ud30c\uc77c \uc800\uc7a5\/save key file: data\/keys\/${username}\/`);\n\n  \/\/ return db.prepare(`\n  \/\/   INSERT INTO users (username, display_name, summary, private_key, public_key, email, password_hash, email_verified, created_at)\n  \/\/   VALUES (?,?, '',?,?,?,?, 1, CURRENT_TIMESTAMP)\n  \/\/ `).run(username, displayName || username, privateKey, publicKey, email, passwordHash);\n\n  \/\/ \u2705 myapp58 - mastodon_id \uc0dd\uc131 \/ create mastodon_id\n <strong> const mastodonId = crypto.randomUUID().slice(0, 8); <\/strong>\n\n  return db.prepare(`\n    INSERT INTO users (username, display_name, summary, private_key, public_key, email, password_hash, email_verified, mastodon_id, created_at)\n    VALUES (?,?, '',?,?,?,?, 1, ?, CURRENT_TIMESTAMP)\n  `).run(username, displayName || username, privateKey, publicKey, email, passwordHash, <strong>mastodonId<\/strong>);\n\n}<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/accounts\/verify_credentials\/route.ts<\/p>\n\n\n\n<p>\ud83d\udca1 verify_credentials\/route.ts \ub77c\uc6b0\ud2b8\uc640 v1\/statuses\/[id]\/route.ts\uc758 id\uac00 \uac19\uc544\uc57c pinafore\uc5d0\uc11c delete \uba54\ub274\ub97c \ubcfc \uc218 \uc788\uc74c.<br>The id in verify_credentials\/route.ts and v1\/statuses\/[id]\/route.ts must match for the delete menu to appear in Pinafore.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>... ... \n\/\/ \u2705 myapp57 - getUser\ud568\uc218\ub85c \uacc4\uc815 \ud655\uc778 \/ Verify the account using the `getUser` function.\nfunction getUser(req: Request) {\n  const auth = req.headers.get('Authorization')?.replace('Bearer ','').trim();\n  if (auth) {\n    \/\/ 1. oauth_tokens\uc5d0\uc11c access_token \uac80\uc0c9\n    \/\/ Retrieve access_token from oauth_tokens\n    const o = db.prepare('SELECT username FROM oauth_tokens WHERE access_token=?').get(auth) as any;\n    if (o?.username) return o.username;\n    \/\/ 2. sessions\uc5d0\uc11c refresh_token\ub3c4 \uac80\uc0c9\n    \/\/ Also search for refresh_token in sessions\n    const s = db.prepare('SELECT username FROM sessions WHERE refresh_token=?').get(auth) as any;\n    if (s?.username) return s.username;\n  }\n  const cookie = req.headers.get('cookie') || '';\n  const m = cookie.match(\/refresh_token=(&#91;^;]+)\/);\n  if (m) {\n    const s = db.prepare('SELECT username FROM sessions WHERE refresh_token=?').get(m&#91;1]) as any;\n    if (s?.username) return s.username;\n  }\n  return null;\n}\n\nexport async function GET(req: Request) {\n  \/\/ \u2705 Mastodon \ud615\uc2dd\uc73c\ub85c \ub9ac\ud134 \/ Return in Mastodon format\n  return NextResponse.json({\n    id:  mastodonId, \/\/ \u2705 myapp57\n    username: username,\n    acct: `${username}@${DOMAIN}`,\n    display_name: username,\n    locked: false,\n    bot: false,\n    created_at: new Date().toISOString(),\n    note: 'My ActivityPub server!',\n    url: `https:\/\/${DOMAIN}\/users\/${username}`,\n    avatar: `https:\/\/${DOMAIN}\/icon.png`,\n    avatar_static: `https:\/\/${DOMAIN}\/icon.png`,\n    header: `https:\/\/${DOMAIN}\/icon.png`,\n    header_static: `https:\/\/${DOMAIN}\/icon.png`,\n    followers_count: 1,\n    following_count: 1,\n    statuses_count: 100,\n    source: {\n      privacy: 'public',\n      sensitive: false,\n      language: 'en',\n      note: '',\n      fields: &#91;]\n    }\n  });\n}<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f users\ud14c\uc774\ube14\uc758 mastodon_id\uc778 \ubd80\ubd84\uc758 \ub370\uc774\ud130 \uc5c5\ub370\uc774\ud2b8 \uc2e4\ud589<br>Execute a data update for the <code>mastodon_id<\/code> field in the <code>users<\/code> table.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>sqlite3 data.sqlite \"UPDATE users SET mastodon_id = lower(hex(randomblob(4))) WHERE mastodon_id IS NULL;\"<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/timelines\/home\/route.ts<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>\/\/ \u2705 myapp48 - visibility \uacf5\ud1b5 \ud5ec\ud37c import \ucd94\uac00 \/ Added import for common visibility helper\nimport { getViewerFromRequest, canSee } from '@\/lib\/visibility';\n... ...\nexport async function GET(req: Request) {\n... ...\n\n  \/\/ \u2705 myapp57 - \ub0b4 mastodon_id \uc870\ud68c \/ select my mastodoin_id\n  const me = db.prepare('SELECT mastodon_id FROM users WHERE username=?').get(viewer) as any;\n  const myMastodonId = me?.mastodon_id || viewer;\n... ...\n  \/\/ \u2705 myapp55 + myapp57\n  if (filteredRows.length === 0) {\n    \/\/ const fallback = db.prepare(`SELECT * FROM posts ORDER BY created_at DESC LIMIT 20`).all() as any&#91;];\n    \/\/ console.log(`HOME fallback=${fallback.length}`);\n\n    const fallback = db.prepare(`\n      SELECT * FROM posts \n      WHERE visibility IN ('public','unlisted')\n      ORDER BY created_at DESC LIMIT 20\n    `).all() as any&#91;];\n    filteredRows = fallback;\n  }\n... ...\n    \/\/ \u2705 myapp57 - \uacc4\uc815 \uc815\ubcf4 mastodon_id\ub85c \uc124\uc815 \/ Set account information to mastodon_id\n    const isMy = myPosts.some(p => p.id === row.id);\n    let accountId = myMastodonId; \n    let accountUsername = viewer; \n... ...\n    const baseAccount = isMy\n    ? { \n        id: accountId, \/\/\u2705 myapp57\n        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    : (() => { ... }\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>${content}&lt;\/p>`,\n      created_at: new Date(parseDate(row.created_at)).toISOString(),\n      visibility: row.visibility || 'public', \/\/ \u2705 myapp57\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    };\n}<\/code><\/pre>\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 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \uc218\uc815\ub418\uc5c8\uae30 \ub54c\ubb38\uc5d0 \uad00\ub828\ub41c \ubd80\ubd84\uc744 \ubaa8\ub450 \uc218\uc815\ud569\ub2c8\ub2e4.<br>Since the database has been modified, all related parts are being updated.<\/p>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/statuses\/route.ts &#8211; \uae00\uc4f0\uae30 API \/ Writin post API<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>... ...\n\nexport async function POST(req: Request) { \n\n... ...\n    \/\/ \u2705 myapp57 - Add Reply\n    const inReplyToId = body.in_reply_to_id || body.inReplyToId || null;\n    const inReplyToUsername = body.in_reply_to_username || body.inReplyToUsername || null;\n\n... ...\n\n    \/\/ \u2705 myapp57 - posts \uc800\uc7a5\uc5d0 reply \uceec\ub7fc \ucd94\uac00 \/ Added reply column to posts storage\n    try {\n      db.prepare(`INSERT INTO posts (id, content, created_at, username, visibility, in_reply_to_id, in_reply_to_username) VALUES (?,?,?,?,?,?,?)`)\n        .run(id, content, now, username, visibility, inReplyToId, inReplyToUsername);\n    } catch (e) {\n      console.error('posts \uc800\uc7a5 \uc5d0\ub7ec \/ posts save error', e);\n    }\n... ...\n   \/\/ \u2705 myapp48 + myapp51 + myapp57 \n   \/\/ - any\ub85c \ud574\uc57c inReplyTo \ucd94\uac00 \uac00\ub2a5 \/ you need to add \"any\" type so then you can to use \"ReplyTo\"\n    const note: any = {\n      id: `https:\/\/${DOMAIN}\/users\/${username}\/statuses\/${id}`,\n      type: 'Note',\n      content: `&lt;p&gt;${content}&lt;\/p&gt;`,\n      attributedTo: `https:\/\/${DOMAIN}\/users\/${username}`,\n      published: new Date(now).toISOString(),\n      to,\n      cc,\n      attachment: attachments.map(m =&gt; ({\n        type: 'Document',\n        mediaType: m.type,\n        url: toAbsoluteUrl(m.url)\n      }))\n    };\n\n}<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/statuses\/[id]\/route.ts <\/p>\n\n\n\n<p>&#8212; \uae30\uc874 \ub77c\uc6b0\ud2b8\uc5d0 GET \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add a GET route to the existing route.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>\/\/ \u2705 myapp57 - Pinafore\uc5d0\uc11c \ub0b4 \uae00\uc774\uba74 DELET E\ubcf4\uc5ec\uc90c \/ In Pinafore, it shows \"DELETE\" for my own posts.\nexport async function GET(\n  req: Request,\n  { params }: { params: Promise&lt;{ id: string }> }\n) {\n  const { id } = await params;\n\n  const row = db.prepare(`\n    SELECT p.*, u.mastodon_id FROM posts p \n    LEFT JOIN users u ON p.username=u.username \n    WHERE p.id=? \n  `).get(id) as any;\n\n  if (!row) {\n    return NextResponse.json({ error: 'not found' }, { status: 404, headers: { 'Access-Control-Allow-Origin': '*' } });\n  }\n\n  return NextResponse.json({\n    id: String(row.id),\n    uri: `https:\/\/${DOMAIN}\/users\/${row.username}\/statuses\/${row.id}`,\n    url: `https:\/\/${DOMAIN}\/users\/${row.username}\/statuses\/${row.id}`,\n    created_at: new Date(row.created_at).toISOString(),\n    content: row.content?.startsWith('&lt;') ? row.content : `&lt;p>${row.content}&lt;\/p>`,\n    account: {\n      id: String(row.mastodon_id || row.username), \/\/ \u2705 \uc5ec\uae30\uac00 verify_credentials\ub791 \uac19\uc544\uc57c Delete \uc0dd\uae40 \/ The 'Delete' option only appears if this matches `verify_credentials`.\n      username: row.username,\n      acct: `${row.username}@${DOMAIN}`,\n      display_name: row.username,\n      avatar: `https:\/\/${DOMAIN}\/icon.png`,\n      avatar_static: `https:\/\/${DOMAIN}\/icon.png`,\n    },\n    visibility: row.visibility || 'public',\n    in_reply_to_id: row.in_reply_to_id || null,\n    reblogs_count: 0, favourites_count: 0, replies_count: 0,\n    favourited: false, reblogged: false, muted: false,\n    sensitive: false, spoiler_text: '', \n    media_attachments: &#91;],\n    mentions: &#91;], tags: &#91;], emojis: &#91;],\n  }, { headers: { 'Access-Control-Allow-Origin': '*' } });\n}<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/statuses\/[id]\/context\/route.ts &#8211; \ub77c\uc6b0\ud2b8 \ucd94\uac00 \/ Add route &#8211; Pinafore<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>... ...\n\/\/ \u2705 myapp57 - Pinafore\uc5d0\uc11c \ub0b4 \uae00\uc774\uba74 DELET E\ubcf4\uc5ec\uc90c \/ In Pinafore, it shows \"DELETE\" for my own posts.\nexport async function GET(\n  req: Request,\n  { params }: { params: Promise&lt;{ id: string }> }\n) {\n  const { id } = await params;\n\n  const row = db.prepare(`\n    SELECT p.*, u.mastodon_id FROM posts p \n    LEFT JOIN users u ON p.username=u.username \n    WHERE p.id=? \n  `).get(id) as any;\n\n  if (!row) {\n    return NextResponse.json({ error: 'not found' }, { status: 404, headers: { 'Access-Control-Allow-Origin': '*' } });\n  }\n\n  return NextResponse.json({\n    id: String(row.id),\n    uri: `https:\/\/${DOMAIN}\/users\/${row.username}\/statuses\/${row.id}`,\n    url: `https:\/\/${DOMAIN}\/users\/${row.username}\/statuses\/${row.id}`,\n    created_at: new Date(row.created_at).toISOString(),\n    content: row.content?.startsWith('&lt;') ? row.content : `&lt;p>${row.content}&lt;\/p>`,\n    account: {\n      id: String(row.mastodon_id || row.username), \/\/ \u2705 \uc5ec\uae30\uac00 verify_credentials\ub791 \uac19\uc544\uc57c Delete \uc0dd\uae40 \/ The 'Delete' option only appears if this matches `verify_credentials`.\n      username: row.username,\n      acct: `${row.username}@${DOMAIN}`,\n      display_name: row.username,\n      avatar: `https:\/\/${DOMAIN}\/icon.png`,\n      avatar_static: `https:\/\/${DOMAIN}\/icon.png`,\n    },\n    visibility: row.visibility || 'public',\n    in_reply_to_id: row.in_reply_to_id || null,\n    reblogs_count: 0, favourites_count: 0, replies_count: 0,\n    favourited: false, reblogged: false, muted: false,\n    sensitive: false, spoiler_text: '', \n    media_attachments: &#91;],\n    mentions: &#91;], tags: &#91;], emojis: &#91;],\n  }, { headers: { 'Access-Control-Allow-Origin': '*' } });\n}\n... ...<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/statuses\/[id]\/route.ts<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code> export async function GET(req: Request) { ... ...  \n    return {\n      id: String(row.id),\n      uri: longId,\n      url: longId,\n      account: baseAccount,\n      content: content.startsWith('&lt;')? content : `&lt;p>${content}&lt;\/p>`,\n      created_at: new Date(parseDate(row.created_at)).toISOString(),\n      visibility: row.visibility || 'public', \/\/ \u2705 myapp57\n      \/\/ \u2705 myapp57 - reply \ud544\ub4dc \ucd94\uac00 \/ Add reply fileds \n<strong>      in_reply_to_id: row.in_reply_to_id ? String(row.in_reply_to_id) : null,\n      in_reply_to_account_id: row.in_reply_to_account_id ? String(row.in_reply_to_account_id) : null,<\/strong>\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    };\n... ... }<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/timelines\/home\/route.ts<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>  ... ...  \n  return {\n      id: String(row.id),\n      uri: longId,\n      url: longId,\n      account: baseAccount,\n      content: content.startsWith('&lt;')? content : `&lt;p>${content}&lt;\/p>`,\n      created_at: new Date(parseDate(row.created_at)).toISOString(),\n      visibility: row.visibility || 'public', \/\/ \u2705 myapp57\n      \/\/ \u2705 myapp57 - reply \ud544\ub4dc \ucd94\uac00 \/ Add reply fileds \n      in_reply_to_id: row.in_reply_to_id ? String(row.in_reply_to_id) : null,\n      in_reply_to_account_id: row.in_reply_to_account_id ? String(row.in_reply_to_account_id) : null,\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: (() => {\n        \/\/ \u2705 myapp57 - image \uc788\uc73c\uba74 \uc5ec\uae30\uc11c \ubc18\ud658 \/ Return the image here if it exists.\n        try {\n          const medias = db.prepare(`\n          SELECT ma.* FROM media_attachments ma\n          JOIN post_media pm ON pm.media_id = ma.id\n          WHERE pm.post_id=?\n          `).all(row.id) as any&#91;];\n          return medias.map((m:any) => ({\n            id: String(m.id),\n            type: 'image',\n            url: m.url.startsWith('http') ? m.url : `https:\/\/${DOMAIN}${m.url}`,\n            preview_url: m.url.startsWith('http') ? m.url : `https:\/\/${DOMAIN}${m.url}`,\n          }));\n        } catch { return &#91;]; }\n      })(),\n      mentions: &#91;],\n      tags: &#91;],\n      emojis: &#91;],\n      card: null,\n      poll: null,\n      reblog: null,\n    };\n... ...<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/timeline\/route.ts &#8211; \ub2e4\uc74c \ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c \uc218\uc815 \/ Revise in the next project<\/p>\n\n\n\n<p>\ud83d\udcc1 \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<p>\u2714\ufe0f \ub2f5\uae00 \uc791\uc131 \/ Write a reply<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"808\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-replay-before-jpg.jpg\" alt=\"\" class=\"wp-image-11729\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-replay-before-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-replay-before-jpg-297x300.jpg 297w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-replay-before-jpg-768x776.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-replay-before-jpg-400x404.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">reply<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f \ub2f5\uae00 \ud655\uc778 \/ Check for replies<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"959\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-reply-jpg.jpg\" alt=\"\" class=\"wp-image-11730\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-reply-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-reply-jpg-250x300.jpg 250w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-reply-jpg-768x921.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/myapp57-reply-jpg-400x480.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">reply<\/figcaption><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\ub9c8\ud0dc\ubcf5\uc74c 21\uc7a5 22\uc808 \/ Matthew 21: 22<\/p>\n\n\n\n<p>&#8220;\ub108\ud76c\uac00 \uae30\ub3c4\ud560 \ub54c\uc5d0 \ubb34\uc5c7\uc774\ub4e0\uc9c0 \ubbff\uace0 \uad6c\ud558\ub294 \uac83\uc740 \ub2e4 \ubc1b\uc73c\ub9ac\ub77c \ud558\uc2dc\ub2c8\ub77c&#8221;<\/p>\n\n\n\n<p>&#8220;And everything you ask in prayer, believing, you will receive.&#8221;<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udc49\ud83c\udffb myapp57 \uc740 \uae30\uc874 sns\ud3ec\uc2a4\ud2b8\uc5d0 \ub313\uae00 \uae30\ub2a5\uc744 \ucd94\uac00\ud569\ub2c8\ub2e4.myapp57 adds a commenting feature to existing social media posts. \ud83d\udc49\ud83c\udffb \ud2b8\ub9ac \ud615\ud0dc\ub85c \uae00 \ud558\ub2e8\uc5d0 \ubb36\uc774\ub294 \uad6c\uc870\uac00 \uc544\ub2c8\ub77c \uc0c8\uae00 \ucc98\ub7fc \ub728\uace0 \uc6d0\ubcf8\uae00\uc5d0 \ucd94\uac00\ub418\ub294 \uae00\uc744 \ud45c\uc2dc\ud558\uae30 \uc704\ud574\uc11c @userid post\ud615\uc2dd\uc744 \ud45c\uc2dc\ub418\ub294 \uae30\ub2a5\uc785\ub2c8\ub2e4.Instead of being grouped in a tree structure at the bottom of the post, this feature displays the content [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":11729,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,27,1,7],"tags":[],"class_list":["post-11651","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\/11651","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=11651"}],"version-history":[{"count":75,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/11651\/revisions"}],"predecessor-version":[{"id":11732,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/11651\/revisions\/11732"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media\/11729"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=11651"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=11651"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=11651"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}