[nextjs]SNS Server-29(myapp39) 

👉🏻 회원가입 및 로그인과 관련된 기능입니다.
These are features related to sign-up and login.

👉🏻 myapp39에서는 이전에 완료되지 않았던 로그인 기능을 완성합니다.
In myapp39, we complete the login functionality that was not finished previously.

👉🏻 전체 코드는 깃허브에서 확인 할 수 있습니다.
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 -> Write Post
│   ├── api/v1/timelines/home/route.ts -> timeline
│   ├── oauth/authorize/route.ts -> auth
│   ├── oauth/token/route.ts -> auth
│   ├── api/v1/search/home/route.ts -> search
│   ├── api/v2/search/home/route.ts -> search
│   ├── api/v1/accounts/[id]/followers/route.ts -> followers
│   ├── api/v1/accounts/[id]/following/route.ts -> following
│   ├── api/auth/signup/route.ts  -> Signup
│   ├── api/auth/login/route.ts   -> Login
│   ├── api/auth/logout/route.ts  -> Logout
│   ├── auth/signup/page.tsx -> Signup UI
│   ├── auth/signup/page.tsx -> Login UI
│   ├── 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


📁 라우트 추가 /. Add Route

✔️ app/api/auth/signup/route.ts – 회원가입 / Signup

✔️ app/api/auth/login/route.ts – 로그인 / Login

✔️ app/auth/login/page.tsx – 로그인 UI / Login UI

✔️ app/auth/signup/page.tsx – 회원가입 UI / Signup UI

✔️ app/api/auth/logout/route.ts – 로그아웃 / Logout

📁 코드수정 / Code Modification

✔️ lib/auth.ts

export function getUsernameFromRequest(req: Request | NextRequest): string | null { ... }
export function getUsernameFromToken(token: string): string | null { ... }

✔️ app/api/v1/instance/route.ts

— OPTIONS함수 제거(middleware.ts에서 처리)
Remove OPTIONS function (handled in middleware.ts)

// ✅ myapp39 - app/api/v1/instance/route.ts - middleware가! CORS 처리!하니까! 깔끔!
import { NextResponse } from 'next/server';

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

export async function GET() {
  return NextResponse.json({
    uri: `https://${DOMAIN}`, 
    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://${DOMAIN}`
    },
    stats: {
      user_count: 1,
      status_count: 100,
      domain_count: 1000
    },
    thumbnail: `https://${DOMAIN}/icon.png`,
    languages: ['en', 'ko'],
    registrations: true,
    approval_required: false,
    invites_enabled: false,
    configuration: {
      statuses: {
        max_characters: 500,
        max_media_attachments: 4
      }
    },
    rules: []
  });
}
// OPTIONS함수 제거 middleware.ts에서 처리
// Remove OPTIONS function, handle in middleware.ts

✔️ app/oauth/authorize/route.ts – localhost:3000

... ...
const DOMAIN = process.env.DOMAIN || 'aloy-horizon.duckdns.org';
//const DOMAIN = 'aloy-horizon.duckdns.org';
const ORIGIN = `https://${DOMAIN}`;
... ...
export async function GET(req: Request) { 
... ...
  // ✅ myapp39 - 로그인 체크 / login check
  const username = getUsernameFromCookie(req);
... ...
 // ✅ for CORS error
  if (!username) {
      const loginUrl = new URL('/auth/login', ORIGIN); 
      const realNextUrl = `${ORIGIN}${url.pathname}${url.search}`;
      loginUrl.searchParams.set('next', realNextUrl);
      return NextResponse.redirect(loginUrl.toString());
  }
... ...
}

✔️ caddyfile

aloy-horizon.duckdns.org {
  reverse_proxy localhost:3000 {
    header_up Host {host}
    header_up X-Real-IP {remote}
    header_up X-Forwarded-For {remote}
    header_up X-Forwarded-Proto {scheme}
    header_up X-Forwarded-Host {host}
  }
 encode gzip
 
  # CORS는! Next.js가! 처리!하니까! Caddy에서! 헤더! 추가! 금지!
  # Next.js handles CORS, so do not add headers in Caddy!
}

✔️ middleware.ts

— Pinafore에서 로그인 하면! ERR_CONNECTION_REFUSED + CORS
Logging in on Pinafore results in ERR_CONNECTION_REFUSED + CORS!

— 모든 요청에 CORS처리 적용 / Apply CORS handling to all requests.

// ✅ myapp39/middleware.ts - CORS 전체 처리 / Global CORS Handling
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(req: NextRequest) {

  // ✅ OPTIONS preflight 먼저 처리
  // Process the OPTIONS preflight request first.
  if (req.method === 'OPTIONS') {
    return new NextResponse(null, {
      status: 204,
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, Authorization',
        'Access-Control-Max-Age': '86400',
      },
    });
  }

  // ✅ 일반 요청 / General Request
  const res = NextResponse.next();
  res.headers.set('Access-Control-Allow-Origin', '*');
  res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  
  return res;
}

export const config = {
  matcher: ['/api/:path*', '/oauth/:path*', '/.well-known/:path*', '/users/:path*'],
};

📁 테스트 / Test

✔️ 터미널 테스트 / Terminal test

# 회원가입 / Signup
curl -X POST https://aloy-horizon.duckdns.org/api/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"username":"test1","email":"test1@test.com","password":"123456","display_name":"Test1"}'

# Database
sqlite> select username from users;
test1
user1
user2
sqlite> 

✔️ 웹사이트 로그인 / Website login

— https://aloy-horizon.duckdns.org/auth/login

1)pinafore에서 로그인 할경우
When logging in on Pinafore

pinafore login

2)웹사이트에서 로그인 할경우
When logging in on the website

website login

✔️ 로그아웃 / Logout

Logout

✔️ 웹 회원가입 / Website Signup

— https://aloy-horizon.duckdns.org/auth/signup

요한복음 8장 32절 / John 8:32

“그리고 너희는 진리를 알게 될 것이며, 진리가 너희를 자유롭게 할 것이다.”

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

Leave a Reply