[nextjs]SNS Server-51(myapp61)Β 

πŸ‘‰πŸ» myapp61μ—μ„œλŠ” μ•Œλ¦ΌνŽ˜μ΄μ§€μ—μ„œ 데이터가 쀑볡 ν‘œμ‹œλ˜λŠ” 문제λ₯Ό μˆ˜μ • ν•©λ‹ˆλ‹€.
In myapp61, the issue where data was displayed redundantly on the notification page is being fixed.

πŸ‘‰πŸ» νƒ€μž„λΌμΈμ—μ„œ 데이터가 μ—†μœΌλ©΄ Home,Local,Federated 탭이 μ‚¬λΌμ§€λŠ” 문제λ₯Ό μˆ˜μ •ν•©λ‹ˆλ‹€.
Fixes an issue where the Home, Local, and Federated tabs disappear when there is no data in the timeline.

πŸ‘‰πŸ» λ‚΄κ°€ μž‘μ„±ν•˜μ§€ μ•Šμ€ 글도 DELETEν‘œμ‹œλ˜λŠ” 문제λ₯Ό μˆ˜μ •ν•©λ‹ˆλ‹€.
Fixes an issue where posts I did not write were marked for deletion.

πŸ‘‰πŸ» 전체 μ½”λ“œλŠ” κΉƒν—ˆλΈŒμ—μ„œ 확인 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
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
β”‚   β”œβ”€β”€ hooks/useCursorPagination.ts    -> Pagination Hook
β”‚   β”œβ”€β”€ 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(deprecated)
β”‚   β”œβ”€β”€ api/timeline/route.ts  -> Timeline API
β”‚   β”œβ”€β”€ api/followinglist/route.ts  -> followinglist API
β”‚   β”œβ”€β”€ api/followerslist/route.ts  -> followerslist 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_back/route.ts -> timeline(Depreacted)
β”‚   β”œβ”€β”€ api/v1/media/route.ts -> Image upload
β”‚   β”œβ”€β”€ api/v1/media/[id]/route.ts  -> Image Delete 
β”‚   β”œβ”€β”€ api/v1/instance/rules/route.ts -> Feature not implemented.
β”‚   β”œβ”€β”€ api/v1/instance/domain_blocks/route.ts -> Feature not implemented.
β”‚   β”œβ”€β”€ api/v1/filters/route.ts -> Feature not implemented.
β”‚   β”œβ”€β”€ api/v1/lists/route.ts -> Feature not implemented.
β”‚   β”œβ”€β”€ api/v1/announcements/route.ts -> Feature not implemented.
β”‚   β”œβ”€β”€ api/v1/timelines/_public_back/route.ts -> Feature not implemented.(Deprecated)
β”‚   β”œβ”€β”€ api/v1/timelines/[type]/route.ts -> Timeline API
β”‚   β”œβ”€β”€ api/v1/statuses/[id]/context/route.ts -> Post Check,Delete
β”‚   β”œβ”€β”€ oauth/authorize/route.ts -> auth
β”‚   β”œβ”€β”€ oauth/token/route.ts -> auth
β”‚   β”œβ”€β”€ oauth/login-and-authorize/route.ts -> Tursky,Pinafore login processing
β”‚   β”œβ”€β”€ oauth/revoke/route.ts -> Feature not implemented.
β”‚   β”œβ”€β”€ api/v1/search/home/route.ts -> search
β”‚   β”œβ”€β”€ api/v2/search/home/route.ts -> search
β”‚   β”œβ”€β”€ api/v2/notifications/policy/route.ts-> Feature not implemented.
β”‚   β”œβ”€β”€ api/v1/accounts/[id]/followers/route.ts -> followers
β”‚   β”œβ”€β”€ api/v1/accounts/[id]/following/route.ts -> following
β”‚   β”œβ”€β”€ api/v1/statuses/[id]/route.ts  -> Post delete(pinafore)
β”‚   β”œβ”€β”€ api/v1/statuses/[id]/favourite/route.ts -> favourite(for pinafore)
β”‚   β”œβ”€β”€ api/v1/statuses/[id]/unfavourite/route.ts -> unfavourite(for pinafore)
β”‚   β”œβ”€β”€ api/v1/statuses/[id]/reblog/route.ts  -> reblog[boost](for pinafore)
β”‚   β”œβ”€β”€ api/v1/statuses/[id]/unreblog/route.ts  -> unreblog[unboost](for pinafore)
β”‚   β”œβ”€β”€ api/auth/signup/route.ts        -> Website Signup
β”‚   β”œβ”€β”€ api/auth/login/route.ts         -> Website Login
β”‚   β”œβ”€β”€ api/auth/logout/route.ts        -> Website Logout
β”‚   β”œβ”€β”€ api/auth/refresh/route.ts       -> Refresh Token
β”‚   β”œβ”€β”€ api/cleanup-orphan/route.ts     -> post,outbox clean up
β”‚   β”œβ”€β”€ api/auth/me/route.tsx           -> Login Check
β”‚   β”œβ”€β”€ 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(Server, 404)
β”‚   β”‚   β”œβ”€β”€ ClientPage.tsx.        -> Timeline UI(Client,Call Active Themes)
β”‚   β”‚   └── _components/themes/
β”‚   β”‚       β”œβ”€β”€ themeex/ThemeexTheme.tsx      -> Example Theme
β”‚   β”‚       β”œβ”€β”€ themeex/FollowersList.tsx     -> Theme Example, FollowersList
β”‚   β”‚       β”œβ”€β”€ themeex/FollowingList.tsx     -> Theme Example, FollowingList
β”‚   β”‚       β”œβ”€β”€ themeex/NotificationsList.tsx -> Theme Example, NotificationsList
β”‚   β”‚       β”œβ”€β”€ themeex/TimelineList.tsx      -> Theme Example, TimelineList
β”‚   β”‚       β”œβ”€β”€ pinafore/PinaforeTheme.tsx -> Theme 1
β”‚   β”‚       β”œβ”€β”€ pinafore/FollowersList.tsx -> Theme 1, FollowersList
β”‚   β”‚       β”œβ”€β”€ pinafore/FollowingList.tsx -> Theme 1, FollowingList
β”‚   β”‚       β”œβ”€β”€ pinafore/NotificationsList.tsx -> Theme 1, NotificationsList
β”‚   β”‚       β”œβ”€β”€ pinafore/TimelineList.tsx  -> Theme 1, TimelineList
β”‚   β”‚       β”œβ”€β”€ 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
β”‚   β”œβ”€β”€ visibility.ts          -> Visibility(Public,Unlisted,Private,Direct)
β”‚   └── 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
β”œβ”€β”€ proxy.ts                   -> middleware.ts - >proxy.ts(for Nextjs16)
β”œβ”€β”€ postcss.config.mjs         -> for Nextjs16
└── package.json

πŸ“ ν”„λ‘œμ νŠΈ μ‹œμž‘ / Project Start


πŸ“ μ½”λ“œ μˆ˜μ • / Code Modification

βœ”οΈ νƒ€μž„λΌμΈμ—μ„œ 데이터가 μ—†μœΌλ©΄ νƒ­μ‚¬λΌμ§€λŠ” 였λ₯˜ μˆ˜μ •
Fixed an error where the tab would disappear when there was no data in the timeline.

βœ”οΈ λ‘œκ·ΈμΈν•˜μ§€ μ•ŠμœΌλ©΄ ν™ˆνƒ­μ€ 둜컬APIλ₯Ό 호좜
If not logged in, the Home tab calls the local API

— app/usersui/[username]/_components/themes/Themeex/TimelineList.tsx

  const {
    flatData,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useCursorPagination({
... ...

      // βœ… myapp61 - API μ£Όμ†Œ μˆ˜μ • / modify API Address
      // - fetcher μ•ˆμ—μ„œ 직접 둜그인 μ—¬λΆ€ 체크 ν•˜κΈ° / Check login status directly within the fetcher.
      let me: string | null = null;
      try {
        const meRes = await fetch(`/api/auth/me`, { credentials: 'include', cache: 'no-store' });
        if (meRes.ok) {
          const j = await meRes.json();
          me = j.username || null;
        }
      } catch {}

      const effectiveUser = me || currentUser;

      let url = '';
      if (tab === 'home') {
        if (effectiveUser) {
          url = `/api/v1/timelines/home?${params.toString()}`;
        } else {
          params.set('local', 'true');
          url = `/api/v1/timelines/public?${params.toString()}`;
        }
      } else if (tab === 'local') {
        params.set('local', 'true');
        url = `/api/v1/timelines/public?${params.toString()}`;
      } else {
        url = `/api/v1/timelines/public?${params.toString()}`;
      }
... ...
  });

  // if (data.length === 0) {
  //   return <div className="p-5 text-[#999]">νƒ€μž„λΌμΈμ΄ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€ / No posts</div>;
  // }

  return ( ... ...

      {/* βœ… myapp61 - No postsλŠ” selector μ•„λž˜μ— ν‘œμ‹œ / "No posts" is displayed below the selector. */} 
      {data.length === 0 && (
        <div className="p-10 text-center text-[#999]">νƒ€μž„λΌμΈμ΄ λΉ„μ–΄μžˆμŠ΅λ‹ˆλ‹€ / No posts in {tab}</div>
      )}
... ...
);

1)둜그인 뢀뢄은 μ„œλ²„κ°€ μ•„λ‹Œ ν΄λΌμ΄μ–ΈνŠΈ λΆ€λΆ„μ—μ„œλŠ” getUserν•¨μˆ˜λ‘œ 인증 체크할 수 μ—†μŒ
Regarding the login functionality, authentication cannot be verified using the getUser function on the client side; this must be handled on the server side.

2)ν΄λΌμ΄μ–ΈνŠΈλŠ” 인증 확인을 μœ„ν•΄ /api/auth/me 라우트 μ‚¬μš©ν•¨
The client uses the /api/auth/me route to verify authentication.

3)둜그인 ν•œ 경우 home라우트 λ‘œκ·ΈμΈν•˜μ§€ μ•ŠμœΌλ©΄ public라우트둜 이동
If logged in, navigate to the home route; if not logged in, navigate to the public route.


βœ”οΈ νƒ€μž„λΌμΈ Unknown 및 였λ₯˜ / Timeline Unknown and Errors

— app/api/v1/timelines/[type]/route.ts

1)νƒ€μž„λΌμΈ APIμ—μ„œ acct값을 잘λͺ» μ€˜μ„œ λ°œμƒν•˜λŠ” 였λ₯˜
An error caused by providing an incorrect ‘acct’ value in the Timeline API.

    return {
      id: String(row.id),
      uri: longId, url: longId,
      account: {
        // id: isMy ? accountId : `remote_${row.username}`, βœ… myapp61
        id: isMy ? accountId : `remote_${row.username}@${acctDomain}`,
        username: row.username, 
        // acct: `${row.username}@${DOMAIN}`, βœ… myapp61
        acct: displayAcct, 
        //display_name: row.username, 
        display_name: accountUsername,
        avatar: `https://${DOMAIN}/icon.png`, avatar_static: `https://${DOMAIN}/icon.png`,
        header: `https://${DOMAIN}/header.png`, header_static: `https://${DOMAIN}/header.png`,
      },
... ...
}

βœ”οΈ FollowingList,FollowerList UI 였λ₯˜/error

— app/usersui/[username]/_components/theme/pinafore/FollowingList.tsx

— app/usersui/[username]/_components/theme/pinafore/FollowersList.tsx

 <div className="w-9 h-9 rounded-full bg-[#6364ff] text-white flex items-center justify-center font-extrabold shrink-0">
... ...
          {/* <button className="ml-auto px-3 py-1 bg-[#6364ff] text-white rounded-full text-xs font-bold hover:bg-[#5556ee]">
            Following
          </button> */} --> or Delete
# v1 API
https://aloy-horizon.duckdns.org/api/v1/timelines/public 
https://aloy-horizon.duckdns.org/api/v1/timelines/public?local=true

# pinafore ν…Œλ§ˆμ—μ„œ μ‚¬μš©ν•˜λŠ” 이전 API
https://aloy-horizon.duckdns.org/api/timeline?username=user1&type=home

βœ”οΈ 리λͺ¨νŠΈ 글에도 DELETEλ²„νŠΌ λ³΄μ΄λŠ”κ²ƒ μˆ˜μ •
Fixed the issue where the DELETE button was visible on remote posts.

— 둜그인 ν›„ λ‚΄κ°€ μž‘μ„±ν•œ κΈ€μ—λ§Œ DELETEλ²„νŠΌ λ³΄μž„
The DELETE button appears only on posts I have written after logging in.

      // βœ… myapp61
      const withMine = arr.map((p:any) => {
        const acct = p.account?.acct || '';
        const username = p.account?.username || p.username || '';
        const acctDomain = acct.includes('@')? acct.split('@')[1] : DOMAIN;
        const isLocalAccount = acctDomain === DOMAIN;

        return {
        ...p,
          isMine:!!(currentUser && isLocalAccount && username === currentUser)
        };
      });  
... ...              
  {/* {currentUser && isMine && (
                  <button onClick={() => onDelete(p.id)} className="text-[#e0245e] ml-auto text-xs hover:underline">
                    πŸ—‘ Delete
                  </button>
                )} */}
                {/* βœ… myapp61 */}
                {isMine && (
                  <button onClick={() => onDelete(p.id)} className="text-[#e0245e] ml-auto text-xs">
                    πŸ—‘ Delete
                  </button>
                )}

βœ”οΈ SNSνŽ˜μ΄μ§€κ°€ 처음 싀행될떄 νƒ€μž„λΌμΈμ΄ λΉ„μ–΄μžˆλŠ” 걸둜 ν‘œμ‹œλ˜λŠ” 였λ₯˜
An error where the timeline appears empty when the social media page is first launched.

— app/usersui/[username]/ClientPage.tsx

  // βœ… myapp60 + myapp61
  // - νŽ˜μ΄μ§€λ„€μ΄μ…˜μ€ TimelineListκ°€ λ‹΄λ‹Ή, μ—¬κΈ°μ„  빈 λ°°μ—΄λ‘œ μ‹œμž‘
  // - Pagination is handled by TimelineList; start with an empty array here.
  useEffect(() => {
    if (!username) return;
    //setTimeline([]);
    setLoading(false);
  }, [username]);

πŸ“ ν…ŒμŠ€νŠΈ / Test

βœ”οΈ Unknown error

Unknown error

βœ”οΈ νŒ”λ‘œμž‰,νŒ”λ‘œμ›Œλ¦¬μŠ€νŠΈ UI 였λ₯˜,μ•Œλ¦Όλ°μ΄ν„° 쀑볡 처리
UI errors in following/follower lists; handling of duplicate notification data.

Notification

βœ”οΈ 리λͺ¨νŠΈ μ„œλ²„ μ‚­μ œκΈˆμ§€,μ•‘ν„° μ •ν™•νžˆ ν‘œμ‹œ,리λͺ¨νŠΈ μ„œλ²„μ•„μ΄μ½˜ν‘œμ‹œ
Prevent remote server deletion, display actors accurately, show remote server icons.

Remote server

λ§ˆνƒœλ³΅μŒ 21μž₯ 22절 / Matthew 21: 22

“λ„ˆν¬κ°€ 기도할 λ•Œμ— 무엇이든지 λ―Ώκ³  κ΅¬ν•˜λŠ” 것은 λ‹€ λ°›μœΌλ¦¬λΌ ν•˜μ‹œλ‹ˆλΌ”

“And everything you ask in prayer, believing, you will receive.”

Leave a Reply