Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
.env.local
.github
.git
docker/local.env
docker/sandbox.env
node_modules
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ ENV NEXT_PUBLIC_FE_URL="NP--FE--URL"
ENV NEXT_PUBLIC_ADMIN_URL="NP--ADMIN--URL"
ENV NEXT_PUBLIC_API_URL="NP--API--URL"
ENV NEXT_PUBLIC_TUS_URL="NP--TUS--URL"
ENV REDIS_CACHE="enabled"

COPY --from=deps /app/node_modules ./node_modules
COPY . ./
Expand Down
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# NGLP Frontend
# Meru Frontend

## Development Environment

```sh
yarn install
yarn run dev # to start dev client
yarn dev -p 3001 # to start dev client
```

That's it! Now go to http://localhost:3001/
Expand Down Expand Up @@ -40,6 +40,38 @@ Header.tsx // Defines the component
styles.ts // Styled components
```

### Cache Testing

`meru-frontend` is designed to use Valkey for caching when running in production. To test this, you may also run it locally with the provided docker-compose.yml and some scripts in `bin`.

First, ensure that Valkey is running locally on port 36379:

```bash
docker compose up -d
```

Then, build a local version of the image. You will need to do this on any change to the application code. Since we need to test how it works in production, there's no live-reloading of changes. Once it has been built, you can run it against one of two versions of the API with some scripts.

```bash
bin/build-local-image
```

To run against the local API, which supports testing revalidation:

```bash
bin/run-local-against-local-api
```

This will be available on [localhost:14700](http://localhost:14700).

To run against the Sandbox API, which _does not_ support testing revalidation:

```bash
bin/run-local-against-sandbox
```

This will be available on [localhost:14800](http://localhost:14800).

### Browser support

- Edge (Chromium latest)
Expand Down
2 changes: 1 addition & 1 deletion app/[frontend]/(pages)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { BasePageParams } from "@/types/page";
import ProgressBar from "@/components/atomic/loading/ProgressBar";
import generateSiteMetadata from "./_metadata/site";

export const revalidate = 0;
export const revalidate = 43200;

export async function generateMetadata(
props: BasePageParams,
Expand Down
2 changes: 1 addition & 1 deletion app/[frontend]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { layoutThemeQuery as Query } from "@/relay/layoutThemeQuery.graphql";
import { getThemeClasses } from "@/styles/theme-helpers";
import type { PropsWithChildren } from "react";

export const revalidate = 0;
export const revalidate = 43200;

export default async function RootLayout({ children }: PropsWithChildren) {
const { data } = await fetchQuery<Query>(query, {});
Expand Down
7 changes: 7 additions & 0 deletions bin/build-local-image
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

# This image will build a local version of the meru-frontend image
# that can be used for local development and testing.
# You can actually run it with the other scripts in this directory.

docker buildx build --platform linux/amd64 -t meru/meru-frontend-local .
13 changes: 13 additions & 0 deletions bin/run-local-against-local-api
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

# The purpose of this script is to run a locally-built docker
# image of the meru-frontend against a locally-running meru API.
# Make sure that you have built the local version of the frontend
# with `bin/build-local-image`.

# Ensure valkey is running
docker compose up -d

docker run -it --rm -p 14700:3000 -w /app \
--env-file docker/local.env \
meru/meru-frontend-local
13 changes: 13 additions & 0 deletions bin/run-local-against-sandbox
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

# The purpose of this script is to run a locally-built docker
# image of the meru-frontend against the sandbox version of the meru API.
# Make sure that you have built the local version of the frontend
# with `bin/build-local-image`.

# Ensure valkey is running
docker compose up -d

docker run -it --rm -p 14800:3000 -w /app \
--env-file docker/sandbox.env \
meru/meru-frontend-local
64 changes: 64 additions & 0 deletions customized-cache-handler.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const { RedisStringsHandler } = require("@trieb.work/nextjs-turbo-redis-cache");

// Singleton pattern to ensure only one instance of RedisStringsHandler
let cachedHandler;

function parseEnvInt(value, defaultValue) {
const parsed = parseInt(value, 10);

return isNaN(parsed) ? defaultValue : parsed;
}

function socketOptionsFor(redisURL) {
if (!redisURL || !redisURL.startsWith('rediss://')) {
return null;
}

return {
tls: true,
rejectUnauthorized: false,
};
}

const REDIS_DB = parseEnvInt(process.env.REDIS_DB, 1);

module.exports = class CustomizedCacheHandler {
constructor() {
// We need this class to be available during production / docker builds,
// but we only want to connect to Redis when actually running the server.
if (!cachedHandler && process.env.REDIS_URL) {
const socketOptions = socketOptionsFor(process.env.REDIS_URL);

cachedHandler = new RedisStringsHandler({
// https://github.com/trieb-work/nextjs-turbo-redis-cache?tab=readme-ov-file#available-options
database: REDIS_DB,
keyPrefix: 'meru-client:',
timeoutMs: 2_000,
revalidateTagQuerySize: 500,
sharedTagsKey: '__sharedTags__',
avgResyncIntervalMs: 10_000 * 60,
redisGetDeduplication: false,
inMemoryCachingTime: 3000,
defaultStaleAge: 1209600,
estimateExpireAge: (staleAge) => staleAge * 2,
socketOptions,
});
}
}

get(...args) {
return cachedHandler?.get(...args) ?? null;
}

set(...args) {
return cachedHandler?.set(...args) ?? null;
}

revalidateTag(...args) {
return cachedHandler?.revalidateTag(...args) ?? null;
}

resetRequestCache(...args) {
return cachedHandler?.resetRequestCache(...args) ?? null;
}
}
21 changes: 21 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
services:
redis:
image: valkey/valkey:8.0
environment:
- "ALLOW_EMPTY_PASSWORD=yes"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "10"
restart: unless-stopped
ports:
- "36379:6379"
volumes:
- ./docker/valkey:/usr/local/etc/valkey
command: ["valkey-server", "/usr/local/etc/valkey/valkey.conf"]
healthcheck:
test: ["CMD", "valkey-cli", "--raw", "incr", "ping"]
interval: 30s
timeout: 5s
retries: 5
26 changes: 26 additions & 0 deletions docker/local.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
AUTH_SECRET=12345secret
NODE_ENV=production

NEXT_PUBLIC_API_URL=http://host.docker.internal:6222/graphql
NEXT_PUBLIC_TUS_URL=http://host.docker.internal:6222/files

# Keycloak
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=meru-public
NEXT_KEYCLOAK_CLIENT_SECRET=12345secret
NEXT_PUBLIC_KEYCLOAK_REALM=meru
NEXT_PUBLIC_KEYCLOAK_URL=https://id.sandbox.meru.host

# Secret token for revalidation requests
REVALIDATE_SECRET=revalidatenow

REDIS_URL=redis://host.docker.internal:36379
REDIS_DB=2

# Other Next.js vars
NEXT_PUBLIC_FE_URL=http://localhost:14700
NEXT_PUBLIC_VERSION=1.0.0
NEXT_PUBLIC_ORDER_PATH_OPTIONS="props.volume.sortable_number,props.sortable_number,props.id"

# Optional
NEXT_PUBLIC_SITEMAP_CACHE_MAXAGE=86400
NEXT_PUBLIC_SITEMAP_CACHE_REVALIDATE=59
26 changes: 26 additions & 0 deletions docker/sandbox.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
AUTH_SECRET=12345secret
NODE_ENV=production

NEXT_PUBLIC_API_URL=https://api.sandbox.meru.host/graphql
NEXT_PUBLIC_TUS_URL=https://api.sandbox.meru.host/files

# Keycloak
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=meru-public
NEXT_KEYCLOAK_CLIENT_SECRET=12345secret
NEXT_PUBLIC_KEYCLOAK_REALM=meru
NEXT_PUBLIC_KEYCLOAK_URL=https://id.sandbox.meru.host

# Secret token for revalidation requests
REVALIDATE_SECRET=revalidatenow

REDIS_URL=redis://host.docker.internal:36379
REDIS_DB=3

# Other Next.js vars
NEXT_PUBLIC_FE_URL=http://localhost:14800
NEXT_PUBLIC_VERSION=1.0.0
NEXT_PUBLIC_ORDER_PATH_OPTIONS="props.volume.sortable_number,props.sortable_number,props.id"

# Optional
NEXT_PUBLIC_SITEMAP_CACHE_MAXAGE=86400
NEXT_PUBLIC_SITEMAP_CACHE_REVALIDATE=59
3 changes: 3 additions & 0 deletions docker/valkey/valkey.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
maxmemory-policy allkeys-lru

notify-keyspace-events Exe
5 changes: 5 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ const nextConfig = {
},
};

if (process.env.NODE_ENV === "production" || process.env.REDIS_CACHE === "enabled") {
nextConfig.cacheHandler = require.resolve("./customized-cache-handler.cjs");
nextConfig.cacheMaxMemorySize = 0;
}

const withMDX = createMDX();

module.exports = withMDX(nextConfig);
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@mdx-js/mdx": "^3.1.0",
"@next/mdx": "^15.0.0",
"@spissvinkel/alea": "^1.2.1",
"@trieb.work/nextjs-turbo-redis-cache": "^1.8.1",
"@types/mdx": "^2.0.13",
"chunk-text": "^2.0.1",
"classnames": "^2.5.1",
Expand All @@ -44,6 +45,7 @@
"react-pdf": "^10.1.0",
"react-relay": "^16.2.0",
"reakit": "^1.3.11",
"redis": "4.7.0",
"regenerator-runtime": "^0.14.1",
"rehype-raw": "^7.0.0",
"relay-compiler": "^16.2.0",
Expand Down
Loading