Skip to content

Commit 792d1d1

Browse files
committed
Enhance project caching
Implements project caching and refresh logic to optimize data retrieval and ensure up-to-date project information. Updates various components to support these enhancements, including the `ProjectsContextProvider` and `CachingProjectDataSource`. Add revalidation logic to API endpoints to keep cached data in sync.
1 parent 608a698 commit 792d1d1

File tree

17 files changed

+214
-102
lines changed

17 files changed

+214
-102
lines changed

docker-compose.yaml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
version: '3.8'
21
services:
32
cache:
43
image: redis:6.2-alpine
@@ -8,6 +7,26 @@ services:
87
command: redis-server --save 20 1 --loglevel warning
98
volumes:
109
- cache:/data
10+
11+
postgres:
12+
image: postgres:16.8
13+
restart: always
14+
ports:
15+
- '5432:5432'
16+
environment:
17+
- POSTGRES_USER=oscar
18+
- POSTGRES_PASSWORD=passw0rd
19+
- POSTGRES_DB=shape-docs
20+
21+
app:
22+
build: .
23+
ports:
24+
- '3000:3000'
25+
env_file:
26+
- .env
27+
depends_on:
28+
- cache
29+
1130
volumes:
1231
cache:
13-
driver: local
32+
driver: local

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/(authed)/layout.tsx

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,39 @@
1-
import { redirect } from "next/navigation"
2-
import { SessionProvider } from "next-auth/react"
3-
import { session, projectRepository } from "@/composition"
4-
import ErrorHandler from "@/common/ui/ErrorHandler"
5-
import SessionBarrier from "@/features/auth/view/SessionBarrier"
6-
import ProjectsContextProvider from "@/features/projects/view/ProjectsContextProvider"
7-
import { SidebarTogglableContextProvider, SplitView } from "@/features/sidebar/view"
1+
import { redirect } from "next/navigation";
2+
import { SessionProvider } from "next-auth/react";
3+
import { session } from "@/composition";
4+
import ErrorHandler from "@/common/ui/ErrorHandler";
5+
import SessionBarrier from "@/features/auth/view/SessionBarrier";
6+
import ProjectsContextProvider from "@/features/projects/view/ProjectsContextProvider";
7+
import { projectDataSource } from "@/composition";
8+
import {
9+
SidebarTogglableContextProvider,
10+
SplitView,
11+
} from "@/features/sidebar/view";
812

9-
export default async function Layout({ children }: { children: React.ReactNode }) {
10-
const isAuthenticated = await session.getIsAuthenticated()
13+
export default async function Layout({
14+
children,
15+
}: {
16+
children: React.ReactNode;
17+
}) {
18+
const isAuthenticated = await session.getIsAuthenticated();
1119
if (!isAuthenticated) {
12-
return redirect("/api/auth/signin")
20+
return redirect("/api/auth/signin");
1321
}
14-
const projects = await projectRepository.get()
22+
23+
const projects = await projectDataSource.getProjects();
24+
console.log("Loaded projects:", projects);
25+
1526
return (
1627
<ErrorHandler>
1728
<SessionProvider>
1829
<SessionBarrier>
1930
<ProjectsContextProvider initialProjects={projects}>
2031
<SidebarTogglableContextProvider>
21-
<SplitView>
22-
{children}
23-
</SplitView>
32+
<SplitView>{children}</SplitView>
2433
</SidebarTogglableContextProvider>
2534
</ProjectsContextProvider>
2635
</SessionBarrier>
2736
</SessionProvider>
2837
</ErrorHandler>
29-
)
38+
);
3039
}

src/app/api/blob/[owner]/[repository]/[...path]/route.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { NextRequest, NextResponse } from "next/server"
22
import { session, userGitHubClient } from "@/composition"
33
import { makeUnauthenticatedAPIErrorResponse } from "@/common"
4+
import { revalidatePath } from "next/cache"
5+
46

57
interface GetBlobParams {
68
owner: string
79
repository: string
810
path: [string]
911
}
1012

11-
export async function GET(req: NextRequest, { params }: { params: Promise<GetBlobParams> }) {
13+
export async function GET(req: NextRequest, { params }: { params: Promise<GetBlobParams> } ) {
1214
const isAuthenticated = await session.getIsAuthenticated()
1315
if (!isAuthenticated) {
1416
return makeUnauthenticatedAPIErrorResponse()
@@ -23,14 +25,18 @@ export async function GET(req: NextRequest, { params }: { params: Promise<GetBlo
2325
})
2426
const url = new URL(item.downloadURL)
2527
const imageRegex = /\.(jpg|jpeg|png|webp|avif|gif)$/;
26-
const file = await fetch(url).then(r => r.blob())
28+
const res = await fetch(url, { next: { revalidate: 6000 } })
29+
const file = await res.blob()
30+
revalidatePath('/(authed)/projects')
2731
const headers = new Headers()
32+
if (res.status !== 200 ) {
33+
headers.set("Content-Type", "text/plain");
34+
headers.set("Cache-Control", `max-age=3000`)
35+
}
2836
if (new RegExp(imageRegex).exec(path)) {
2937
const cacheExpirationInSeconds = 60 * 60 * 24 * 30 // 30 days
3038
headers.set("Content-Type", "image/*");
3139
headers.set("Cache-Control", `max-age=${cacheExpirationInSeconds}`)
32-
} else {
33-
headers.set("Content-Type", "text/plain");
34-
}
40+
}
3541
return new NextResponse(file, { status: 200, headers })
3642
}

src/app/api/hooks/github/route.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
import { NextRequest, NextResponse } from "next/server"
2-
import { gitHubHookHandler } from "@/composition"
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { gitHubHookHandler } from "@/composition";
3+
import { revalidatePath } from "next/cache";
4+
import { projectDataSource } from "@/composition";
35

4-
export const POST = async (req: NextRequest): Promise<NextResponse> => {
5-
await gitHubHookHandler.handle(req)
6-
return NextResponse.json({ status: "OK" })
7-
}
6+
// I GitHubHookHandler eller composition
7+
export const POST = async (req: NextRequest) => {
8+
await gitHubHookHandler.handle(req);
9+
10+
// Opdater projects cache når webhook modtages
11+
await projectDataSource.refreshProjects();
12+
13+
revalidatePath("/(authed)/projects");
14+
return NextResponse.json({ status: "OK" });
15+
};

src/app/api/projects/route.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { NextResponse } from "next/server"
2+
import { projectDataSource } from "@/composition"
3+
4+
export async function GET() {
5+
const projects = await projectDataSource.refreshProjects()
6+
return NextResponse.json({ projects })
7+
}

src/common/utils/fetcher.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export class FetcherError extends Error {
1+
export class FetcherError extends Error {
22
readonly status: number
33

44
constructor(status: number, message: string) {
@@ -12,9 +12,9 @@
1212
input: RequestInfo,
1313
init?: RequestInit
1414
): Promise<JSON> {
15-
const res = await fetch(input, init)
15+
const res = await fetch(input, init)
1616
if (!res.ok) {
1717
throw new FetcherError(res.status, "An error occurred while fetching the data.")
1818
}
1919
return res.json()
20-
}
20+
}

src/composition.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,4 @@ export const gitHubHookHandler = new GitHubHookHandler({
230230
})
231231
})
232232
})
233-
})
233+
})

src/features/docs/view/Stoplight.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,4 @@ const ElementsAPI = ({
7575
})
7676
}
7777

78-
export default Stoplight
78+
export default Stoplight

src/features/docs/view/Swagger.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,4 @@ const Swagger = ({ url }: { url: string }) => {
6262
)
6363
}
6464

65-
export default Swagger
65+
export default Swagger

0 commit comments

Comments
 (0)