-
Notifications
You must be signed in to change notification settings - Fork 7
fix: resolve auth api and dashboard issues #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,21 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "module": "nodenext", | ||
| "moduleResolution": "nodenext", | ||
| "resolvePackageJsonExports": true, | ||
| "esModuleInterop": true, | ||
| "isolatedModules": true, | ||
| "declaration": true, | ||
| "removeComments": true, | ||
| "emitDecoratorMetadata": true, | ||
| "experimentalDecorators": true, | ||
| "allowSyntheticDefaultImports": true, | ||
| "target": "ES2023", | ||
| { | ||
| "compilerOptions": { | ||
| "module": "nodenext", | ||
| "moduleResolution": "nodenext", | ||
| "resolvePackageJsonExports": true, | ||
| "esModuleInterop": true, | ||
| "isolatedModules": true, | ||
| "declaration": true, | ||
| "removeComments": true, | ||
| "emitDecoratorMetadata": true, | ||
| "experimentalDecorators": true, | ||
| "allowSyntheticDefaultImports": true, | ||
| "target": "ES2023", | ||
| "sourceMap": true, | ||
| "rootDir": "./src", | ||
| "outDir": "./dist", | ||
| "baseUrl": "./", | ||
| "ignoreDeprecations": "6.0", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check pinned typescript version in backend
fd package.json backend -x jq -r '.devDependencies.typescript // .dependencies.typescript' {}Repository: HappyHackingSpace/CommunityHub Length of output: 175 🌐 Web query:
💡 Result: In TypeScript 5.7.3, the compiler option "ignoreDeprecations": "6.0" is invalid and will trigger diagnostic error TS5103: "Invalid value for '--ignoreDeprecations'" [1][2][3]. The "ignoreDeprecations": "6.0" setting is a feature introduced specifically in TypeScript 6.0 to allow developers to suppress deprecation warnings for features that are planned for removal in TypeScript 7.0 [4]. Because TypeScript 5.7.3 predates the 6.0 release, it does not recognize "6.0" as a valid argument for this configuration option [4][5]. If you are encountering deprecation warnings in TypeScript 5.7.3, you must use a version-appropriate value (such as "5.0") if supported by your specific environment, or address the underlying configuration issue directly by removing or updating the deprecated feature [6][1]. Attempting to use "6.0" in any TypeScript 5.x version will consistently result in error TS5103 [1][2][3]. Citations:
🤖 Prompt for AI Agents |
||
| "paths": { | ||
| "src/*": ["src/*"], | ||
| "@/*": ["src/*"] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| AUTH_SECRET={AUTH_SECRET} | ||
| AUTH_SECRET=XKZ7Z1O3bhU4caz2D8ouu7dKhXyQ+ZUhsjvTwtHxp10= | ||
| NEXTAUTH_SECRET=XKZ7Z1O3bhU4caz2D8ouu7dKhXyQ+ZUhsjvTwtHxp10= | ||
|
Comment on lines
+1
to
+2
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win Use an obvious placeholder instead of a real-looking secret value. Both 🧰 Tools🪛 Betterleaks (1.6.0)[high] 1-1: Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (generic-api-key) [high] 2-2: Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (generic-api-key) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| AUTH_URL=http://localhost:3001 | ||
| NEXTAUTH_URL=http://localhost:3001 | ||
| AUTH_GOOGLE_ID={AUTH_GOOGLE_ID} | ||
| AUTH_GOOGLE_SECRET={AUTH_GOOGLE_SECRET} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| "use client"; | ||
|
|
||
| import { CreatePostInput } from "@/components/dashboard/create-post-input"; | ||
| import { PostCard } from "@/components/dashboard/post-card"; | ||
| import { RightSidebar } from "@/components/dashboard/right-sidebar"; | ||
|
|
||
| import { useActivityFeed } from "@/hooks/use-activity-feed"; | ||
| import { useCommunities } from "@/hooks/use-communities"; | ||
| import { useTenantContext } from "@/components/providers/tenant-provider"; | ||
| import { useSession } from "next-auth/react"; | ||
| import { useRouter, useSearchParams } from "next/navigation"; | ||
| import { useEffect } from "react"; | ||
|
|
||
| export function DashboardContent() { | ||
| const { data: session } = useSession(); | ||
| const { communities, isLoading: communitiesLoading } = useCommunities(); | ||
| const { tenantId: currentTenantId, setTenantId } = useTenantContext(); | ||
| const router = useRouter(); | ||
| const searchParams = useSearchParams(); | ||
| const tenantIdFromUrl = searchParams.get("tenantId"); | ||
|
|
||
| const { posts, isLoading, error, createPost, toggleLike, deletePost, editPost } = useActivityFeed(); | ||
|
|
||
| useEffect(() => { | ||
| if (tenantIdFromUrl && tenantIdFromUrl !== currentTenantId) { | ||
| setTenantId(tenantIdFromUrl); | ||
| } | ||
| }, [tenantIdFromUrl, currentTenantId, setTenantId]); | ||
|
|
||
| useEffect(() => { | ||
| if (!communitiesLoading && communities.length > 0 && !tenantIdFromUrl) { | ||
| const myCommunity = communities.find( | ||
| (c) => c.founderId === session?.user?.id || c.isMember | ||
| ); | ||
|
|
||
| if (myCommunity) { | ||
| router.replace(`/dashboard?tenantId=${myCommunity.tenantId || myCommunity.id}`); | ||
| } | ||
| } | ||
| }, [communities, communitiesLoading, tenantIdFromUrl, router, session]); | ||
|
|
||
| return ( | ||
| <div className="grid grid-cols-1 lg:grid-cols-12 gap-6 pb-20"> | ||
| <div className="lg:col-span-8 xl:col-span-9 flex flex-col"> | ||
| <CreatePostInput onPost={createPost} /> | ||
|
|
||
| <div className="flex flex-col gap-2"> | ||
| {isLoading ? ( | ||
| <div className="text-center py-8 text-muted-foreground flex items-center justify-center gap-2"> | ||
| <span className="loading loading-spinner loading-sm"></span> Loading feed... | ||
| </div> | ||
| ) : error ? ( | ||
| <div className="text-center py-8 text-red-500"> | ||
| Failed to load activity feed. | ||
| </div> | ||
| ) : posts.length === 0 ? ( | ||
| <div className="text-center py-8 text-muted-foreground"> | ||
| No posts yet. Be the first to write something! | ||
| </div> | ||
| ) : ( | ||
| <> | ||
| {posts.map((post) => ( | ||
| <PostCard | ||
| key={post.id} | ||
| post={post} | ||
| onLike={() => toggleLike(post.id, post.isLikedByMe || false)} | ||
| onDelete={() => deletePost(post.id)} | ||
| onEdit={(newContent) => editPost(post.id, newContent)} | ||
| /> | ||
| ))} | ||
|
|
||
| <div className="text-center text-muted-foreground text-sm py-8"> | ||
| You've caught up with all the latest activity! | ||
| </div> | ||
| </> | ||
| )} | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="lg:col-span-4 xl:col-span-3"> | ||
| <div className="sticky top-[88px]"> | ||
| <RightSidebar /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,94 +1,10 @@ | ||
| "use client"; | ||
|
|
||
| import { CreatePostInput } from "@/components/dashboard/create-post-input"; | ||
| import { PostCard } from "@/components/dashboard/post-card"; | ||
| import { RightSidebar } from "@/components/dashboard/right-sidebar"; | ||
|
|
||
| import { useActivityFeed } from "@/hooks/use-activity-feed"; | ||
| import { useCommunities } from "@/hooks/use-communities"; | ||
| import { useTenantContext } from "@/components/providers/tenant-provider"; | ||
| import { useSession } from "next-auth/react"; | ||
| import { useRouter, useSearchParams } from "next/navigation"; | ||
| import { useEffect } from "react"; | ||
| import { Suspense } from "react"; | ||
| import { DashboardContent } from "./dashboard-content"; | ||
|
|
||
| export default function DashboardPage() { | ||
| const { data: session } = useSession(); | ||
| const { communities, isLoading: communitiesLoading } = useCommunities(); | ||
| const { tenantId: currentTenantId, setTenantId } = useTenantContext(); | ||
| const router = useRouter(); | ||
| const searchParams = useSearchParams(); | ||
| const tenantIdFromUrl = searchParams.get("tenantId"); | ||
|
|
||
| const { posts, isLoading, error, createPost, toggleLike, deletePost, editPost } = useActivityFeed(); | ||
|
|
||
| // Sync URL tenantId to context | ||
| useEffect(() => { | ||
| if (tenantIdFromUrl && tenantIdFromUrl !== currentTenantId) { | ||
| setTenantId(tenantIdFromUrl); | ||
| } | ||
| }, [tenantIdFromUrl, currentTenantId, setTenantId]); | ||
|
|
||
| useEffect(() => { | ||
| if (!communitiesLoading && communities.length > 0 && !tenantIdFromUrl) { | ||
| const myCommunity = communities.find( | ||
| (c) => c.founderId === session?.user?.id || c.isMember | ||
| ); | ||
|
|
||
| if (myCommunity) { | ||
| router.replace(`/dashboard?tenantId=${myCommunity.tenantId || myCommunity.id}`); | ||
| } | ||
| } | ||
| }, [communities, communitiesLoading, tenantIdFromUrl, router, session]); | ||
|
|
||
| return ( | ||
| <div className="grid grid-cols-1 lg:grid-cols-12 gap-6 pb-20"> | ||
|
|
||
| {/* Main Feed Column */} | ||
| <div className="lg:col-span-8 xl:col-span-9 flex flex-col"> | ||
| <CreatePostInput onPost={createPost} /> | ||
|
|
||
| {/* Posts List */} | ||
| <div className="flex flex-col gap-2"> | ||
| {isLoading ? ( | ||
| <div className="text-center py-8 text-muted-foreground flex items-center justify-center gap-2"> | ||
| <span className="loading loading-spinner loading-sm"></span> Loading feed... | ||
| </div> | ||
| ) : error ? ( | ||
| <div className="text-center py-8 text-red-500"> | ||
| Failed to load activity feed. | ||
| </div> | ||
| ) : posts.length === 0 ? ( | ||
| <div className="text-center py-8 text-muted-foreground"> | ||
| No posts yet. Be the first to write something! | ||
| </div> | ||
| ) : ( | ||
| <> | ||
| {posts.map((post) => ( | ||
| <PostCard | ||
| key={post.id} | ||
| post={post} | ||
| onLike={() => toggleLike(post.id, post.isLikedByMe || false)} | ||
| onDelete={() => deletePost(post.id)} | ||
| onEdit={(newContent) => editPost(post.id, newContent)} | ||
| /> | ||
| ))} | ||
|
|
||
| {/* End of feed message */} | ||
| <div className="text-center text-muted-foreground text-sm py-8"> | ||
| You've caught up with all the latest activity! | ||
| </div> | ||
| </> | ||
| )} | ||
| </div> | ||
| </div> | ||
|
|
||
| {/* Right Sidebar Column */} | ||
| <div className="lg:col-span-4 xl:col-span-3"> | ||
| <div className="sticky top-[88px]"> | ||
| <RightSidebar /> | ||
| </div> | ||
| </div> | ||
|
|
||
| </div> | ||
| <Suspense fallback={<div className="text-center py-8 text-muted-foreground">Loading dashboard...</div>}> | ||
| <DashboardContent /> | ||
| </Suspense> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -27,13 +27,13 @@ export function useCommunities() { | |||||||||||||||||||||
| const fetchCommunities = async () => { | ||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| setIsLoading(true); | ||||||||||||||||||||||
| // Assuming backend exposes a route to get public/discoverable communities | ||||||||||||||||||||||
| const response = await apiClient.get<Community[]>("/communities"); | ||||||||||||||||||||||
| setCommunities(response.data); | ||||||||||||||||||||||
| const response = await apiClient.get<Community[]>('/communities'); | ||||||||||||||||||||||
| setCommunities(response.data ?? []); | ||||||||||||||||||||||
| setError(null); | ||||||||||||||||||||||
| } catch (err: any) { | ||||||||||||||||||||||
| console.error("Failed to fetch communities", err); | ||||||||||||||||||||||
| setError(err); | ||||||||||||||||||||||
| console.warn('Failed to fetch communities, using empty state', err?.message || err); | ||||||||||||||||||||||
| setCommunities([]); | ||||||||||||||||||||||
| setError(null); | ||||||||||||||||||||||
|
Comment on lines
33
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Don't clear Resetting DiscoverPage renders a spinner while loading, shows an error panel only when 🐛 Proposed fix to preserve error visibility } catch (err: any) {
console.warn('Failed to fetch communities, using empty state', err?.message || err);
setCommunities([]);
- setError(null);
+ setError(err instanceof Error ? err : new Error(err?.message || 'Failed to fetch communities'));
} finally {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||
| setIsLoading(false); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Dev-only origins remain allowed in production.
allowedOriginsunconditionally includeslocalhost:3001/127.0.0.1:3001/:8080dev URLs alongsideFRONTEND_URL, with no environment gating. In production this means the credentialed CORS allowlist always trusts local dev origins, unnecessarily widening the trust boundary for a credentials-enabled API.🔒 Proposed fix — gate dev origins by environment
📝 Committable suggestion
🤖 Prompt for AI Agents