@eventcatalog/core 2.42.10 → 2.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/catalog-to-astro-content-directory.js +2 -2
- package/dist/{chunk-K7RD2O76.js → chunk-4DXH4NAT.js} +1 -1
- package/dist/{chunk-ZG2E6QCK.js → chunk-IZ7E57FH.js} +1 -1
- package/dist/{chunk-HDG7YSFG.js → chunk-LUUBKWYP.js} +8 -0
- package/dist/{chunk-QVBE3VN4.js → chunk-MXNHNGRS.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.auth.cjs +18 -0
- package/dist/eventcatalog.auth.d.cts +18 -0
- package/dist/eventcatalog.auth.d.ts +18 -0
- package/dist/eventcatalog.auth.js +0 -0
- package/dist/eventcatalog.cjs +50 -23
- package/dist/eventcatalog.js +29 -8
- package/dist/features.cjs +9 -0
- package/dist/features.d.cts +2 -1
- package/dist/features.d.ts +2 -1
- package/dist/features.js +3 -1
- package/dist/watcher.js +1 -1
- package/eventcatalog/astro.config.mjs +7 -3
- package/eventcatalog/auth.config.ts +142 -0
- package/eventcatalog/src/components/Header.astro +125 -25
- package/eventcatalog/src/middleware.ts +62 -0
- package/eventcatalog/src/pages/auth/error.astro +55 -0
- package/eventcatalog/src/pages/auth/login.astro +231 -0
- package/eventcatalog/src/pages/directory/[type]/_index.data.ts +63 -0
- package/eventcatalog/src/pages/directory/[type]/index.astro +6 -23
- package/eventcatalog/src/pages/discover/[type]/_index.data.ts +62 -0
- package/eventcatalog/src/pages/discover/[type]/index.astro +7 -24
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +62 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +5 -37
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/_[filename].data.ts +98 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +68 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +5 -25
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +6 -25
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +6 -35
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/_[filename].data.ts +99 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/language/_index.data.ts +40 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/{language.astro → language/index.astro} +6 -20
- package/eventcatalog/src/pages/docs/custom/[...path]/_index.data.ts +49 -0
- package/eventcatalog/src/pages/docs/custom/[...path]/index.astro +5 -11
- package/eventcatalog/src/pages/docs/teams/[id]/_index.data.ts +46 -0
- package/eventcatalog/src/pages/docs/teams/[id]/index.astro +6 -10
- package/eventcatalog/src/pages/docs/users/[id]/_index.data.ts +46 -0
- package/eventcatalog/src/pages/docs/users/[id]/index.astro +5 -9
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +99 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +5 -29
- package/eventcatalog/src/utils/feature.ts +10 -0
- package/eventcatalog/src/utils/page-loaders/hybrid-page.ts +68 -0
- package/eventcatalog/tsconfig.json +2 -1
- package/package.json +3 -1
- package/dist/{chunk-SLEMYHTU.js → chunk-SFA7F3CQ.js} +3 -3
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { defineConfig } from 'auth-astro';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import GitHub from '@auth/core/providers/github';
|
|
4
|
+
import Okta from '@auth/core/providers/okta';
|
|
5
|
+
import type { Account, Profile, User, Session } from '@auth/core/types';
|
|
6
|
+
import { isAuthEnabled, isSSR } from '@utils/feature';
|
|
7
|
+
import Google from '@auth/core/providers/google';
|
|
8
|
+
|
|
9
|
+
// Need to try and read the eventcatalog.auth.js file and get the auth providers from there
|
|
10
|
+
const catalogDirectory = process.env.PROJECT_DIR || process.cwd();
|
|
11
|
+
|
|
12
|
+
const getAuthProviders = async () => {
|
|
13
|
+
try {
|
|
14
|
+
const config = await import(/* @vite-ignore */ join(catalogDirectory, 'eventcatalog.auth.js'));
|
|
15
|
+
const authConfig = config.default;
|
|
16
|
+
|
|
17
|
+
const providers = [];
|
|
18
|
+
|
|
19
|
+
// GitHub provider
|
|
20
|
+
if (authConfig.providers?.github) {
|
|
21
|
+
const githubConfig = authConfig.providers.github;
|
|
22
|
+
providers.push(
|
|
23
|
+
GitHub({
|
|
24
|
+
clientId: githubConfig.clientId,
|
|
25
|
+
clientSecret: githubConfig.clientSecret,
|
|
26
|
+
})
|
|
27
|
+
);
|
|
28
|
+
console.log('✅ GitHub provider configured');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Google provider
|
|
32
|
+
if (authConfig.providers?.google) {
|
|
33
|
+
const googleConfig = authConfig.providers.google;
|
|
34
|
+
providers.push(
|
|
35
|
+
Google({
|
|
36
|
+
clientId: googleConfig.clientId,
|
|
37
|
+
clientSecret: googleConfig.clientSecret,
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
console.log('✅ Google provider configured');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Okta provider
|
|
44
|
+
if (authConfig.providers?.okta) {
|
|
45
|
+
const oktaConfig = authConfig.providers.okta;
|
|
46
|
+
providers.push(
|
|
47
|
+
Okta({
|
|
48
|
+
clientId: oktaConfig.clientId,
|
|
49
|
+
clientSecret: oktaConfig.clientSecret,
|
|
50
|
+
issuer: oktaConfig.issuer,
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
console.log('✅ Okta provider configured');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (providers.length === 0) {
|
|
57
|
+
console.warn('⚠️ No auth providers configured');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return providers;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.log('No eventcatalog.auth.js found or error loading config:', (error as Error).message);
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const getAuthConfig = async () => {
|
|
68
|
+
// If auth is disabled or we are not in SSR, return an empty config
|
|
69
|
+
if (!isAuthEnabled() || !isSSR()) {
|
|
70
|
+
return {
|
|
71
|
+
providers: [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const config = await import(/* @vite-ignore */ join(catalogDirectory, 'eventcatalog.auth.js'));
|
|
76
|
+
const authConfig = config.default;
|
|
77
|
+
|
|
78
|
+
// If custom auth config is specified (Enterprise feature)
|
|
79
|
+
if (authConfig?.customAuthConfig) {
|
|
80
|
+
console.log('🚀 Loading custom auth configuration:', authConfig.customAuthConfig);
|
|
81
|
+
try {
|
|
82
|
+
const customConfig = await import(/* @vite-ignore */ join(catalogDirectory, authConfig.customAuthConfig));
|
|
83
|
+
return customConfig.default;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error('❌ Failed to load custom auth config:', error);
|
|
86
|
+
// Fall back to managed config
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Return managed auth config
|
|
91
|
+
const providers = await getAuthProviders();
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
providers,
|
|
95
|
+
callbacks: {
|
|
96
|
+
async signIn({ user, account, profile }: { user: User; account: Account | null; profile?: Profile }) {
|
|
97
|
+
// Just allow everyone who can authenticate with the provider
|
|
98
|
+
return true;
|
|
99
|
+
},
|
|
100
|
+
async session({ session, token }: { session: Session; token: any }) {
|
|
101
|
+
// Add provider info to session
|
|
102
|
+
if (token?.provider) {
|
|
103
|
+
(session.user as any).provider = token.provider;
|
|
104
|
+
}
|
|
105
|
+
if (token?.login) {
|
|
106
|
+
(session.user as any).username = token.login;
|
|
107
|
+
}
|
|
108
|
+
return session;
|
|
109
|
+
},
|
|
110
|
+
async jwt({ token, account, profile }: { token: any; account: Account | null; profile?: Profile }) {
|
|
111
|
+
// Persist provider info in JWT
|
|
112
|
+
if (account && profile) {
|
|
113
|
+
token.provider = account.provider;
|
|
114
|
+
token.login = (profile as any).login || (profile as any).preferred_username;
|
|
115
|
+
}
|
|
116
|
+
return token;
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
pages: {
|
|
120
|
+
signIn: '/auth/login',
|
|
121
|
+
error: '/auth/error',
|
|
122
|
+
},
|
|
123
|
+
session: {
|
|
124
|
+
strategy: 'jwt' as const,
|
|
125
|
+
maxAge: authConfig?.session?.maxAge || 30 * 24 * 60 * 60, // 30 days default
|
|
126
|
+
},
|
|
127
|
+
debug: authConfig?.debug || false,
|
|
128
|
+
};
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.log(
|
|
131
|
+
'No auth config found, auth disabled. If you want to use auth, create a eventcatalog.auth.js file in your project directory.'
|
|
132
|
+
);
|
|
133
|
+
return {
|
|
134
|
+
providers: [],
|
|
135
|
+
pages: {
|
|
136
|
+
signIn: '/auth/disabled',
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export default defineConfig(await getAuthConfig());
|
|
@@ -3,6 +3,13 @@ import catalog from '@utils/eventcatalog-config/catalog';
|
|
|
3
3
|
import Search from '@components/Search.astro';
|
|
4
4
|
import { buildUrl } from '@utils/url-builder';
|
|
5
5
|
import { showEventCatalogBranding, showCustomBranding } from '@utils/feature';
|
|
6
|
+
import { getSession } from 'auth-astro/server';
|
|
7
|
+
import { isAuthEnabled, isSSR } from '@utils/feature';
|
|
8
|
+
|
|
9
|
+
let session = null;
|
|
10
|
+
if (isAuthEnabled()) {
|
|
11
|
+
session = await getSession(Astro.request);
|
|
12
|
+
}
|
|
6
13
|
|
|
7
14
|
const logo = {
|
|
8
15
|
src: ('/' + (catalog?.logo?.src || 'logo.png')).replace(/^\/+/, '/'),
|
|
@@ -27,35 +34,94 @@ const repositoryUrl = catalog?.repositoryUrl || 'https://github.com/event-catalo
|
|
|
27
34
|
</div>
|
|
28
35
|
|
|
29
36
|
<div class="hidden lg:block flex-grow w-6/12 px-10">
|
|
30
|
-
|
|
37
|
+
<!-- Page find only works on static builds, disable for SSR builds for now -->
|
|
38
|
+
{!isSSR() && <Search />}
|
|
31
39
|
</div>
|
|
32
40
|
|
|
33
41
|
<div class="hidden md:block w-3/12">
|
|
34
42
|
{
|
|
35
|
-
|
|
36
|
-
<
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
43
|
+
session ? (
|
|
44
|
+
<div class="flex justify-end pr-2">
|
|
45
|
+
<div class="relative">
|
|
46
|
+
<button
|
|
47
|
+
id="profile-menu-button"
|
|
48
|
+
type="button"
|
|
49
|
+
class="flex items-center focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 rounded-full"
|
|
50
|
+
aria-expanded="false"
|
|
51
|
+
aria-haspopup="true"
|
|
52
|
+
>
|
|
53
|
+
{session.user?.image ? (
|
|
54
|
+
<img
|
|
55
|
+
src={session.user.image}
|
|
56
|
+
alt={session.user?.name || 'User'}
|
|
57
|
+
class="h-8 w-8 rounded-full border-2 border-gray-200 hover:border-gray-300 transition-colors"
|
|
58
|
+
/>
|
|
59
|
+
) : (
|
|
60
|
+
<div class="h-8 w-8 rounded-full border-2 border-gray-200 hover:border-gray-300 transition-colors bg-gray-100 flex items-center justify-center text-sm font-medium text-gray-600">
|
|
61
|
+
{session.user?.name
|
|
62
|
+
? session.user.name
|
|
63
|
+
.split(' ')
|
|
64
|
+
.map((n) => n[0])
|
|
65
|
+
.join('')
|
|
66
|
+
.substring(0, 2)
|
|
67
|
+
.toUpperCase()
|
|
68
|
+
: 'U'}
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
</button>
|
|
72
|
+
<div
|
|
73
|
+
id="profile-dropdown"
|
|
74
|
+
class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50 border border-gray-100"
|
|
75
|
+
>
|
|
76
|
+
<div class="px-4 py-2 text-sm text-gray-700 border-b border-gray-100">
|
|
77
|
+
<div class="font-medium">{session.user?.name || 'User'}</div>
|
|
78
|
+
{session.user?.email && <div class="text-gray-500">{session.user.email}</div>}
|
|
79
|
+
</div>
|
|
80
|
+
<button
|
|
81
|
+
id="signout-btn"
|
|
82
|
+
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors"
|
|
83
|
+
>
|
|
84
|
+
Sign out
|
|
85
|
+
</button>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
) : (
|
|
90
|
+
<>
|
|
91
|
+
<div class="flex items-center space-x-4 justify-end pr-2">
|
|
92
|
+
{isAuthEnabled() && (
|
|
93
|
+
<button
|
|
94
|
+
id="okta-signin-btn"
|
|
95
|
+
class="bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium px-4 py-2 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
96
|
+
>
|
|
97
|
+
Sign In
|
|
98
|
+
</button>
|
|
99
|
+
)}
|
|
100
|
+
{showEventCatalogBranding() && (
|
|
101
|
+
<ul class="flex space-x-8">
|
|
102
|
+
<li>
|
|
103
|
+
<a href="https://discord.com/invite/3rjaZMmrAm">
|
|
104
|
+
<img src={buildUrl('/icons/discord.svg', true)} class="h-7 w-7" />
|
|
105
|
+
</a>
|
|
106
|
+
</li>
|
|
107
|
+
<li>
|
|
108
|
+
<a href="https://github.com/event-catalog/eventcatalog">
|
|
109
|
+
<img src={buildUrl('/icons/github.svg', true)} class="h-7 w-7" />
|
|
110
|
+
</a>
|
|
111
|
+
</li>
|
|
112
|
+
</ul>
|
|
113
|
+
)}
|
|
114
|
+
{showCustomBranding() && !showEventCatalogBranding() && (
|
|
115
|
+
<ul class="flex space-x-8">
|
|
116
|
+
<li>
|
|
117
|
+
<a href={repositoryUrl} class="text-gray-500 hover:text-gray-600 focus:outline-none focus:text-gray-600">
|
|
118
|
+
<img src={buildUrl('/icons/github.svg', true)} class="h-7 w-7" />
|
|
119
|
+
</a>
|
|
120
|
+
</li>
|
|
121
|
+
</ul>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</>
|
|
59
125
|
)
|
|
60
126
|
}
|
|
61
127
|
</div>
|
|
@@ -100,12 +166,46 @@ const repositoryUrl = catalog?.repositoryUrl || 'https://github.com/event-catalo
|
|
|
100
166
|
<script>
|
|
101
167
|
const menuToggle = document.getElementById('menu-toggle');
|
|
102
168
|
const mobileMenu = document.getElementById('mobile-menu');
|
|
169
|
+
import { signOut } from 'auth-astro/client';
|
|
103
170
|
|
|
104
171
|
if (menuToggle && mobileMenu) {
|
|
105
172
|
menuToggle.addEventListener('click', () => {
|
|
106
173
|
mobileMenu.classList.toggle('hidden');
|
|
107
174
|
});
|
|
108
175
|
}
|
|
176
|
+
|
|
177
|
+
// Profile dropdown functionality
|
|
178
|
+
const profileButton = document.getElementById('profile-menu-button');
|
|
179
|
+
const profileDropdown = document.getElementById('profile-dropdown');
|
|
180
|
+
|
|
181
|
+
if (profileButton && profileDropdown) {
|
|
182
|
+
profileButton.addEventListener('click', (e) => {
|
|
183
|
+
e.stopPropagation();
|
|
184
|
+
profileDropdown.classList.toggle('hidden');
|
|
185
|
+
profileButton.setAttribute('aria-expanded', !profileDropdown.classList.contains('hidden') ? 'true' : 'false');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Close dropdown when clicking outside
|
|
189
|
+
document.addEventListener('click', (e) => {
|
|
190
|
+
if (!profileButton.contains(e.target as Node) && !profileDropdown.contains(e.target as Node)) {
|
|
191
|
+
profileDropdown.classList.add('hidden');
|
|
192
|
+
profileButton.setAttribute('aria-expanded', 'false');
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Close dropdown on escape key
|
|
197
|
+
document.addEventListener('keydown', (e) => {
|
|
198
|
+
if (e.key === 'Escape' && !profileDropdown.classList.contains('hidden')) {
|
|
199
|
+
profileDropdown.classList.add('hidden');
|
|
200
|
+
profileButton.setAttribute('aria-expanded', 'false');
|
|
201
|
+
profileButton.focus();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
document.getElementById('signout-btn')?.addEventListener('click', async () => {
|
|
206
|
+
await signOut();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
109
209
|
</script>
|
|
110
210
|
|
|
111
211
|
<style>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/middleware.ts
|
|
2
|
+
import type { MiddlewareHandler } from 'astro';
|
|
3
|
+
import { getSession } from 'auth-astro/server';
|
|
4
|
+
import { isAuthEnabled } from '@utils/feature';
|
|
5
|
+
|
|
6
|
+
export const onRequest: MiddlewareHandler = async (context, next) => {
|
|
7
|
+
const { request, redirect, locals } = context;
|
|
8
|
+
const url = new URL(request.url);
|
|
9
|
+
const pathname = url.pathname;
|
|
10
|
+
|
|
11
|
+
// If auth is disabled and we are on an auth route, redirect to home
|
|
12
|
+
if (!isAuthEnabled() && pathname.includes('/auth')) {
|
|
13
|
+
return redirect('/');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Auth is disabled, skip auth check
|
|
17
|
+
if (!isAuthEnabled()) {
|
|
18
|
+
return next();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Skip system/browser requests
|
|
22
|
+
const systemRoutes = ['/.well-known/', '/favicon.ico', '/robots.txt', '/sitemap.xml', '/_astro/', '/__astro'];
|
|
23
|
+
|
|
24
|
+
// Skip auth check for these routes
|
|
25
|
+
const publicRoutes = ['/auth/login', '/auth/signout', '/auth/error', '/api/auth'];
|
|
26
|
+
|
|
27
|
+
// Skip static files, system routes, and browser requests
|
|
28
|
+
if (
|
|
29
|
+
pathname.startsWith('/_') ||
|
|
30
|
+
systemRoutes.some((route) => pathname.startsWith(route)) ||
|
|
31
|
+
pathname.startsWith('/.well-known/')
|
|
32
|
+
) {
|
|
33
|
+
return next();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Skip public routes
|
|
37
|
+
if (publicRoutes.some((route) => pathname.startsWith(route))) {
|
|
38
|
+
return next();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Check if user is logged in
|
|
43
|
+
const session = await getSession(request);
|
|
44
|
+
|
|
45
|
+
if (!session) {
|
|
46
|
+
const callbackUrl = encodeURIComponent(pathname + url.search);
|
|
47
|
+
return redirect(`/auth/login?callbackUrl=${callbackUrl}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Add session to locals for pages to use
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
locals.session = session;
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
locals.user = session.user;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error('Session error:', error);
|
|
57
|
+
const callbackUrl = encodeURIComponent(pathname + url.search);
|
|
58
|
+
return redirect(`/auth/login?callbackUrl=${callbackUrl}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return next();
|
|
62
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
// src/pages/auth/error.astro
|
|
3
|
+
|
|
4
|
+
const { searchParams } = new URL(Astro.request.url);
|
|
5
|
+
const error = searchParams.get('error');
|
|
6
|
+
|
|
7
|
+
const errorMessages = {
|
|
8
|
+
Configuration: 'There is a problem with the server configuration.',
|
|
9
|
+
AccessDenied: 'You do not have permission to access this resource.',
|
|
10
|
+
Verification: 'The verification token has expired or has already been used.',
|
|
11
|
+
Default: 'An error occurred during authentication.',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
const errorMessage = errorMessages[error] || errorMessages.Default;
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<main title="Authentication Error - EventCatalog">
|
|
19
|
+
<div class="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
|
|
20
|
+
<div class="max-w-md w-full space-y-8 text-center">
|
|
21
|
+
<div>
|
|
22
|
+
<svg class="mx-auto h-12 w-12 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
23
|
+
<path
|
|
24
|
+
stroke-linecap="round"
|
|
25
|
+
stroke-linejoin="round"
|
|
26
|
+
stroke-width="2"
|
|
27
|
+
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"
|
|
28
|
+
></path>
|
|
29
|
+
</svg>
|
|
30
|
+
<h2 class="mt-6 text-3xl font-bold text-gray-900">Authentication Error</h2>
|
|
31
|
+
<p class="mt-2 text-sm text-gray-600">
|
|
32
|
+
{errorMessage}
|
|
33
|
+
</p>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="space-y-4">
|
|
37
|
+
<a
|
|
38
|
+
href="/auth/login"
|
|
39
|
+
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
40
|
+
>
|
|
41
|
+
Try Again
|
|
42
|
+
</a>
|
|
43
|
+
|
|
44
|
+
<a
|
|
45
|
+
href="/"
|
|
46
|
+
class="w-full flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
47
|
+
>
|
|
48
|
+
Back to Home
|
|
49
|
+
</a>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{error && <div class="mt-8 text-xs text-gray-400">Error code: {error}</div>}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</main>
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
import config from '@config';
|
|
3
|
+
const { title, logo } = config;
|
|
4
|
+
import { getSession } from 'auth-astro/server';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { isAuthEnabled, isSSR } from '@utils/feature';
|
|
7
|
+
|
|
8
|
+
const session = await getSession(Astro.request);
|
|
9
|
+
const catalogDirectory = process.env.PROJECT_DIR || process.cwd();
|
|
10
|
+
|
|
11
|
+
let hasAuthConfigurationFile = false;
|
|
12
|
+
let providers: string[] = [];
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const authConfig = await import(/* @vite-ignore */ join(catalogDirectory, 'eventcatalog.auth.js'));
|
|
16
|
+
providers = Object.keys(authConfig.default.providers);
|
|
17
|
+
hasAuthConfigurationFile = true;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
hasAuthConfigurationFile = false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check if we should show login (auth file exists, SSR enabled, auth enabled, and has providers)
|
|
23
|
+
const shouldShowLogin = hasAuthConfigurationFile && isSSR() && isAuthEnabled() && providers.length > 0;
|
|
24
|
+
|
|
25
|
+
// Check if configuration exists but no providers are set up
|
|
26
|
+
const hasConfigButNoProviders = hasAuthConfigurationFile && isSSR() && isAuthEnabled() && providers.length === 0;
|
|
27
|
+
|
|
28
|
+
if (session) {
|
|
29
|
+
return Astro.redirect('/');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Provider configurations
|
|
33
|
+
const providerConfig = {
|
|
34
|
+
github: {
|
|
35
|
+
name: 'GitHub',
|
|
36
|
+
icon: `<svg class="mr-2 h-5 w-5" fill="currentColor" viewBox="0 0 24 24">
|
|
37
|
+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"></path>
|
|
38
|
+
</svg>`,
|
|
39
|
+
},
|
|
40
|
+
google: {
|
|
41
|
+
name: 'Google',
|
|
42
|
+
icon: `<svg class="mr-2 h-5 w-5" viewBox="0 0 24 24">
|
|
43
|
+
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
|
|
44
|
+
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
|
|
45
|
+
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
|
|
46
|
+
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
|
|
47
|
+
</svg>`,
|
|
48
|
+
},
|
|
49
|
+
okta: {
|
|
50
|
+
name: 'Okta',
|
|
51
|
+
icon: `<svg class="mr-2 h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
|
|
52
|
+
<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm0 18c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8zm-1-13h2v6h-2V7zm0 8h2v2h-2v-2z"></path>
|
|
53
|
+
</svg>`,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
<!doctype html>
|
|
59
|
+
<html lang="en">
|
|
60
|
+
<head>
|
|
61
|
+
<meta charset="UTF-8" />
|
|
62
|
+
<meta name="viewport" content="width=device-width" />
|
|
63
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
64
|
+
<meta name="generator" content={Astro.generator} />
|
|
65
|
+
<title>Sign In | {title}</title>
|
|
66
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
67
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
68
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
|
69
|
+
<style>
|
|
70
|
+
body {
|
|
71
|
+
font-family: 'Inter', sans-serif;
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
74
|
+
</head>
|
|
75
|
+
<body class="bg-gray-50 min-h-screen">
|
|
76
|
+
<!-- Header -->
|
|
77
|
+
<nav class="fixed top-0 left-0 right-0 h-16 bg-white border-b border-gray-100 py-3 z-10">
|
|
78
|
+
<div class="px-4 sm:px-4 lg:px-4">
|
|
79
|
+
<div class="flex justify-between items-center h-10">
|
|
80
|
+
<!-- Logo -->
|
|
81
|
+
<div class="flex-shrink-0 flex items-center">
|
|
82
|
+
{
|
|
83
|
+
logo && (
|
|
84
|
+
<a href="/" class="flex space-x-2 items-center">
|
|
85
|
+
<img alt={logo.alt} src={logo.src} class="w-8 h-8" />
|
|
86
|
+
<span class="hidden sm:inline-block text-lg font-bold">{title}</span>
|
|
87
|
+
</a>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<!-- GitHub Link -->
|
|
93
|
+
<div class="flex items-center">
|
|
94
|
+
<a
|
|
95
|
+
href="https://github.com/event-catalog/eventcatalog"
|
|
96
|
+
target="_blank"
|
|
97
|
+
class="text-gray-500 hover:text-gray-600 focus:outline-none focus:text-gray-600"
|
|
98
|
+
>
|
|
99
|
+
<svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24">
|
|
100
|
+
<path
|
|
101
|
+
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
|
102
|
+
></path>
|
|
103
|
+
</svg>
|
|
104
|
+
</a>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</nav>
|
|
109
|
+
|
|
110
|
+
<!-- Content with top padding to account for fixed header -->
|
|
111
|
+
<div class="flex min-h-screen flex-col items-center justify-center py-12 px-4 sm:px-6 lg:px-8 pt-24 bg-gray-50">
|
|
112
|
+
{
|
|
113
|
+
shouldShowLogin ? (
|
|
114
|
+
<div class="w-full max-w-md space-y-8">
|
|
115
|
+
<div class="text-center">
|
|
116
|
+
<h2 class="text-3xl font-bold tracking-tight text-gray-900">Welcome to {title} EventCatalog</h2>
|
|
117
|
+
<p class="mt-2 text-gray-600">Sign in to your account</p>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div class="mt-8 space-y-4">
|
|
121
|
+
{providers.map((provider) => {
|
|
122
|
+
const config = providerConfig[provider as keyof typeof providerConfig];
|
|
123
|
+
if (!config) return null;
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<button
|
|
127
|
+
data-provider={provider}
|
|
128
|
+
class="provider-login-btn flex w-full items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2"
|
|
129
|
+
>
|
|
130
|
+
<Fragment set:html={config.icon} />
|
|
131
|
+
Sign in with {config.name}
|
|
132
|
+
</button>
|
|
133
|
+
);
|
|
134
|
+
})}
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
) : hasConfigButNoProviders ? (
|
|
138
|
+
<div class="w-full max-w-2xl space-y-8">
|
|
139
|
+
<div class="text-center">
|
|
140
|
+
<h2 class="text-3xl font-bold tracking-tight text-gray-900">No Authentication Providers Configured</h2>
|
|
141
|
+
<p class="mt-4 text-lg text-gray-600">
|
|
142
|
+
Authentication is enabled but no providers are configured in your auth configuration file.
|
|
143
|
+
</p>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div class="bg-white shadow-sm rounded-lg p-6 space-y-4">
|
|
147
|
+
<h3 class="text-lg font-semibold text-gray-900">To add authentication providers:</h3>
|
|
148
|
+
<ol class="list-decimal list-inside space-y-2 text-gray-700">
|
|
149
|
+
<li>
|
|
150
|
+
Update your <code class="bg-gray-100 px-2 py-1 rounded text-sm">eventcatalog.auth.js</code> file to include at
|
|
151
|
+
least one provider (GitHub, Google, Okta, etc.)
|
|
152
|
+
</li>
|
|
153
|
+
<li>Configure the provider with the necessary credentials and settings</li>
|
|
154
|
+
<li>Restart your EventCatalog server to apply the changes</li>
|
|
155
|
+
</ol>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div class="text-center">
|
|
159
|
+
<a
|
|
160
|
+
href="#"
|
|
161
|
+
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
162
|
+
>
|
|
163
|
+
Read Authentication Documentation
|
|
164
|
+
</a>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
) : (
|
|
168
|
+
<div class="w-full max-w-2xl space-y-8">
|
|
169
|
+
<div class="text-center">
|
|
170
|
+
<h2 class="text-3xl font-bold tracking-tight text-gray-900">Authentication Not Configured</h2>
|
|
171
|
+
<p class="mt-4 text-lg text-gray-600">
|
|
172
|
+
{!hasAuthConfigurationFile
|
|
173
|
+
? 'No authentication configuration file found.'
|
|
174
|
+
: 'Authentication is not properly enabled.'}
|
|
175
|
+
</p>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<div class="bg-white shadow-sm rounded-lg p-6 space-y-4">
|
|
179
|
+
<h3 class="text-lg font-semibold text-gray-900">To enable authentication:</h3>
|
|
180
|
+
<ol class="list-decimal list-inside space-y-2 text-gray-700">
|
|
181
|
+
{!hasAuthConfigurationFile && (
|
|
182
|
+
<li>
|
|
183
|
+
Create an <code class="bg-gray-100 px-2 py-1 rounded text-sm">eventcatalog.auth.js</code> configuration file
|
|
184
|
+
in your project root
|
|
185
|
+
</li>
|
|
186
|
+
)}
|
|
187
|
+
{!isSSR() && (
|
|
188
|
+
<li>
|
|
189
|
+
Enable SSR (Server-Side Rendering) in your{' '}
|
|
190
|
+
<code class="bg-gray-100 px-2 py-1 rounded text-sm">eventcatalog.config.js</code> file by setting{' '}
|
|
191
|
+
<code class="bg-gray-100 px-2 py-1 rounded text-sm">output: 'server'</code>
|
|
192
|
+
</li>
|
|
193
|
+
)}
|
|
194
|
+
{!isAuthEnabled() && (
|
|
195
|
+
<li>
|
|
196
|
+
Enable authentication in your{' '}
|
|
197
|
+
<code class="bg-gray-100 px-2 py-1 rounded text-sm">eventcatalog.config.js</code> file by setting{' '}
|
|
198
|
+
<code class="bg-gray-100 px-2 py-1 rounded text-sm">auth: {`{ enabled: true }`}</code>
|
|
199
|
+
</li>
|
|
200
|
+
)}
|
|
201
|
+
</ol>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<div class="text-center">
|
|
205
|
+
<a
|
|
206
|
+
href="#"
|
|
207
|
+
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
|
208
|
+
>
|
|
209
|
+
Read Authentication Documentation
|
|
210
|
+
</a>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
</div>
|
|
216
|
+
</body>
|
|
217
|
+
</html>
|
|
218
|
+
|
|
219
|
+
<script>
|
|
220
|
+
import { signIn } from 'auth-astro/client';
|
|
221
|
+
|
|
222
|
+
// Add event listeners to all provider login buttons
|
|
223
|
+
const providerButtons = document.querySelectorAll('.provider-login-btn');
|
|
224
|
+
|
|
225
|
+
providerButtons.forEach((button) => {
|
|
226
|
+
const provider = button.getAttribute('data-provider');
|
|
227
|
+
if (provider) {
|
|
228
|
+
button.addEventListener('click', () => signIn(provider));
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
</script>
|