ππ» νμκ°μ
λ° λ‘κ·ΈμΈκ³Ό κ΄λ ¨λ κΈ°λ₯μ
λλ€.
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

2)μΉμ¬μ΄νΈμμ λ‘κ·ΈμΈ ν κ²½μ°
When logging in on the website

βοΈ λ‘κ·Έμμ / 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”