[nextjs]SNS Server-23(myapp33)ย 

๐Ÿ‘‰๐Ÿป ํšŒ์›๊ฐ€์ž… ๋ฐ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
These are the sign-up and login functions.

๐Ÿ‘‰๐Ÿป pinafore์— ๋กœ๊ทธ์ธํ•˜๋Š” ๊ธฐ๋Šฅ(OAuth)์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
Add a login feature (OAuth) to Pinafore.

๐Ÿ‘‰๐Ÿป ์•„์ด๋”” ํŒจ์Šค์›Œ๋“œ ์ž…๋ ฅ์—†์ด user1์œผ๋กœ ๋กœ๊ทธ์ธํ•ฉ๋‹ˆ๋‹ค.
Log in as user1 without entering an ID or password.

๐Ÿ‘‰๐Ÿป ์ „์ฒด ์ฝ”๋“œ๋Š” ๊นƒํ—ˆ๋ธŒ์—์„œ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
You can find the full code on GitHub.

https://github.com/gideonslife01/flm-nextjs

๐Ÿ“ ์ „์ฒด ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ / Overall Project Structure

myapp project/  
โ”œโ”€โ”€ app/  (Next.js App Router)
โ”‚   โ”œโ”€โ”€ .well-known/webfinger/route.ts  -> webfinger
โ”‚   โ”œโ”€โ”€ api/follow/route.ts    -> Follow API(temporary)
โ”‚   โ”œโ”€โ”€ api/announce/route.ts  -> Boost(Announcement)
โ”‚   โ”œโ”€โ”€ api/like/route.ts.     -> Like API
โ”‚   โ”œโ”€โ”€ api/posts/route.ts     -> Writing API
โ”‚   โ”œโ”€โ”€ api/timeline/route.ts  -> Timeline API
โ”‚   โ”œโ”€โ”€ api/v1/instance/route.ts -> auth
โ”‚   โ”œโ”€โ”€ api/v1/apps/route.ts.    -> auth
โ”‚   โ”œโ”€โ”€ api/v1/accounts/verify_credentials/route.ts -> auth
โ”‚   โ”œโ”€โ”€ api/v1/statuses/route.ts -> auth
โ”‚   โ”œโ”€โ”€ api/v1/timelines/home/route.ts -> auth
โ”‚   โ”œโ”€โ”€ oauth/authorize/route.ts -> auth
โ”‚   โ”œโ”€โ”€ oauth/token/route.ts -> auth
โ”‚   โ”œโ”€โ”€ users/[username]/
โ”‚   โ”‚   โ”œโ”€โ”€ statuses/[id]/route.ts -> Indivisual Post
โ”‚   โ”‚   โ”œโ”€โ”€ route.ts               -> Acotr Information
โ”‚   โ”‚   โ”œโ”€โ”€ followers/route.ts     -> Followers List
โ”‚   โ”‚   โ”œโ”€โ”€ following/route.ts     -> Following List
โ”‚   โ”‚   โ”œโ”€โ”€ inbox/route.ts         -> Inbox
โ”‚   โ”‚   โ””โ”€โ”€ outbox/route.ts        -> outbox
โ”‚   โ”œโ”€โ”€ usersui/[username]/
โ”‚   โ”‚   โ”œโ”€โ”€ page.tsx               -> Timeline UI
โ”‚   โ”‚   โ””โ”€โ”€ _components/themes/
โ”‚   โ”‚       โ”œโ”€โ”€ themeex/ThemeexTheme.tsx   -> Example Theme
โ”‚   โ”‚       โ”œโ”€โ”€ pinafore/PinaforeTheme.tsx -> Theme 1
โ”‚   โ”‚       โ”œโ”€โ”€ mastodon/MastodonTheme.tsx -> Theme 2
โ”‚   โ”‚       โ””โ”€โ”€ minimal/MinimaltTheme.tsx  -> Theme 3
โ”‚   โ”œโ”€โ”€ layout.tsx, page.tsx, globals.css
โ”‚   โ””โ”€โ”€ favicon.ico
โ”œโ”€โ”€ lib/
โ”‚   โ”œโ”€โ”€ theme.tsx              -> Theme Provider
โ”‚   โ”œโ”€โ”€ watchThemes.ts         -> Check real-time theme changes
โ”‚   โ”œโ”€โ”€ auth.ts                -> Authentication, User Management
โ”‚   โ”œโ”€โ”€ ap.ts                  -> Follow,Undo,Create,Likes,Announce
โ”‚   โ””โ”€โ”€ db.ts                  -> DB connection
โ”œโ”€โ”€ data/
โ”‚   โ”œโ”€โ”€ keys/userIDs/          -> private.pem, public.pem(New)
โ”‚   โ””โ”€โ”€ keys/                  -> private.pem, public.pem(legacy)
โ”œโ”€โ”€ data.sqlite                -> Database(1/3)
โ”œโ”€โ”€ data.sqlite-wal            -> Database(2/3)
โ”œโ”€โ”€ data.sqlite-shm            -> Database(3/3)
โ”œโ”€โ”€ Caddyfile                  -> https 
โ”œโ”€โ”€ instrumentation.ts         -> Background Server
โ””โ”€โ”€ package.json

๐Ÿ“ ํ”„๋กœ์ ํŠธ ์‹œ์ž‘ / Project Start


๐Ÿ“ ๊ฐœ์š” / Overview

Pinafore (Mastodon ํด๋ผ์ด์–ธํŠธ) ๋กœ๊ทธ์ธ ๊ฐœ์š”
Pinafore (Mastodon Client) Login Overview

1. Pinafore์—์„œ Instance์— https://aloy-horizon.duckdns.org ์ž…๋ ฅ
Enter https://aloy-horizon.duckdns.org into the Instance field in Pinafore.

2. Pinafore๊ฐ€ ์ง„ํ–‰ํ•˜๋Š” ์ฒ˜๋ฆฌ ๊ณผ์ •
The process carried out by Pinafore

  - ๋‚ด ์„œ๋ฒ„ ๊ฒ€์ƒ‰ / Search My Servers
   GET /.well-known/webfinger?resource=acct:user1@aloy-horizon.duckdns.org

  - ๋‚ด ์„œ๋ฒ„ ์ •๋ณด ๊ฐ€์ ธ๊ฐ€๊ธฐ / Retrieve My Server Information
   GET /api/v1/instance( ๋‚ด ์„œ๋ฒ„ ์ •๋ณด๋ฅผ Pinafore๊ฐ€ ๊ฐ€์ ธ๊ฐ)

  - ์•ฑ ๋“ฑ๋ก / App Registration
   (์•ฑ ๋“ฑ๋กํ•˜๊ณ  pinafore์— ์‘๋‹ต / Register the app and respond to Pinafore.)
   POST /api/v1/apps 

3. ๋ธŒ๋ผ์šฐ์ € ํŒ์—…(์Šน์ธํ™”๋ฉด) / Browser pop-up (approval screen):
  - pinafore๊ฐ€ ๋‚ด ์„œ๋ฒ„์—์„œ ์•„๋ž˜์˜ ๋ผ์šฐํŠธ GET์œผ๋กœ ์‹คํ–‰ํ•˜์—ฌ ์Šน์ธํ™”๋ฉด ์•ˆ๋‚ด
    Pinafore displays the authorization screen when the GET route below is executed on my server.
   GET /oauth/authorize?client_id=...&scope=read write follow

4. ์Šน์ธ ๋ฒ„ํŠผ ๋ˆ„๋ฅด๊ธฐ / Press the approval button. :
  - ์Šน์ธํ™”๋ฉด์—์„œ ์Šน์ธ๋ฒ„ํŠผ ๋ˆ„๋ฅด๋ฉด POST /oauth/authorize ์‹คํ–‰
    Clicking the approval button on the approval screen executes POST /oauth/authorize.

  - oauth_codes ๋ฐœ๊ธ‰ํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ DB์— ์ €์žฅ
    Issue oauth_codes and store the codes in the database.

  - ๋‹ค์‹œ pinafor.social๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹คํ–‰
    Redirecting back to pinafor.social

  - Pinafore๊ฐ€ POST /oauth/token ๋ผ์šฐํŠธ ์‹คํ–‰ํ•ด์„œ access_token ๋ฐœ๊ธ‰ํ•˜๊ณ  DB์ €์žฅ
    Pinafore executes the POST /oauth/token route to issue an access_token and save it to the DB.

5. Pinafore ๋กœ๊ทธ์ธ ์„ฑ๊ณต / Pinafore login successful : 
   - /api/v1/accounts/verify_credentials ๋ผ์šฐํŠธ ์‹คํ–‰ ๋‚ด ์„œ๋ฒ„ ์ •๋ณด๋ฆฌํ„ด
     Return server information upon execution of the /api/v1/accounts/verify_credentials route.

   - verify_credentials์—์„œ ๋ฆฌํ„ดํ•œ ์ •๋ณด๋ฅผ ํƒ€์ž„๋ผ์ธ์— ํ‘œ์‹œ
     Display the information returned by verify_credentials on the timeline.

   - pinafore ํƒ€์ž„๋ผ์ธ์—์„œ ๊ธ€์“ฐ๊ธฐ ๊ฐ€๋Šฅ Outbox POST ์‚ฌ์šฉ๊ฐ€๋Šฅ
     Posting is enabled on the Pinafore timeline; Outbox POST is supported.

โœ”๏ธ ํ•„์š”ํ•œ ๋ผ์šฐํŠธ / route needed

์ˆœ์„œ / order๋ผ์šฐํŠธ / route์—ญํ•  / roleํŒŒ์ผ / file์ƒํƒœ / ststus
1/.well-known/webfinger– Pinafore๊ฐ€ user@๋„๋ฉ”์ธ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ!
Pinafore checks if the user@domain exists!
app/.well-known/webfinger/route.ts์ด๋ฏธ ์žˆ์Œ
Already exists
2/api/v1/instance– ์„œ๋ฒ„ ์ •๋ณด! version, title
Server Information! version, title
app/api/v1/instance/route.tsmyapp33
3/api/v1/apps POST– ์•ฑ ๋“ฑ๋ก! client_id/secret ๋ฐœ๊ธ‰
App registration! Issue client_id/secret.
app/api/v1/apps/route.ts
myapp33
4/oauth/authorize GET– ๋กœ๊ทธ์ธ์Šน์ธ ํ™”๋ฉด,์Šน์ธ๋ฒ„ํŠผ
Login approval screen, approval button
app/oauth/authorize/route.ts
myapp33
5/oauth/authorize POST์Šน์ธ code ๋ฐœ๊ธ‰app/oauth/authorize/route.ts (POST)
myapp33
6/oauth/token POSTcode -> access_token ๊ตํ™˜app/oauth/token/route.ts
myapp33
7/api/v1/accounts/verify_credentials ๋‚ด ์ •๋ณด ๋ฆฌํ„ด! ๋กœ๊ทธ์ธ app/api/v1/accounts/verify_credentials/route.ts
myapp33
8/api/v1/timelines/home๋กœ๊ทธ์ธ ํ›„ ํƒ€์ž„๋ผ์ธapp/api/v1/timelines/home/route.ts
myapp33
9/api/v1/statuses POST๊ธ€์“ฐ๊ธฐ(pinafore) Outbox ์ €์žฅapp/api/v1/statuses/route.tsmyapp33

โœ”๏ธ ์ž๋™์ƒ์„ฑ ํ…Œ์ด๋ธ” / Automatically generated table

# ์•ฑ ๋“ฑ๋ก / App Registration
CREATE TABLE oauth_apps (
          client_id TEXT PRIMARY KEY,
          client_secret TEXT NOT NULL,
          client_name TEXT,
          redirect_uri TEXT,
          scopes TEXT,
          website TEXT,
          created_at INTEGER
        );

# Oauth codes
CREATE TABLE oauth_codes (
        code TEXT PRIMARY KEY,
        client_id TEXT,
        redirect_uri TEXT,
        scope TEXT,
        username TEXT,
        created_at INTEGER
      );

# OAuth tokens 
CREATE TABLE oauth_tokens (
          access_token TEXT PRIMARY KEY,
          client_id TEXT,
          username TEXT,
          scope TEXT,
          created_at INTEGER
        );
๐Ÿ’ก
oauth_apps 
- ์•ฑ๋“ฑ๋ก(pinaafore์—๋งŒ ๋กœ๊ทธ์ธ ํ–ˆ์œผ๋ฉด ๋ฐ์ดํ„ฐ 1๊ฐœ)
App registration (1 data entry if logged into Pinaafore only)

oauth_tokens 
- ์•ฑํ† ํฐ(์œ ์ € 100๋ช…์ด๋ฉด 100๋ช…์ด ๋กœ๊ทธ์ธํ•œ ํšŸ์ˆ˜ ๋งŒํผ ,์ฆ‰ 100+)
App tokens (based on the number of logins by the 100 usersโ€”i.e., 100+).

oauth_codes 
- ์“ฐ๊ณ  ๋ฒ„๋ฆฌ๋Š” ์ฝ”๋“œ ํ•ญ์ƒ ๋ฐ์ดํ„ฐ ๊ฐฏ์ˆ˜๋Š” 0
Discarded code; the data count is always 0

๐Ÿ“ ๋ผ์šฐํŠธ ๋ฐ ํŒŒ์ผ ์ถ”๊ฐ€ / Add Routes and Files

โœ”๏ธ app/api/v1/instance/route.ts

— ๋™์ž‘ ๊ฐœ์š” / Operation Overview

 /api/v1/instance GET 
-> Pinafore๊ฐ€ ๋‚ด ์„œ๋ฒ„์ •๋ณด๋ฅผ ๊ฐ€์ ธ๊ฐ‘๋‹ˆ๋‹ค.
Pinafore retrieves my server information.

-> ์ด๋•Œ ๋‚ด ์„œ๋ฒ„๋Š” ๋งˆ์Šคํ† ๋ˆ ์„œ๋ฒ„๋กœ ์ธ์‹ํ•˜๋„๋ก ์„ค์ •์„ ํ•ฉ๋‹ˆ๋‹ค.
At this point, configure your server so that it is recognized as a Mastodon server.

— ์ฝ”๋“œ / Code

// app/api/v1/instance/route.ts 
import { NextResponse } from 'next/server';

const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';

export async function GET() {
  return NextResponse.json({ ... ...
})}

— ํ…Œ์ŠคํŠธ / Test

# ํ„ฐ๋ฏธ๋„์—์„œ ์‹คํ–‰ / Run in the terminal

 % curl https://aloy-horizon.duckdns.org/api/v1/instance

# ์‘๋‹ต / Response
{"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":["en","ko"],"registrations":true,"approval_required":false,"invites_enabled":false,"configuration":{"statuses":{"max_characters":500,"max_media_attachments":4}},"rules":[]}

%  
v1/instance

โœ”๏ธ /api/v1/apps POST

— ๋™์ž‘๊ฐœ์š” / Operation Overview

# Pinafore ์•ฑ์„ ๋‚ด ์„œ๋ฒ„์— ์•ฑ๋“ฑ๋ก ํ•˜๊ธฐ
# Registering the Pinafore app on my server

# - /api/v1/apps POST -> Pinafore ์•ฑ ๋“ฑ๋ก! client_id ๋ฐœ๊ธ‰!
# - /api/v1/apps POST -> Register Pinafore app! Issue client_id!

# Pinafore๊ฐ€ ๋ณด๋‚ด๋Š” ์ •๋ณด
# Information from Pinafore
POST /api/v1/apps
{
  "client_name": "Pinafore",
  "redirect_uris": "https://pinafore.social/settings/instances/add",
  "scopes": "read write follow push",
  "website": "https://pinafore.social"
}

— ์ฝ”๋“œ / Code

// app/api/v1/apps/route.ts - โœ… myapp33-2 - Pinafore ์•ฑ ๋“ฑ๋ก / Pinafore App Registration
import { NextResponse } from 'next/server';
import db from '@/lib/db';
import { randomUUID } from 'crypto';

const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';

export async function POST(req: Request) { ... ... }

— ํ…Œ์ŠคํŠธ / Test

# ํ„ฐ๋ฏธ๋„์—์„œ ์‹คํ–‰ / Run in the terminal

% curl -X POST https://aloy-horizon.duckdns.org/api/v1/apps \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Pinafore",
    "redirect_uris": "urn:ietf:wg:oauth:2.0:oob",
    "scopes": "read write follow push",
    "website": "https://pinafore.social"
  }'

# ์‘๋‹ต / Response
{"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":""}                  

% 
v1/apps

โœ”๏ธ app/oauth/authorize/route.ts

— ๋™์ž‘๊ฐœ์š” / Operation Overview

# Pinafore์—์„œ ์•„๋ž˜์˜ ๋ผ์šฐํŠธ ์‹คํ–‰ํ•ด์„œ ๋ธŒ๋ผ์šฐ์ € ์Šน์ธํ™”๋ฉด ํŒ์—…:
# Execute the route below in Pinafore to open the browser approval screen popup

https://aloy-horizon.duckdns.org/oauth/authorize?
  client_id=bdbaf975...
  &redirect_uri=urn:ietf:wg:oauth:2.0:oob
  &response_type=code
  &scope=read write follow push

# ํ™”๋ฉด์˜ ์Šน์ธ ๋ˆ„๋ฅด๋ฉด code ๋ฐœ๊ธ‰
# Press Approve on the screen to issue a code

— ์ฝ”๋“œ / Code

// app/oauth/authorize/route.ts - โœ… myapp33-3 - ๋กœ๊ทธ์ธ ์Šน์ธ ํŽ˜์ด์ง€! / Login Authorization Page!
import { NextResponse } from 'next/server';
import db from '@/lib/db';
import { randomUUID } from 'crypto';

export async function GET(req: Request) { ... ... }
export async function POST(req: Request) { ... ... }

— ํ…Œ์ŠคํŠธ / Test

# Terminal 
% curl -X POST https://aloy-horizon.duckdns.org/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=bdbaf975-d04b-4dc6-8525-6b94e9d09752&client_secret=ee233e0e-a99f-4ca4-8487-09cce8219a1c4115f412-d65b-4268-944f-f0e6aed5199d&code=df066428-3240-45cf-aa2a-6c4f809f875c&grant_type=authorization_code&redirect_uri=urn:ietf:wg:oauth:2.0:oob"

# ์‘๋‹ต / Response
{"access_token":"2f7f89d8-20e0-4e63-acac-5d3bea32deb11e323217-71b3-455a-8320-fc348e305611","token_type":"Bearer","scope":"read write follow","created_at":1788651972}

%

— ์•„๋ž˜์˜ ํ™”๋ฉด์—์„œ pinafore์˜ ์ •๋ณด๊ฐ€ ์—†์œผ๋ฉด auth ์ฝ”๋“œ๊ฐ€ ๋ณด์ž…๋‹ˆ๋‹ค.(ํ…Œ์ŠคํŠธ ๋‹จ๊ณ„)
If there is no information for Pinafore on the screen below, the auth code will be displayed.(Testing phase)

— pinafore์ •๋ณด๋ฅผ ์ •์ƒ์ ์œผ๋กœ ํ™•์ธ๋˜๋ฉด pinafore ํƒ€์ž„๋ผ์ธ์„ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
Once the Pinafore information is successfully verified, you will navigate to the Pinafore timeline.

confirmed, auth code

โœ”๏ธ app/api/v1/accounts/verify_credentials/route.ts

— Pinafore๊ฐ€ ๋ณด๋Š” ๋‚ด ์„œ๋ฒ„์ •๋ณด,ํƒ€์ž„๋ผ์ธ์— ํ‘œ์‹œ๋˜๋Š” ์ •๋ณด
My server information as seen by Pinafore, information displayed on the timeline

import { NextResponse } from 'next/server';
import db from '@/lib/db';

const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';

export async function GET(req: Request) { ... }
export async function OPTIONS() { ... }

— ํ…Œ์ŠคํŠธ / Test

curl https://aloy-horizon.duckdns.org/api/v1/accounts/verify_credentials \
  -H "Authorization: Bearer 2f7f89d8-20e0-4e63-acac-5d3bea32deb11e323217-71b3-455a-8320-fc348e305611"
% curl https://aloy-horizon.duckdns.org/api/v1/accounts/verify_credentials \
  -H "Authorization: Bearer 2f7f89d8-20e0-4e63-acac-5d3bea32deb11e323217-71b3-455a-8320-fc348e305611"

# ์‘๋‹ต / Response
{"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":[]}}                                                                            

% 

โœ”๏ธ app/api/v1/timelines/home/route.ts

— ๋กœ๊ทธ์ธ ํ›„ ํƒ€์ž„๋ผ์ธ (์•„์ง ๋นˆ ํƒ€์ž„๋ผ์ธ)
Timeline after logging in (currently empty)

export async function GET() {
  // โœ… ๋นˆ ํƒ€์ž„๋ผ์ธ! (๋‚˜์ค‘์— Outbox ๊ธ€ ๊ฐ€์ ธ์˜ค๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Œ!) / Empty timeline! (Later, you can fetch posts from the Outbox!)
  return NextResponse.json([]);
}
export async function OPTIONS() { ... }

โœ”๏ธ app/api/v1/statuses/route.ts

— ๋กœ๊ทธ์ธ ํ›„ ํƒ€์ž„๋ผ์ธ์— ๋ณด์ด๋Š” ๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ๊ธ€(์•„์ง DB์ €์žฅ ๊ตฌํ˜„ ์•ˆํ•จ.)
Posts I’ve written that appear on the timeline after logging in (DB storage implementation not yet complete).

import { NextResponse } from 'next/server';
import db from '@/lib/db';
import { randomUUID } from 'crypto';

const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';

export async function POST(req: Request) { ... }
export async function OPTIONS() { ... }

๐Ÿ“ CORS์„ค์ • ์ถ”๊ฐ€ / Add CORS configuration

โœ”๏ธ ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— middlewatre.ts ํŒŒ์ผ ์ถ”๊ฐ€
Add a middleware.ts file to the project root.

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(req: NextRequest) { ... }
export const config = {
  matcher: ['/api/:path*', '/oauth/:path*', '/.well-known/:path*', '/users/:path*'],
};

โœ”๏ธ OPTIONS ํ•จ์ˆ˜ ์ถ”๊ฐ€ / Add OPTIONS function

— ์•„๋ž˜์˜ ํŒŒ์ผ๋“ค์˜ ๊ฐ€์žฅํ•˜๋‹จ์— OPTIONSํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
Add the OPTIONS function to the very bottom of the files listed below.

1) OPTIONS ํ•จ์ˆ˜ / OPTIONS Function

// CORS ์—๋Ÿฌ ๋ฐฉ์ง€์šฉ OPTIONS ์ฒ˜๋ฆฌ / Handle OPTIONS to prevent CORS errors
export async function OPTIONS() {
  return new NextResponse(null, {
    status: 204,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  });
}

2)ํŒŒ์ผ / Files

app/api/v1/instance/route.ts
app/api/v1/apps/route.ts 
app/api/v1/accounts/verify_credentials/route.ts 
app/api/v1/timelines/home/route.ts
app/api/v1/statuses/route.ts
app/oauth/token/route.ts
app/oauth/authorize/route.ts 

— ์„ค์ •์„ ์•ˆํ•˜๋ฉด pinafore.social์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
If you do not configure the settings, an error like the one below may occur on pinafore.social.

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.

— ํ…Œ์ŠคํŠธ / Test

# preflight Test
curl -i -X OPTIONS https://aloy-horizon.duckdns.org/api/v1/instance \
  -H "Origin: https://pinafore.social" \
  -H "Access-Control-Request-Method: GET"

# ์œ„์˜ curl์„ ํ„ฐ๋ฏธ๋„์—์„œ ์‹คํ–‰ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์— ์ธ์ฆ ํ—ˆ์šฉ ํ™”๋ฉด ์‹คํ–‰ ๋จ.
# Running the curl command above in the terminal triggers the authentication approval screen in your browser.

๐Ÿ“ ์ตœ์ข… ํ…Œ์ŠคํŠธ / Final Test

โœ”๏ธ ์œ„์˜ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋ฉด ๋กœ์ปฌ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ณ  pinafore.social์—์„œ ๋กœ๊ทธ์ธ์„ ํ•ด๋ด…๋‹ˆ๋‹ค.
If all the tests above run successfully, start the local server and try logging in at pinafore.social.

โœ”๏ธ ๋กœ๊ทธ์ธ์„ ์™„๋ฃŒ ํ•˜๋ฉด ์‹ค์ œ๋กœ ๊ธ€์“ฐ๊ธฐ๊ฐ€ ์‹คํ–‰๋˜๋Š”์ง€ ํ™•์ธํ•ด ๋ด…๋‹ˆ๋‹ค.
After logging in, verify that the writing function actually works.

โœ”๏ธ ๊ธ€์“ฐ๊ธฐ ๋ฐ์ดํ„ฐ๋Š” ์ง€๊ธˆ์€ outbox์— ์ €์žฅํ•˜์ง€ ์•Š๊ณ  pinafore์— ๋ณด์ด๋Š”์ง€๋งŒ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
For now, writing data is not saved to the outbox; we only verify that it appears in Pinafore.

aloy-horizon.duckdns.org(pinafore.social

์š”ํ•œ๋ณต์Œ 8์žฅ 32์ ˆ / John 8:32

“๊ทธ๋ฆฌ๊ณ  ๋„ˆํฌ๋Š” ์ง„๋ฆฌ๋ฅผ ์•Œ๊ฒŒ ๋  ๊ฒƒ์ด๋ฉฐ, ์ง„๋ฆฌ๊ฐ€ ๋„ˆํฌ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ํ•  ๊ฒƒ์ด๋‹ค.”

“Then you will know the truth ,and the truth will set you free”

Leave a Reply