Skip to content

Commit a508fbe

Browse files
authored
feat(ui): add keycloak theme (#1881)
Signed-off-by: Petr Kadlec <[email protected]>
1 parent 927b904 commit a508fbe

File tree

101 files changed

+5727
-742
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+5727
-742
lines changed

.github/workflows/release.yml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ jobs:
8181
environment: release
8282
env:
8383
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
84-
HELM_EXPERIMENTAL_OCI: '1'
84+
HELM_EXPERIMENTAL_OCI: "1"
8585
steps:
8686
- uses: actions/checkout@v4
8787
- id: version
@@ -137,10 +137,27 @@ jobs:
137137
file: ./apps/supergateway/Dockerfile
138138
push: true
139139
platforms: linux/amd64,linux/arm64
140-
tags: ghcr.io/${{ github.repository }}/supergateway:latest
140+
tags: ghcr.io/${{ github.repository }}/supergateway:${{ github.sha }}
141141
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}/supergateway:cache
142142
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}/supergateway:cache,mode=max
143143

144+
- run: mise run 'keycloak-theme:build:*'
145+
- id: keycloak-version
146+
run: |
147+
KEYCLOAK_VERSION=$(grep -oP 'FROM quay.io/keycloak/keycloak:\K[0-9.]+' ./apps/keycloak-theme/Dockerfile)
148+
echo "version=$KEYCLOAK_VERSION" >> $GITHUB_OUTPUT
149+
- uses: docker/build-push-action@v6
150+
with:
151+
context: ./apps/keycloak-theme
152+
file: ./apps/keycloak-theme/Dockerfile
153+
push: true
154+
platforms: linux/amd64,linux/arm64
155+
tags: |
156+
ghcr.io/${{ github.repository }}/keycloak-themed:${{ github.sha }}
157+
ghcr.io/${{ github.repository }}/keycloak-themed:${{ steps.keycloak-version.outputs.version }}
158+
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}/keycloak-themed:cache
159+
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}/keycloak-themed:cache,mode=max
160+
144161
- run: mise run 'agentstack-ui:build:*'
145162
- uses: docker/build-push-action@v6
146163
with:
@@ -178,5 +195,5 @@ jobs:
178195
git fetch origin
179196
git checkout -B install origin/main
180197
git push --force --set-upstream origin install
181-
198+
182199
- run: uv cache prune --ci

agentstack.code-workspace

Lines changed: 73 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,76 @@
11
{
2-
"folders": [
3-
{
4-
"name": "root",
5-
"path": "."
6-
},
7-
{
8-
"name": "agentstack-server",
9-
"path": "apps/agentstack-server"
10-
},
11-
{
12-
"name": "agentstack-sdk-py",
13-
"path": "apps/agentstack-sdk-py"
14-
},
15-
{
16-
"name": "agentstack-sdk-ts",
17-
"path": "apps/agentstack-sdk-ts"
18-
},
19-
{
20-
"name": "agentstack-cli",
21-
"path": "apps/agentstack-cli"
22-
},
23-
{
24-
"name": "agentstack-ui",
25-
"path": "apps/agentstack-ui",
26-
},
27-
{
28-
"name": "beeai-web",
29-
"path": "apps/beeai-web",
30-
},
31-
{
32-
"name": "agent-chat",
33-
"path": "agents/chat"
34-
},
35-
{
36-
"name": "agent-form",
37-
"path": "agents/form"
38-
},
39-
{
40-
"name": "agent-rag",
41-
"path": "agents/rag"
42-
},
43-
{
44-
"name": "agent-canvas",
45-
"path": "agents/canvas"
46-
},
47-
{
48-
"name": "helm",
49-
"path": "helm"
50-
}
2+
"folders": [
3+
{
4+
"name": "root",
5+
"path": ".",
6+
},
7+
{
8+
"name": "agentstack-server",
9+
"path": "apps/agentstack-server",
10+
},
11+
{
12+
"name": "agentstack-sdk-py",
13+
"path": "apps/agentstack-sdk-py",
14+
},
15+
{
16+
"name": "agentstack-sdk-ts",
17+
"path": "apps/agentstack-sdk-ts",
18+
},
19+
{
20+
"name": "agentstack-cli",
21+
"path": "apps/agentstack-cli",
22+
},
23+
{
24+
"name": "agentstack-ui",
25+
"path": "apps/agentstack-ui",
26+
},
27+
{
28+
"name": "beeai-web",
29+
"path": "apps/beeai-web",
30+
},
31+
{
32+
"name": "keycloak-theme",
33+
"path": "apps/keycloak-theme",
34+
},
35+
{
36+
"name": "agent-chat",
37+
"path": "agents/chat",
38+
},
39+
{
40+
"name": "agent-form",
41+
"path": "agents/form",
42+
},
43+
{
44+
"name": "agent-rag",
45+
"path": "agents/rag",
46+
},
47+
{
48+
"name": "agent-canvas",
49+
"path": "agents/canvas",
50+
},
51+
{
52+
"name": "helm",
53+
"path": "helm",
54+
},
55+
],
56+
"settings": {
57+
"python.analysis.extraPaths": [
58+
"${workspaceFolder}/apps/agentstack-sdk-py/src",
5159
],
52-
"settings": {
53-
"python.analysis.extraPaths": [
54-
"${workspaceFolder}/apps/agentstack-sdk-py/src"
55-
],
56-
"python.analysis.diagnosticMode": "openFilesOnly",
57-
"python.analysis.typeCheckingMode": "basic",
58-
"files.associations": {
59-
"**/helm/templates/**/*.yaml": "helm",
60-
"**/helm/templates/**/*.tpl": "helm"
61-
},
62-
"basedpyright.analysis.diagnosticMode": "openFilesOnly",
63-
"cursorpyright.analysis.diagnosticMode": "openFilesOnly",
64-
"cursorpyright.analysis.extraPaths": [
65-
"${workspaceFolder}/apps/agentstack-sdk-py/src"
66-
],
67-
"cursorpyright.analysis.typeCheckingMode": "basic"
60+
"python.analysis.diagnosticMode": "openFilesOnly",
61+
"python.analysis.typeCheckingMode": "basic",
62+
"files.associations": {
63+
"**/helm/templates/**/*.yaml": "helm",
64+
"**/helm/templates/**/*.tpl": "helm",
6865
},
69-
"extensions": {
70-
"recommendations": [
71-
"detachhead.basedpyright",
72-
"charliermarsh.ruff"
73-
]
74-
}
75-
}
66+
"basedpyright.analysis.diagnosticMode": "openFilesOnly",
67+
"cursorpyright.analysis.diagnosticMode": "openFilesOnly",
68+
"cursorpyright.analysis.extraPaths": [
69+
"${workspaceFolder}/apps/agentstack-sdk-py/src",
70+
],
71+
"cursorpyright.analysis.typeCheckingMode": "basic",
72+
},
73+
"extensions": {
74+
"recommendations": ["detachhead.basedpyright", "charliermarsh.ruff"],
75+
},
76+
}

apps/agentstack-ui/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
"@a2a-js/sdk": "^0.3.7",
1515
"@bprogress/next": "^3.2.12",
1616
"@carbon/icons-react": "catalog:",
17-
"@carbon/layout": "^11.44.0",
17+
"@carbon/layout": "catalog:",
1818
"@carbon/motion": "^11.38.0",
1919
"@carbon/react": "catalog:",
20-
"@carbon/styles": "^1.96.0",
20+
"@carbon/styles": "catalog:",
2121
"@floating-ui/react": "^0.27.16",
2222
"@hookform/resolvers": "^5.2.2",
23-
"@ibm/plex": "^6.4.1",
23+
"@ibm/plex": "catalog:",
2424
"@next/bundle-analyzer": "16.1.3",
2525
"@radix-ui/react-slot": "^1.2.4",
2626
"@tanstack/react-query": "catalog:",

apps/agentstack-ui/src/app/(auth)/auth.ts

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import NextAuth from 'next-auth';
77
import type { OIDCConfig } from 'next-auth/providers';
88

99
import { runtimeConfig } from '#contexts/App/runtime-config.ts';
10-
import type { AuthProvider } from '#modules/auth/types.ts';
1110
import { routes } from '#utils/router.ts';
1211

1312
import type { ProviderConfig, ProviderWithId } from './types';
@@ -18,14 +17,6 @@ export const AUTH_COOKIE_NAME = 'agentstack';
1817

1918
const { isAuthEnabled } = runtimeConfig;
2019

21-
export function getAuthProviders(): AuthProvider[] {
22-
return getProviders()
23-
.filter((provider) => provider.id !== 'credentials')
24-
.map((provider) => {
25-
return { id: provider.id, name: provider.name };
26-
});
27-
}
28-
2920
function createOIDCProvider(config: ProviderConfig): OIDCConfig<unknown> {
3021
const options = {
3122
clientId: config.client_id,
@@ -53,11 +44,11 @@ function createOIDCProvider(config: ProviderConfig): OIDCConfig<unknown> {
5344
};
5445
}
5546

56-
function getProviders(): ProviderWithId[] {
47+
export function getProvider(): ProviderWithId | null {
5748
const { isAuthEnabled } = runtimeConfig;
5849

5950
if (!isAuthEnabled) {
60-
return [];
51+
return null;
6152
}
6253

6354
try {
@@ -86,25 +77,27 @@ function getProviders(): ProviderWithId[] {
8677
// Validate using the schema
8778
const validatedConfig = providerConfigSchema.parse(providerConfig);
8879

89-
return [createOIDCProvider(validatedConfig)];
80+
return createOIDCProvider(validatedConfig);
9081
} catch (err) {
9182
console.error('Unable to parse OIDC provider configuration environment variables.', err);
9283

93-
return [];
84+
return null;
9485
}
9586
}
9687

97-
const providers = getProviders();
88+
const provider = getProvider();
89+
90+
// Prevents nextauth errors when authentication is disabled and NEXTAUTH_SECRET is not provided
91+
export const AUTH_SECRET = isAuthEnabled ? process.env.NEXTAUTH_SECRET : 'dummy_secret';
9892

9993
export const { handlers, signIn, signOut, auth } = NextAuth({
100-
providers,
94+
providers: provider ? [provider] : [],
10195
pages: {
10296
signIn: routes.signIn(),
10397
},
10498
session: { strategy: 'jwt' },
10599
trustHost: true,
106-
// Prevents nextauth errors when authentication is disabled and NEXTAUTH_SECRET is not provided
107-
secret: isAuthEnabled ? process.env.NEXTAUTH_SECRET : 'dummy_secret',
100+
secret: AUTH_SECRET,
108101
cookies: {
109102
sessionToken: {
110103
name: AUTH_COOKIE_NAME,
@@ -135,8 +128,12 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
135128
}
136129

137130
try {
131+
if (!provider) {
132+
return null;
133+
}
134+
138135
const proactiveTokenRefresh = trigger === 'update' && Boolean(session?.proactiveTokenRefresh);
139-
return await jwtWithRefresh(token, providers, proactiveTokenRefresh);
136+
return await jwtWithRefresh(token, provider, proactiveTokenRefresh);
140137
} catch (error) {
141138
console.error('Error while refreshing jwt token:', error);
142139

@@ -163,10 +160,11 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
163160
const clientSecret = process.env.OIDC_PROVIDER_CLIENT_SECRET;
164161

165162
if (refreshToken && issuer && clientId && clientSecret) {
166-
const params = new URLSearchParams();
167-
params.append('client_id', clientId);
168-
params.append('client_secret', clientSecret);
169-
params.append('refresh_token', refreshToken as string);
163+
const params = new URLSearchParams({
164+
client_id: clientId,
165+
client_secret: clientSecret,
166+
refresh_token: refreshToken,
167+
});
170168

171169
try {
172170
await fetch(`${issuer}/protocol/openid-connect/logout`, {

apps/agentstack-ui/src/app/(auth)/rsc.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { logErrorDetails } from '#api/utils.ts';
1414
import { runtimeConfig } from '#contexts/App/runtime-config.ts';
1515
import { routes } from '#utils/router.ts';
1616

17-
import { auth, AUTH_COOKIE_NAME } from './auth';
17+
import { auth, AUTH_COOKIE_NAME, AUTH_SECRET } from './auth';
1818

1919
export async function ensureToken(request: Request) {
2020
const { isAuthEnabled } = runtimeConfig;
@@ -34,7 +34,7 @@ export async function ensureToken(request: Request) {
3434
request.headers.set('cookie', cookieStore.toString());
3535
}
3636

37-
const token = await getToken({ req: request, cookieName: AUTH_COOKIE_NAME, secret: process.env.NEXTAUTH_SECRET });
37+
const token = await getToken({ req: request, cookieName: AUTH_COOKIE_NAME, secret: AUTH_SECRET });
3838

3939
return token;
4040
}

apps/agentstack-ui/src/app/(auth)/utils.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ interface OIDCProviderOptions {
2020

2121
export async function jwtWithRefresh(
2222
token: JWT,
23-
providers: ProviderWithId[],
23+
tokenProvider: ProviderWithId,
2424
proactiveTokenRefresh: boolean = false,
2525
): Promise<JWT> {
2626
const triggerProactiveRefresh =
@@ -30,17 +30,12 @@ export async function jwtWithRefresh(
3030
// Subsequent requests, `accessToken` is still valid
3131
return token;
3232
} else {
33-
const { refreshToken, provider } = token;
33+
const { refreshToken } = token;
3434
// Subsequent requests, `accessToken` has expired, try to refresh it
3535
if (!refreshToken) {
3636
throw new TypeError('Missing refreshToken');
3737
}
3838

39-
const tokenProvider = providers.find(({ id }) => id === provider);
40-
if (!tokenProvider) {
41-
throw new TypeError('No matching provider found');
42-
}
43-
4439
// Type assertion to ensure we have the OIDC options
4540
const providerOptions = tokenProvider.options as OIDCProviderOptions | undefined;
4641

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2025 © BeeAI a Series of LF Projects, LLC
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
.root {
7+
display: flex;
8+
flex-direction: column;
9+
gap: $spacing-06;
10+
max-inline-size: rem(540px);
11+
}

0 commit comments

Comments
 (0)