{"id":9547,"date":"2026-09-05T10:10:02","date_gmt":"2026-09-05T01:10:02","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=9547"},"modified":"2026-09-06T14:22:08","modified_gmt":"2026-09-06T05:22:08","slug":"nextjs-sns-server-23","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/09\/05\/nextjs-sns-server-23\/","title":{"rendered":"[nextjs]SNS Server-23(myapp33)\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 pinafore\uc5d0 \ub85c\uadf8\uc778\ud558\ub294 \uae30\ub2a5(OAuth)\uc744 \ucd94\uac00\ud569\ub2c8\ub2e4.<br>Add a login feature (OAuth) to Pinafore.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \uc544\uc774\ub514 \ud328\uc2a4\uc6cc\ub4dc \uc785\ub825\uc5c6\uc774 user1\uc73c\ub85c \ub85c\uadf8\uc778\ud569\ub2c8\ub2e4.<br>Log in as user1 without entering an ID or password.<\/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  -&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\n\u2502   \u251c\u2500\u2500 api\/timeline\/route.ts  -&gt; Timeline API\n\u2502   \u251c\u2500\u2500 <strong><span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">api\/v1\/instance<\/span>\/route.ts -&gt; auth<\/strong>\n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/apps\/route.ts.    -&gt; auth<\/strong>\n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/accounts\/verify_credentials\/route.ts -&gt; auth<\/strong>\n\u2502   \u251c\u2500\u2500 <strong>api\/v1\/statuses\/route.ts -&gt; auth<\/strong>\n\u2502   \u251c\u2500\u2500 <strong><span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">api\/v1\/timelines\/home\/route.ts<\/span> -&gt; auth<\/strong>\n\u2502   \u251c\u2500\u2500 <strong><span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">oauth\/authorize\/route.ts<\/span> -&gt; auth<\/strong>\n\u2502   \u251c\u2500\u2500 <strong><span style=\"background-color: initial; font-size: inherit; text-align: initial; color: initial;\">oauth\/token\/route.ts<\/span> -&gt; auth<\/strong>\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\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 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   \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=\"j8koTzmgea\"><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=RuWqzSswg4#?secret=j8koTzmgea\" data-secret=\"j8koTzmgea\" 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 \uac1c\uc694 \/ Overview<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Pinafore (Mastodon \ud074\ub77c\uc774\uc5b8\ud2b8) \ub85c\uadf8\uc778 \uac1c\uc694\nPinafore (Mastodon Client) Login Overview\n\n1. Pinafore\uc5d0\uc11c Instance\uc5d0 https:\/\/aloy-horizon.duckdns.org \uc785\ub825\nEnter https:\/\/aloy-horizon.duckdns.org into the Instance field in Pinafore.\n\n2. Pinafore\uac00 \uc9c4\ud589\ud558\ub294 \ucc98\ub9ac \uacfc\uc815\nThe process carried out by Pinafore\n\n  - \ub0b4 \uc11c\ubc84 \uac80\uc0c9 \/ Search My Servers\n   GET \/.well-known\/webfinger?resource=acct:user1@aloy-horizon.duckdns.org\n\n  - \ub0b4 \uc11c\ubc84 \uc815\ubcf4 \uac00\uc838\uac00\uae30 \/ Retrieve My Server Information\n   GET \/api\/v1\/instance( \ub0b4 \uc11c\ubc84 \uc815\ubcf4\ub97c Pinafore\uac00 \uac00\uc838\uac10)\n\n  - \uc571 \ub4f1\ub85d \/ App Registration\n   (\uc571 \ub4f1\ub85d\ud558\uace0 pinafore\uc5d0 \uc751\ub2f5 \/ Register the app and respond to Pinafore.)\n   POST \/api\/v1\/apps \n\n3. \ube0c\ub77c\uc6b0\uc800 \ud31d\uc5c5(\uc2b9\uc778\ud654\uba74) \/ Browser pop-up (approval screen):\n  - pinafore\uac00 \ub0b4 \uc11c\ubc84\uc5d0\uc11c \uc544\ub798\uc758 \ub77c\uc6b0\ud2b8 GET\uc73c\ub85c \uc2e4\ud589\ud558\uc5ec \uc2b9\uc778\ud654\uba74 \uc548\ub0b4\n    Pinafore displays the authorization screen when the GET route below is executed on my server.\n   GET \/oauth\/authorize?client_id=...&amp;scope=read write follow\n\n4. \uc2b9\uc778 \ubc84\ud2bc \ub204\ub974\uae30 \/ Press the approval button. :\n  - \uc2b9\uc778\ud654\uba74\uc5d0\uc11c \uc2b9\uc778\ubc84\ud2bc \ub204\ub974\uba74 POST \/oauth\/authorize \uc2e4\ud589\n    Clicking the approval button on the approval screen executes POST \/oauth\/authorize.\n\n  - oauth_codes \ubc1c\uae09\ud558\uace0 \ucf54\ub4dc\ub97c DB\uc5d0 \uc800\uc7a5\n    Issue oauth_codes and store the codes in the database.\n\n  - \ub2e4\uc2dc pinafor.social\ub85c \ub9ac\ub2e4\uc774\ub809\ud2b8 \uc2e4\ud589\n    Redirecting back to pinafor.social\n\n  - Pinafore\uac00 POST \/oauth\/token \ub77c\uc6b0\ud2b8 \uc2e4\ud589\ud574\uc11c access_token \ubc1c\uae09\ud558\uace0 DB\uc800\uc7a5\n    Pinafore executes the POST \/oauth\/token route to issue an access_token and save it to the DB.\n\n5. Pinafore \ub85c\uadf8\uc778 \uc131\uacf5 \/ Pinafore login successful : \n   - \/api\/v1\/accounts\/verify_credentials \ub77c\uc6b0\ud2b8 \uc2e4\ud589 \ub0b4 \uc11c\ubc84 \uc815\ubcf4\ub9ac\ud134\n     Return server information upon execution of the \/api\/v1\/accounts\/verify_credentials route.\n\n   - verify_credentials\uc5d0\uc11c \ub9ac\ud134\ud55c \uc815\ubcf4\ub97c \ud0c0\uc784\ub77c\uc778\uc5d0 \ud45c\uc2dc\n     Display the information returned by verify_credentials on the timeline.\n\n   - pinafore \ud0c0\uc784\ub77c\uc778\uc5d0\uc11c \uae00\uc4f0\uae30 \uac00\ub2a5 Outbox POST \uc0ac\uc6a9\uac00\ub2a5\n     Posting is enabled on the Pinafore timeline; Outbox POST is supported.<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ud544\uc694\ud55c \ub77c\uc6b0\ud2b8 \/ route needed<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-regular\" style=\"font-size:14px\"><table class=\"has-fixed-layout\"><thead><tr><th>\uc21c\uc11c \/ order<\/th><th>\ub77c\uc6b0\ud2b8 \/ route<\/th><th>\uc5ed\ud560 \/ role<\/th><th>\ud30c\uc77c \/ file<\/th><th>\uc0c1\ud0dc \/ ststus<\/th><\/tr><\/thead><tbody><tr><td>1<\/td><td><code>\/.well-known\/webfinger<\/code><\/td><td>&#8211; Pinafore\uac00&nbsp;<code>user@\ub3c4\uba54\uc778<\/code>&nbsp;\uc874\uc7ac\ud558\ub294\uc9c0 \ud655\uc778!<br>Pinafore checks if the user@domain exists!<\/td><td><code>app\/.well-known\/webfinger\/route.ts<\/code><\/td><td>\uc774\ubbf8 \uc788\uc74c<br>Already exists<\/td><\/tr><tr><td>2<\/td><td><code>\/api\/v1\/instance<\/code><\/td><td>&#8211; \uc11c\ubc84 \uc815\ubcf4!&nbsp;<code>version<\/code>,&nbsp;<code>title<\/code><br>Server Information! version, title<\/td><td><code>app\/api\/v1\/instance\/route.ts<\/code><\/td><td>myapp33<\/td><\/tr><tr><td>3<\/td><td><code>\/api\/v1\/apps<\/code>&nbsp;POST<\/td><td>&#8211; \uc571 \ub4f1\ub85d!&nbsp;<code>client_id\/secret<\/code>&nbsp;\ubc1c\uae09<br>App registration! Issue client_id\/secret.<\/td><td><code>app\/api\/v1\/apps\/route.ts<\/code><\/td><td><br>myapp33<\/td><\/tr><tr><td>4<\/td><td><code>\/oauth\/authorize<\/code>&nbsp;GET<\/td><td>&#8211; \ub85c\uadf8\uc778\uc2b9\uc778 \ud654\uba74,\uc2b9\uc778\ubc84\ud2bc<br>Login approval screen, approval button<\/td><td><code>app\/oauth\/authorize\/route.ts<\/code><\/td><td><br>myapp33<\/td><\/tr><tr><td>5<\/td><td><code>\/oauth\/authorize<\/code>&nbsp;POST<\/td><td>\uc2b9\uc778&nbsp;<code>code<\/code>&nbsp;\ubc1c\uae09<\/td><td><code>app\/oauth\/authorize\/route.ts<\/code>&nbsp;(POST)<\/td><td><br>myapp33<\/td><\/tr><tr><td>6<\/td><td><code>\/oauth\/token<\/code>&nbsp;POST<\/td><td><code>code<\/code>&nbsp;-&gt;&nbsp;<code>access_token<\/code>&nbsp;\uad50\ud658<\/td><td><code>app\/oauth\/token\/route.ts<\/code><\/td><td><br>myapp33<\/td><\/tr><tr><td>7<\/td><td><code>\/api\/v1\/accounts\/verify_credentials<\/code><\/td><td> \ub0b4 \uc815\ubcf4 \ub9ac\ud134! \ub85c\uadf8\uc778 <\/td><td><code>app\/api\/v1\/accounts\/verify_credentials\/route.ts<\/code><\/td><td><br>myapp33<\/td><\/tr><tr><td>8<\/td><td><code>\/api\/v1\/timelines\/home<\/code><\/td><td>\ub85c\uadf8\uc778 \ud6c4 \ud0c0\uc784\ub77c\uc778<\/td><td><code>app\/api\/v1\/timelines\/home\/route.ts<\/code><\/td><td><br>myapp33<\/td><\/tr><tr><td>9<\/td><td><code>\/api\/v1\/statuses<\/code>&nbsp;POST<\/td><td>\uae00\uc4f0\uae30(pinafore) Outbox \uc800\uc7a5<\/td><td><code>app\/api\/v1\/statuses\/route.ts<\/code><\/td><td>myapp33<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>\u2714\ufe0f \uc790\ub3d9\uc0dd\uc131 \ud14c\uc774\ube14 \/ Automatically generated table<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \uc571 \ub4f1\ub85d \/ App Registration\nCREATE TABLE oauth_apps (\n          client_id TEXT PRIMARY KEY,\n          client_secret TEXT NOT NULL,\n          client_name TEXT,\n          redirect_uri TEXT,\n          scopes TEXT,\n          website TEXT,\n          created_at INTEGER\n        );\n\n# Oauth codes\nCREATE TABLE oauth_codes (\n        code TEXT PRIMARY KEY,\n        client_id TEXT,\n        redirect_uri TEXT,\n        scope TEXT,\n        username TEXT,\n        created_at INTEGER\n      );\n\n# OAuth tokens \nCREATE TABLE oauth_tokens (\n          access_token TEXT PRIMARY KEY,\n          client_id TEXT,\n          username TEXT,\n          scope TEXT,\n          created_at INTEGER\n        );<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\ud83d\udca1\noauth_apps \n- \uc571\ub4f1\ub85d(pinaafore\uc5d0\ub9cc \ub85c\uadf8\uc778 \ud588\uc73c\uba74 \ub370\uc774\ud130 1\uac1c)\nApp registration (1 data entry if logged into Pinaafore only)\n\noauth_tokens \n- \uc571\ud1a0\ud070(\uc720\uc800 100\uba85\uc774\uba74 100\uba85\uc774 \ub85c\uadf8\uc778\ud55c \ud69f\uc218 \ub9cc\ud07c ,\uc989 100+)\nApp tokens (based on the number of logins by the 100 users\u2014i.e., 100+).\n\noauth_codes \n- \uc4f0\uace0 \ubc84\ub9ac\ub294 \ucf54\ub4dc \ud56d\uc0c1 \ub370\uc774\ud130 \uac2f\uc218\ub294 0\nDiscarded code; the data count is always 0<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \ub77c\uc6b0\ud2b8 \ubc0f \ud30c\uc77c \ucd94\uac00 \/ Add Routes and Files<\/p>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/instance\/route.ts<\/p>\n\n\n\n<p>&#8212; \ub3d9\uc791 \uac1c\uc694 \/ Operation Overview<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> \/api\/v1\/instance GET \n-&gt; Pinafore\uac00 \ub0b4 \uc11c\ubc84\uc815\ubcf4\ub97c \uac00\uc838\uac11\ub2c8\ub2e4.\nPinafore retrieves my server information.\n\n-&gt; \uc774\ub54c \ub0b4 \uc11c\ubc84\ub294 \ub9c8\uc2a4\ud1a0\ub3c8 \uc11c\ubc84\ub85c \uc778\uc2dd\ud558\ub3c4\ub85d \uc124\uc815\uc744 \ud569\ub2c8\ub2e4.\nAt this point, configure your server so that it is recognized as a Mastodon server.<\/code><\/pre>\n\n\n\n<p>&#8212; \ucf54\ub4dc \/ Code<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ app\/api\/v1\/instance\/route.ts \nimport { NextResponse } from 'next\/server';\n\nconst DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';\n\nexport async function GET() {\n  return NextResponse.json({ ... ...\n})}<\/code><\/pre>\n\n\n\n<p>&#8212; \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \ud130\ubbf8\ub110\uc5d0\uc11c \uc2e4\ud589 \/ Run in the terminal\n\n % <strong>curl https:\/\/aloy-horizon.duckdns.org\/api\/v1\/instance<\/strong>\n\n# \uc751\ub2f5 \/ Response\n{\"uri\":\"aloy-horizon.duckdns.org\",\"title\":\"Aloy Horizon\",\"short_description\":\"My ActivityPub server!\",\"description\":\"My own Fediverse server running Next.js\",\"email\":\"admin@aloy-horizon.duckdns.org\",\"version\":\"4.3.0 (compatible; Aloy Horizon 1.0)\",\"urls\":{\"streaming_api\":\"wss:\/\/aloy-horizon.duckdns.org\"},\"stats\":{\"user_count\":1,\"status_count\":100,\"domain_count\":1000},\"thumbnail\":\"https:\/\/aloy-horizon.duckdns.org\/icon.png\",\"languages\":&#91;\"en\",\"ko\"],\"registrations\":true,\"approval_required\":false,\"invites_enabled\":false,\"configuration\":{\"statuses\":{\"max_characters\":500,\"max_media_attachments\":4}},\"rules\":&#91;]}\n\n%  <\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"945\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-instance-jpg.jpg\" alt=\"\" class=\"wp-image-9751\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-instance-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-instance-jpg-254x300.jpg 254w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-instance-jpg-768x907.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-instance-jpg-400x473.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">v1\/instance<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f \/api\/v1\/apps&nbsp;POST<\/p>\n\n\n\n<p>&#8212; \ub3d9\uc791\uac1c\uc694 \/  Operation Overview<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Pinafore \uc571\uc744 \ub0b4 \uc11c\ubc84\uc5d0 \uc571\ub4f1\ub85d \ud558\uae30\n# Registering the Pinafore app on my server\n\n# - \/api\/v1\/apps POST -&gt; Pinafore \uc571 \ub4f1\ub85d! client_id \ubc1c\uae09!\n# - \/api\/v1\/apps POST -&gt; Register Pinafore app! Issue client_id!\n\n# Pinafore\uac00 \ubcf4\ub0b4\ub294 \uc815\ubcf4\n# Information from Pinafore\nPOST \/api\/v1\/apps\n{\n  \"client_name\": \"Pinafore\",\n  \"redirect_uris\": \"https:\/\/pinafore.social\/settings\/instances\/add\",\n  \"scopes\": \"read write follow push\",\n  \"website\": \"https:\/\/pinafore.social\"\n}<\/code><\/pre>\n\n\n\n<p>&#8212; \ucf54\ub4dc \/ Code<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ app\/api\/v1\/apps\/route.ts - \u2705 myapp33-2 - Pinafore \uc571 \ub4f1\ub85d \/ Pinafore App Registration\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<\/code><\/pre>\n\n\n\n<p>&#8212; \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \ud130\ubbf8\ub110\uc5d0\uc11c \uc2e4\ud589 \/ Run in the terminal\n\n% <strong>curl -X POST https:\/\/aloy-horizon.duckdns.org\/api\/v1\/apps \\\n  -H \"Content-Type: application\/json\" \\\n  -d '{\n    \"client_name\": \"Pinafore\",\n    \"redirect_uris\": \"urn:ietf:wg:oauth:2.0:oob\",\n    \"scopes\": \"read write follow push\",\n    \"website\": \"https:\/\/pinafore.social\"\n  }'<\/strong>\n\n# \uc751\ub2f5 \/ Response\n{\"id\":\"e6988bbc-3ef3-4e7d-8c28-620be9a2602b\",\"name\":\"Pinafore\",\"website\":\"https:\/\/pinafore.social\",\"redirect_uri\":\"urn:ietf:wg:oauth:2.0:oob\",\"client_id\":\"e6988bbc-3ef3-4e7d-8c28-620be9a2602b\",\"client_secret\":\"c8b558db-7dc1-40dd-a711-9ff322ad29ccc1d71931-09d6-491c-8d59-17968d1f3f1c\",\"vapid_key\":\"\"}                  \n\n% <\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"248\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-apps-post-jpg.jpg\" alt=\"\" class=\"wp-image-9754\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-apps-post-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-apps-post-jpg-300x93.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-apps-post-jpg-768x238.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-apps-post-jpg-400x124.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">v1\/apps<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f <code>app\/oauth\/authorize\/route.ts<\/code><\/p>\n\n\n\n<p>&#8212; \ub3d9\uc791\uac1c\uc694 \/ Operation Overview<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Pinafore\uc5d0\uc11c \uc544\ub798\uc758 \ub77c\uc6b0\ud2b8 \uc2e4\ud589\ud574\uc11c \ube0c\ub77c\uc6b0\uc800 \uc2b9\uc778\ud654\uba74 \ud31d\uc5c5:\n# Execute the route below in Pinafore to open the browser approval screen popup\n\nhttps:&#47;&#47;aloy-horizon.duckdns.org\/oauth\/authorize?\n  client_id=bdbaf975...\n  &amp;redirect_uri=urn:ietf:wg:oauth:2.0:oob\n  &amp;response_type=code\n  &amp;scope=read write follow push\n\n# \ud654\uba74\uc758 \uc2b9\uc778 \ub204\ub974\uba74 code \ubc1c\uae09\n# Press Approve on the screen to issue a code<\/code><\/pre>\n\n\n\n<p>&#8212; \ucf54\ub4dc \/ Code<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ app\/oauth\/authorize\/route.ts - \u2705 myapp33-3 - \ub85c\uadf8\uc778 \uc2b9\uc778 \ud398\uc774\uc9c0! \/ Login Authorization Page!\nimport { NextResponse } from 'next\/server';\nimport db from '@\/lib\/db';\nimport { randomUUID } from 'crypto';\n\nexport async function GET(req: Request) { ... ... }\nexport async function POST(req: Request) { ... ... }<\/code><\/pre>\n\n\n\n<p>&#8212; \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Terminal \n% curl -X POST https:\/\/aloy-horizon.duckdns.org\/oauth\/token \\\n  -H \"Content-Type: application\/x-www-form-urlencoded\" \\\n  -d \"client_id=bdbaf975-d04b-4dc6-8525-6b94e9d09752&amp;client_secret=ee233e0e-a99f-4ca4-8487-09cce8219a1c4115f412-d65b-4268-944f-f0e6aed5199d&amp;code=df066428-3240-45cf-aa2a-6c4f809f875c&amp;grant_type=authorization_code&amp;redirect_uri=urn:ietf:wg:oauth:2.0:oob\"\n\n# \uc751\ub2f5 \/ Response\n{\"access_token\":\"2f7f89d8-20e0-4e63-acac-5d3bea32deb11e323217-71b3-455a-8320-fc348e305611\",\"token_type\":\"Bearer\",\"scope\":\"read write follow\",\"created_at\":1788651972}\n\n%\n<\/code><\/pre>\n\n\n\n<p>&#8212; \uc544\ub798\uc758 \ud654\uba74\uc5d0\uc11c pinafore\uc758 \uc815\ubcf4\uac00 \uc5c6\uc73c\uba74 auth \ucf54\ub4dc\uac00 \ubcf4\uc785\ub2c8\ub2e4.(\ud14c\uc2a4\ud2b8 \ub2e8\uacc4)<br>If there is no information for Pinafore on the screen below, the auth code will be displayed.(Testing phase)<\/p>\n\n\n\n<p>&#8212; pinafore\uc815\ubcf4\ub97c \uc815\uc0c1\uc801\uc73c\ub85c \ud655\uc778\ub418\uba74 pinafore \ud0c0\uc784\ub77c\uc778\uc744 \uc774\ub3d9\ud569\ub2c8\ub2e4.<br>Once the Pinafore information is successfully verified, you will navigate to the Pinafore timeline.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"746\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-OAuth-confirm-jpg.png\" alt=\"\" class=\"wp-image-9759\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-OAuth-confirm-jpg.png 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-OAuth-confirm-jpg-300x280.png 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-OAuth-confirm-jpg-768x716.png 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-OAuth-confirm-jpg-400x373.png 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"545\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-oob-jpg.jpg\" alt=\"\" class=\"wp-image-9760\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-oob-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-oob-jpg-300x204.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-oob-jpg-768x523.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-preflight-oob-jpg-400x273.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">confirmed, auth code<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/accounts\/verify_credentials\/route.ts<\/p>\n\n\n\n<p>&#8212; Pinafore\uac00 \ubcf4\ub294 \ub0b4 \uc11c\ubc84\uc815\ubcf4,\ud0c0\uc784\ub77c\uc778\uc5d0 \ud45c\uc2dc\ub418\ub294 \uc815\ubcf4<br>My server information as seen by Pinafore, information displayed on the timeline<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { 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\/accounts\/verify_credentials \\\n  -H \"Authorization: Bearer 2f7f89d8-20e0-4e63-acac-5d3bea32deb11e323217-71b3-455a-8320-fc348e305611\"<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>% <strong>curl https:\/\/aloy-horizon.duckdns.org\/api\/v1\/accounts\/verify_credentials \\\n  -H \"Authorization: Bearer 2f7f89d8-20e0-4e63-acac-5d3bea32deb11e323217-71b3-455a-8320-fc348e305611\"<\/strong>\n\n# \uc751\ub2f5 \/ Response\n{\"id\":\"1\",\"username\":\"user1\",\"acct\":\"user1@aloy-horizon.duckdns.org\",\"display_name\":\"user1\",\"locked\":false,\"bot\":false,\"created_at\":\"2026-09-05T23:52:44.236Z\",\"note\":\"My ActivityPub server!\",\"url\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\",\"avatar\":\"https:\/\/aloy-horizon.duckdns.org\/icon.png\",\"avatar_static\":\"https:\/\/aloy-horizon.duckdns.org\/icon.png\",\"header\":\"https:\/\/aloy-horizon.duckdns.org\/icon.png\",\"header_static\":\"https:\/\/aloy-horizon.duckdns.org\/icon.png\",\"followers_count\":1,\"following_count\":1,\"statuses_count\":100,\"source\":{\"privacy\":\"public\",\"sensitive\":false,\"language\":\"en\",\"note\":\"\",\"fields\":&#91;]}}                                                                            \n\n% <\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/timelines\/home\/route.ts<\/p>\n\n\n\n<p>&#8212; \ub85c\uadf8\uc778 \ud6c4 \ud0c0\uc784\ub77c\uc778 (\uc544\uc9c1 \ube48 \ud0c0\uc784\ub77c\uc778)<br>Timeline after logging in (currently empty)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>export async function GET() {\n  \/\/ \u2705 \ube48 \ud0c0\uc784\ub77c\uc778! (\ub098\uc911\uc5d0 Outbox \uae00 \uac00\uc838\uc624\uac8c \ud560 \uc218 \uc788\uc74c!) \/ Empty timeline! (Later, you can fetch posts from the Outbox!)\n  return NextResponse.json(&#91;]);\n}\nexport async function OPTIONS() { ... }<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/v1\/statuses\/route.ts<\/p>\n\n\n\n<p>&#8212; \ub85c\uadf8\uc778 \ud6c4 \ud0c0\uc784\ub77c\uc778\uc5d0 \ubcf4\uc774\ub294 \ub0b4\uac00 \uc791\uc131\ud55c\uae00(\uc544\uc9c1 DB\uc800\uc7a5 \uad6c\ud604 \uc548\ud568.)<br>Posts I&#8217;ve written that appear on the timeline after logging in (DB storage implementation not yet complete).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { 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) { ... }\nexport async function OPTIONS() { ... }<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 CORS\uc124\uc815 \ucd94\uac00 \/ Add CORS configuration<\/p>\n\n\n\n<p>\u2714\ufe0f \ud504\ub85c\uc81d\ud2b8 \ub8e8\ud2b8\uc5d0 middlewatre.ts \ud30c\uc77c \ucd94\uac00<br>Add a <code>middleware.ts<\/code> file to the project root.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { NextResponse } from 'next\/server';\nimport type { NextRequest } from 'next\/server';\n\nexport function middleware(req: NextRequest) { ... }\nexport const config = {\n  matcher: &#91;'\/api\/:path*', '\/oauth\/:path*', '\/.well-known\/:path*', '\/users\/:path*'],\n};<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f OPTIONS \ud568\uc218 \ucd94\uac00 \/ Add OPTIONS function<\/p>\n\n\n\n<p>&#8212; \uc544\ub798\uc758 \ud30c\uc77c\ub4e4\uc758 \uac00\uc7a5\ud558\ub2e8\uc5d0 OPTIONS\ud568\uc218\ub97c \ucd94\uac00\ud569\ub2c8\ub2e4.<br>Add the OPTIONS function to the very bottom of the files listed below.<\/p>\n\n\n\n<p>1) OPTIONS \ud568\uc218 \/ OPTIONS Function<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ CORS \uc5d0\ub7ec \ubc29\uc9c0\uc6a9 OPTIONS \ucc98\ub9ac \/ Handle OPTIONS to prevent CORS errors\nexport async function OPTIONS() {\n  return new NextResponse(null, {\n    status: 204,\n    headers: {\n      'Access-Control-Allow-Origin': '*',\n      'Access-Control-Allow-Methods': 'GET, OPTIONS',\n      'Access-Control-Allow-Headers': 'Content-Type, Authorization',\n    },\n  });\n}<\/code><\/pre>\n\n\n\n<p>2)\ud30c\uc77c \/ Files<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app\/api\/v1\/instance\/route.ts\napp\/api\/v1\/apps\/route.ts \napp\/api\/v1\/accounts\/verify_credentials\/route.ts \napp\/api\/v1\/timelines\/home\/route.ts\napp\/api\/v1\/statuses\/route.ts\napp\/oauth\/token\/route.ts\napp\/oauth\/authorize\/route.ts <\/code><\/pre>\n\n\n\n<p>&#8212; \uc124\uc815\uc744 \uc548\ud558\uba74 pinafore.social\uc5d0\uc11c \uc544\ub798\uc640 \uac19\uc740 \uc624\ub958\uac00 \ubc1c\uc0dd\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<br>If you do not configure the settings, an error like the one below may occur on pinafore.social.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Error: FetchEvent.respondWith received an error: TypeError: Load failed. Is this a valid Mastodon instance? Is a browser extension blocking the request? Are you in private browsing mode? If you believe this is a problem with your instance, please send this link to the administrator of your instance.<\/code><\/pre>\n\n\n\n<p>&#8212; \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># preflight Test\ncurl -i -X OPTIONS https:\/\/aloy-horizon.duckdns.org\/api\/v1\/instance \\\n  -H \"Origin: https:\/\/pinafore.social\" \\\n  -H \"Access-Control-Request-Method: GET\"\n\n# \uc704\uc758 curl\uc744 \ud130\ubbf8\ub110\uc5d0\uc11c \uc2e4\ud589\ud558\uba74 \ube0c\ub77c\uc6b0\uc800\uc5d0 \uc778\uc99d \ud5c8\uc6a9 \ud654\uba74 \uc2e4\ud589 \ub428.\n# Running the curl command above in the terminal triggers the authentication approval screen in your browser.<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udcc1 \ucd5c\uc885 \ud14c\uc2a4\ud2b8 \/ Final Test<\/p>\n\n\n\n<p>\u2714\ufe0f \uc704\uc758 \ubaa8\ub4e0 \ud14c\uc2a4\ud2b8\uac00 \uc815\uc0c1\uc801\uc73c\ub85c \uc791\ub3d9\ud558\uba74 \ub85c\uceec \uc11c\ubc84\ub97c \uc2e4\ud589\ud558\uace0 pinafore.social\uc5d0\uc11c \ub85c\uadf8\uc778\uc744 \ud574\ubd05\ub2c8\ub2e4.<br>If all the tests above run successfully, start the local server and try logging in at pinafore.social.<\/p>\n\n\n\n<p>\u2714\ufe0f \ub85c\uadf8\uc778\uc744 \uc644\ub8cc \ud558\uba74 \uc2e4\uc81c\ub85c \uae00\uc4f0\uae30\uac00 \uc2e4\ud589\ub418\ub294\uc9c0 \ud655\uc778\ud574 \ubd05\ub2c8\ub2e4.<br>After logging in, verify that the writing function actually works.<\/p>\n\n\n\n<p>\u2714\ufe0f \uae00\uc4f0\uae30 \ub370\uc774\ud130\ub294 \uc9c0\uae08\uc740 outbox\uc5d0 \uc800\uc7a5\ud558\uc9c0 \uc54a\uace0 pinafore\uc5d0 \ubcf4\uc774\ub294\uc9c0\ub9cc \ud655\uc778\ud569\ub2c8\ub2e4.<br>For now, writing data is not saved to the outbox; we only verify that it appears in Pinafore.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"774\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-writeok-jpg.jpg\" alt=\"\" class=\"wp-image-9746\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-writeok-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-writeok-jpg-300x290.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-writeok-jpg-768x743.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/09\/myapp33-writeok-jpg-400x387.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">aloy-horizon.duckdns.org(pinafore.social<\/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 \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 pinafore\uc5d0 \ub85c\uadf8\uc778\ud558\ub294 \uae30\ub2a5(OAuth)\uc744 \ucd94\uac00\ud569\ub2c8\ub2e4.Add a login feature (OAuth) to Pinafore. \ud83d\udc49\ud83c\udffb \uc544\uc774\ub514 \ud328\uc2a4\uc6cc\ub4dc \uc785\ub825\uc5c6\uc774 user1\uc73c\ub85c \ub85c\uadf8\uc778\ud569\ub2c8\ub2e4.Log in as user1 without entering an ID or password. \ud83d\udc49\ud83c\udffb \uc804\uccb4 \ucf54\ub4dc\ub294 \uae43\ud5c8\ube0c\uc5d0\uc11c \ud655\uc778 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.You can find the full code on GitHub. https:\/\/github.com\/gideonslife01\/flm-nextjs \ud83d\udcc1 \uc804\uccb4 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":9759,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,27,1,7],"tags":[],"class_list":["post-9547","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\/9547","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=9547"}],"version-history":[{"count":135,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/9547\/revisions"}],"predecessor-version":[{"id":9774,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/9547\/revisions\/9774"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media\/9759"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=9547"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=9547"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=9547"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}