Skip to content

Commit 7d7671b

Browse files
committed
Refactor API endpoints and add v6 controllers
Migrates client and UI code to use new /api/v6/data and /api/v6/metadata endpoints. Adds DataController and MetadataController to the server for v6 API, introduces AuthMiddleware for authentication, and updates AppSidebar to use a new NavUser component. Removes legacy objectql router and related code from ObjectQLModule. Updates package dependencies and cleans up header usage in client API.
1 parent 4bd2aab commit 7d7671b

File tree

15 files changed

+418
-58
lines changed

15 files changed

+418
-58
lines changed

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/client/src/components/app-sidebar.tsx

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ import {
1212
SidebarRail,
1313
SidebarFooter,
1414
} from "@objectql/ui"
15-
import { Database, Settings, FileText } from "lucide-react"
15+
import { Database, FileText } from "lucide-react"
1616
import { useRouter } from "../hooks/useRouter"
17+
import { NavUser } from "./nav-user"
18+
import { useAuth } from "../context/AuthContext"
1719

1820
export function AppSidebar({ objects, ...props }: React.ComponentProps<typeof Sidebar> & { objects: Record<string, any> }) {
1921
const { path, navigate } = useRouter()
22+
const { user } = useAuth()
2023

2124
return (
2225
<Sidebar collapsible="icon" {...props}>
@@ -56,17 +59,11 @@ export function AppSidebar({ objects, ...props }: React.ComponentProps<typeof Si
5659
</SidebarGroup>
5760
</SidebarContent>
5861
<SidebarFooter>
59-
<SidebarMenu>
60-
<SidebarMenuItem>
61-
<SidebarMenuButton
62-
isActive={path === '/settings'}
63-
onClick={() => navigate('/settings')}
64-
>
65-
<Settings />
66-
<span>Settings</span>
67-
</SidebarMenuButton>
68-
</SidebarMenuItem>
69-
</SidebarMenu>
62+
<NavUser user={{
63+
name: user?.name || 'User',
64+
email: user?.email || '',
65+
avatar: user?.image
66+
}} />
7067
</SidebarFooter>
7168
<SidebarRail />
7269
</Sidebar>

packages/client/src/components/dashboard/ObjectDetailView.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ export function ObjectDetailView({ objectName, recordId, navigate, objectSchema
2222
useEffect(() => {
2323
setLoading(true);
2424
Promise.all([
25-
fetch(`/api/object/${objectName}/${recordId}`, { headers: getHeaders() }).then(async r => {
25+
fetch(`/api/v6/data/${objectName}/${recordId}`, { headers: getHeaders() }).then(async r => {
2626
if (!r.ok) throw new Error("Failed to load record");
2727
return r.json();
2828
}),
29-
fetch(`/api/object/_schema/object/${objectName}`, { headers: getHeaders() }).then(r => r.json())
29+
fetch(`/api/v6/metadata/object/${objectName}`, { headers: getHeaders() }).then(r => r.json())
3030
]).then(([record, schemaData]) => {
3131
setData(record);
3232
setSchema(schemaData);
@@ -36,15 +36,15 @@ export function ObjectDetailView({ objectName, recordId, navigate, objectSchema
3636

3737
const handleDelete = () => {
3838
if (!confirm('Are you sure you want to delete this record?')) return;
39-
fetch(`/api/object/${objectName}/${recordId}`, {
39+
fetch(`/api/v6/data/${objectName}/${recordId}`, {
4040
method: 'DELETE',
4141
headers: getHeaders()
4242
}).then(() => navigate(`/object/${objectName}`))
4343
.catch(e => alert(e.message));
4444
};
4545

4646
const handleUpdate = (formData: any) => {
47-
fetch(`/api/object/${objectName}/${recordId}`, {
47+
fetch(`/api/v6/data/${objectName}/${recordId}`, {
4848
method: 'PUT',
4949
headers: getHeaders(),
5050
body: JSON.stringify(formData)
@@ -54,7 +54,7 @@ export function ObjectDetailView({ objectName, recordId, navigate, objectSchema
5454
}).then(() => {
5555
setIsEditing(false);
5656
// Reload data
57-
fetch(`/api/object/${objectName}/${recordId}`, { headers: getHeaders() })
57+
fetch(`/api/v6/data/${objectName}/${recordId}`, { headers: getHeaders() })
5858
.then(r => r.json())
5959
.then(setData);
6060
}).catch(e => alert(e.message));

packages/client/src/components/dashboard/ObjectForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function ObjectForm({ objectName, initialValues, onSubmit, onCancel, head
1717
});
1818

1919
useEffect(() => {
20-
fetch(`/api/object/_schema/object/${objectName}`, { headers })
20+
fetch(`/api/v6/metadata/object/${objectName}`, { headers })
2121
.then(res => res.json())
2222
.then(res => {
2323
setSchema(res);

packages/client/src/components/dashboard/ObjectListView.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export function ObjectListView({ objectName, user, isCreating, navigate, objectS
102102
}
103103
}
104104

105-
fetch(`/api/object/${objectName}?${params.toString()}`, { headers: getHeaders() })
105+
fetch(`/api/v6/data/${objectName}?${params.toString()}`, { headers: getHeaders() })
106106
.then(async res => {
107107
if (!res.ok) {
108108
const contentType = res.headers.get("content-type");
@@ -131,7 +131,7 @@ export function ObjectListView({ objectName, user, isCreating, navigate, objectS
131131
}, [objectName, user, sortConfig, debouncedSearch]);
132132

133133
const handleCreate = (formData: any) => {
134-
fetch(`/api/object/${objectName}`, {
134+
fetch(`/api/v6/data/${objectName}`, {
135135
method: 'POST',
136136
headers: getHeaders(),
137137
body: JSON.stringify(formData)
@@ -153,7 +153,7 @@ export function ObjectListView({ objectName, user, isCreating, navigate, objectS
153153
if (!confirm('Are you sure you want to delete this record?')) return;
154154
const id = row.id || row._id;
155155

156-
fetch(`/api/object/${objectName}/${id}`, {
156+
fetch(`/api/v6/data/${objectName}/${id}`, {
157157
method: 'DELETE',
158158
headers: getHeaders()
159159
})
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"use client"
2+
3+
import {
4+
BadgeCheck,
5+
Bell,
6+
ChevronsUpDown,
7+
CreditCard,
8+
LogOut,
9+
} from "lucide-react"
10+
11+
import {
12+
Avatar,
13+
AvatarFallback,
14+
AvatarImage,
15+
DropdownMenu,
16+
DropdownMenuContent,
17+
DropdownMenuGroup,
18+
DropdownMenuItem,
19+
DropdownMenuLabel,
20+
DropdownMenuSeparator,
21+
DropdownMenuTrigger,
22+
SidebarMenu,
23+
SidebarMenuButton,
24+
SidebarMenuItem,
25+
useSidebar,
26+
} from "@objectql/ui"
27+
import { useAuth } from "../context/AuthContext"
28+
29+
export function NavUser({
30+
user,
31+
}: {
32+
user: {
33+
name: string
34+
email: string
35+
avatar?: string
36+
}
37+
}) {
38+
const { isMobile } = useSidebar()
39+
const { signOut } = useAuth()
40+
41+
return (
42+
<SidebarMenu>
43+
<SidebarMenuItem>
44+
<DropdownMenu>
45+
<DropdownMenuTrigger asChild>
46+
<SidebarMenuButton
47+
size="lg"
48+
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
49+
>
50+
<Avatar className="h-8 w-8 rounded-lg">
51+
<AvatarImage src={user.avatar} alt={user.name} />
52+
<AvatarFallback className="rounded-lg">{user.name ? user.name.substring(0,2).toUpperCase() : 'U'}</AvatarFallback>
53+
</Avatar>
54+
<div className="grid flex-1 text-left text-sm leading-tight">
55+
<span className="truncate font-semibold">{user.name}</span>
56+
<span className="truncate text-xs">{user.email}</span>
57+
</div>
58+
<ChevronsUpDown className="ml-auto size-4" />
59+
</SidebarMenuButton>
60+
</DropdownMenuTrigger>
61+
<DropdownMenuContent
62+
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
63+
side={isMobile ? "bottom" : "right"}
64+
align="end"
65+
sideOffset={4}
66+
>
67+
<DropdownMenuLabel className="p-0 font-normal">
68+
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
69+
<Avatar className="h-8 w-8 rounded-lg">
70+
<AvatarImage src={user.avatar} alt={user.name} />
71+
<AvatarFallback className="rounded-lg">{user.name ? user.name.substring(0,2).toUpperCase() : 'U'}</AvatarFallback>
72+
</Avatar>
73+
<div className="grid flex-1 text-left text-sm leading-tight">
74+
<span className="truncate font-semibold">{user.name}</span>
75+
<span className="truncate text-xs">{user.email}</span>
76+
</div>
77+
</div>
78+
</DropdownMenuLabel>
79+
<DropdownMenuSeparator />
80+
<DropdownMenuGroup>
81+
<DropdownMenuItem>
82+
<BadgeCheck className="mr-2 h-4 w-4" />
83+
Account
84+
</DropdownMenuItem>
85+
<DropdownMenuItem>
86+
<CreditCard className="mr-2 h-4 w-4" />
87+
Billing
88+
</DropdownMenuItem>
89+
<DropdownMenuItem>
90+
<Bell className="mr-2 h-4 w-4" />
91+
Notifications
92+
</DropdownMenuItem>
93+
</DropdownMenuGroup>
94+
<DropdownMenuSeparator />
95+
<DropdownMenuItem onClick={() => signOut()}>
96+
<LogOut className="mr-2 h-4 w-4" />
97+
Log out
98+
</DropdownMenuItem>
99+
</DropdownMenuContent>
100+
</DropdownMenu>
101+
</SidebarMenuItem>
102+
</SidebarMenu>
103+
)
104+
}

packages/client/src/lib/api.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export const getHeaders = () => {
22
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
3-
headers['x-user-id'] = 'admin';
43
return headers;
54
};

packages/client/src/pages/Dashboard.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,19 @@ export default function Dashboard() {
3737
useEffect(() => {
3838
if (user) {
3939
// Fetch objects
40-
fetch('/api/object/_schema/object', { headers: getHeaders() })
40+
fetch('/api/v6/metadata/object', { headers: getHeaders() })
4141
.then(res => res.json())
4242
.then(result => {
43-
const objNames = Object.keys(result);
44-
setObjects(result);
43+
// Convert array to map
44+
const objectsMap: Record<string, any> = {};
45+
if (Array.isArray(result)) {
46+
result.forEach((obj: any) => {
47+
objectsMap[obj.name] = obj;
48+
});
49+
}
50+
51+
const objNames = Object.keys(objectsMap);
52+
setObjects(objectsMap);
4553

4654
const currentPath = window.location.pathname;
4755
if ((currentPath === '/' || currentPath === '/object') && objNames.length > 0) {

packages/server/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@example/project-management": "*",
1616
"@nestjs/common": "^10.0.0",
1717
"@nestjs/core": "^10.0.0",
18+
"@nestjs/mapped-types": "^2.1.0",
1819
"@nestjs/platform-express": "^10.0.0",
1920
"@nestjs/serve-static": "^4.0.2",
2021
"@objectql/api": "*",

packages/server/src/app.module.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
1+
import { Module, MiddlewareConsumer, RequestMethod, NestModule } from '@nestjs/common';
22
import { AppController } from './app.controller';
33
import { AppService } from './app.service';
44
import { ObjectQLModule } from './objectql/objectql.module';
55
import { AuthModule } from './auth/auth.module';
66
import { ServeStaticModule } from '@nestjs/serve-static';
77
import { join } from 'path';
8+
import { DataController } from './controllers/data.controller';
9+
import { MetadataController } from './controllers/metadata.controller';
10+
import { AuthMiddleware } from './auth/auth.middleware';
811

912
@Module({
1013
imports: [
@@ -15,7 +18,13 @@ import { join } from 'path';
1518
exclude: ['/api/(.*)', '/docs/(.*)'],
1619
}),
1720
],
18-
controllers: [AppController],
21+
controllers: [AppController, DataController, MetadataController],
1922
providers: [AppService],
2023
})
21-
export class AppModule {}
24+
export class AppModule implements NestModule {
25+
configure(consumer: MiddlewareConsumer) {
26+
consumer
27+
.apply(AuthMiddleware)
28+
.forRoutes({ path: 'api/v6/*', method: RequestMethod.ALL });
29+
}
30+
}

0 commit comments

Comments
 (0)