@htlkg/astro 0.0.1 → 0.0.3

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.
Files changed (41) hide show
  1. package/README.md +24 -8
  2. package/dist/chunk-2GML443T.js +273 -0
  3. package/dist/chunk-2GML443T.js.map +1 -0
  4. package/dist/{chunk-Z2ZAL7KX.js → chunk-UBF5F2RG.js} +1 -1
  5. package/dist/{chunk-Z2ZAL7KX.js.map → chunk-UBF5F2RG.js.map} +1 -1
  6. package/dist/chunk-XOY5BM3N.js +151 -0
  7. package/dist/chunk-XOY5BM3N.js.map +1 -0
  8. package/dist/htlkg/config.js +1 -1
  9. package/dist/htlkg/index.js +1 -1
  10. package/dist/index.js +126 -14
  11. package/dist/index.js.map +1 -1
  12. package/dist/middleware/index.js +27 -28
  13. package/dist/middleware/index.js.map +1 -1
  14. package/dist/utils/index.js +31 -12
  15. package/dist/vue-app-setup.js +47 -0
  16. package/dist/vue-app-setup.js.map +1 -0
  17. package/package.json +60 -26
  18. package/src/auth/auth.md +77 -0
  19. package/src/components/Island.astro +56 -0
  20. package/src/components/components.md +79 -0
  21. package/src/factories/createListPage.ts +290 -0
  22. package/src/factories/index.ts +16 -0
  23. package/src/htlkg/config.ts +10 -0
  24. package/src/htlkg/htlkg.md +63 -0
  25. package/src/htlkg/index.ts +49 -157
  26. package/src/index.ts +3 -0
  27. package/src/layouts/AdminLayout.astro +103 -92
  28. package/src/layouts/layouts.md +87 -0
  29. package/src/middleware/auth.ts +42 -0
  30. package/src/middleware/middleware.md +82 -0
  31. package/src/middleware/route-guards.ts +4 -28
  32. package/src/patterns/patterns.md +104 -0
  33. package/src/utils/filters.ts +320 -0
  34. package/src/utils/index.ts +8 -2
  35. package/src/utils/params.ts +260 -0
  36. package/src/utils/utils.md +86 -0
  37. package/src/vue-app-setup.ts +21 -28
  38. package/dist/chunk-WLOFOVCL.js +0 -210
  39. package/dist/chunk-WLOFOVCL.js.map +0 -1
  40. package/dist/chunk-ZQ4XMJH7.js +0 -1
  41. package/dist/chunk-ZQ4XMJH7.js.map +0 -1
@@ -1,12 +1,7 @@
1
1
  /**
2
2
  * htlkg Astro Integration
3
3
  *
4
- * Provides zero-config setup for Hotelinking applications with:
5
- * - Tailwind CSS integration
6
- * - Vue 3 integration with Amplify setup
7
- * - Authentication middleware
8
- * - Route guards
9
- * - Login page generation
4
+ * Supports static, hybrid, and full SSR output modes.
10
5
  */
11
6
 
12
7
  import tailwind from '@astrojs/tailwind';
@@ -14,47 +9,13 @@ import vue from '@astrojs/vue';
14
9
  import type { AstroIntegration } from 'astro';
15
10
  import type { HtlkgIntegrationOptions } from './config.js';
16
11
  import { createVirtualModulePlugin, virtualModuleTypes } from './virtual-modules.js';
12
+ import vueDevTools from 'vite-plugin-vue-devtools';
17
13
 
18
- /**
19
- * Default environment variables required for AWS Amplify authentication
20
- */
21
14
  const DEFAULT_ENV_VARS = [
22
15
  'PUBLIC_COGNITO_USER_POOL_ID',
23
16
  'PUBLIC_COGNITO_USER_POOL_CLIENT_ID'
24
17
  ];
25
18
 
26
- /**
27
- * htlkg Astro integration that provides zero-config authentication setup.
28
- *
29
- * This integration automatically:
30
- * - Includes Tailwind CSS integration (can be disabled)
31
- * - Includes Vue 3 integration with Amplify and Nanostores setup
32
- * - Injects authentication middleware for AWS Amplify
33
- * - Configures route guards based on declarative configuration
34
- * - Validates required environment variables (optional)
35
- * - Injects TypeScript types for Astro.locals.user
36
- * - Provides a default login page (optional)
37
- *
38
- * @param options - Configuration options for the integration
39
- * @returns Astro integration object or array of integrations
40
- *
41
- * @example
42
- * // astro.config.mjs
43
- * import { htlkg } from '@htlkg/astro';
44
- *
45
- * export default defineConfig({
46
- * integrations: [
47
- * htlkg({
48
- * tailwind: { configFile: './tailwind.config.mjs' },
49
- * auth: {
50
- * publicRoutes: ['/login', '/'],
51
- * adminRoutes: [/^\/admin/],
52
- * loginUrl: '/login'
53
- * }
54
- * })
55
- * ]
56
- * });
57
- */
58
19
  export function htlkg(
59
20
  options: HtlkgIntegrationOptions = {},
60
21
  ): AstroIntegration | AstroIntegration[] {
@@ -65,6 +26,7 @@ export function htlkg(
65
26
  requiredEnvVars = DEFAULT_ENV_VARS,
66
27
  tailwind: tailwindOptions,
67
28
  amplify,
29
+ vueAppSetup = 'auto',
68
30
  } = options;
69
31
 
70
32
  const integrations: AstroIntegration[] = [];
@@ -78,146 +40,77 @@ export function htlkg(
78
40
  );
79
41
  }
80
42
 
81
- // Add Vue integration with Amplify setup entrypoint
82
- // Use package import specifier that Vite can resolve
83
- integrations.push(
84
- vue({
85
- appEntrypoint: '@htlkg/astro/vue-app-setup',
86
- }),
87
- );
43
+ // Determine Vue setup mode:
44
+ // - 'full': Use Amplify app entrypoint (requires SSR)
45
+ // - 'basic': Basic Vue without app entrypoint (works with static)
46
+ // - 'auto': Default to 'basic' for compatibility with static builds
47
+ const useFullVueSetup = vueAppSetup === 'full';
48
+
49
+ // Add Vue integration
50
+ if (useFullVueSetup) {
51
+ integrations.push(vue({ appEntrypoint: '@htlkg/astro/vue-app-setup' }));
52
+ } else {
53
+ integrations.push(vue());
54
+ }
88
55
 
89
56
  // Add the main htlkg integration
90
57
  integrations.push({
91
58
  name: '@htlkg/astro',
92
59
  hooks: {
93
- 'astro:config:setup': ({
94
- config,
95
- updateConfig,
96
- addMiddleware,
97
- injectRoute,
98
- logger,
99
- }) => {
60
+ 'astro:config:setup': ({ updateConfig, addMiddleware, injectRoute, logger }) => {
100
61
  try {
101
- // 1. Verify Vue integration is present
102
- const hasVue = config.integrations.some(
103
- (i) => i.name === '@astrojs/vue',
104
- );
105
- if (hasVue) {
106
- logger.info('Vue integration configured with Amplify app setup');
107
- }
62
+ logger.info(useFullVueSetup
63
+ ? 'Vue configured with Amplify app setup'
64
+ : 'Vue configured (basic mode)');
108
65
 
109
- // 2. Amplify will be configured by the middleware on first request
110
66
  if (amplify) {
111
- logger.info('Amplify configuration provided - will be configured on first request');
112
- } else {
113
- logger.info('No Amplify configuration provided - will use environment variables');
67
+ logger.info('Amplify configuration provided');
114
68
  }
115
69
 
116
- // 3. Validate environment variables (only if not using amplify_outputs.json)
117
- if (validateEnv && !amplify) {
118
- const missing = requiredEnvVars.filter(
119
- (varName) => !process.env[varName],
120
- );
121
-
70
+ // Validate env vars (only for full setup)
71
+ if (validateEnv && !amplify && useFullVueSetup) {
72
+ const missing = requiredEnvVars.filter(v => !process.env[v]);
122
73
  if (missing.length > 0) {
123
- logger.warn(
124
- `Missing required environment variables: ${missing.join(', ')}\nAuthentication may not work correctly. Please set these in your .env file:\n${missing.map((v) => ` - ${v}`).join('\n')}`,
125
- );
126
- } else {
127
- logger.info('All required environment variables are present');
74
+ logger.warn(`Missing env vars: ${missing.join(', ')}`);
128
75
  }
129
76
  }
130
77
 
131
- // 4. Create Vite virtual module plugin to pass configuration to middleware and pages
132
- try {
133
- const virtualModulePlugin = createVirtualModulePlugin(
134
- auth,
135
- loginPage,
136
- amplify || null
137
- );
138
-
139
- updateConfig({
140
- vite: {
141
- plugins: [virtualModulePlugin],
142
- },
143
- });
144
- } catch (error) {
145
- const errorMsg =
146
- error instanceof Error ? error.message : 'Unknown error';
147
- logger.error(
148
- `Failed to create virtual module for route configuration: ${errorMsg}`,
149
- );
150
- throw error;
78
+ // Create Vite virtual module plugin
79
+ const virtualModulePlugin = createVirtualModulePlugin(auth, loginPage, amplify || null);
80
+ const vitePlugins: any[] = [virtualModulePlugin];
81
+
82
+ if (import.meta.env?.DEV !== false) {
83
+ vitePlugins.push(vueDevTools());
84
+ logger.info('Vue DevTools enabled');
151
85
  }
152
86
 
153
- // 5. Inject middleware
154
- try {
155
- addMiddleware({
156
- entrypoint: '@htlkg/astro/middleware',
157
- order: 'pre',
158
- });
159
- logger.info('Authentication middleware injected successfully');
160
- } catch (error) {
161
- const errorMsg =
162
- error instanceof Error ? error.message : 'Unknown error';
163
- logger.error(`Failed to inject middleware: ${errorMsg}`);
164
- throw error;
165
- }
87
+ updateConfig({ vite: { plugins: vitePlugins } });
166
88
 
167
- // 6. Verify Vue app entrypoint is configured
168
- const vueIntegrationIndex = config.integrations.findIndex(
169
- (i) => i.name === '@astrojs/vue',
170
- );
171
-
172
- if (vueIntegrationIndex === -1) {
173
- logger.warn(
174
- '@astrojs/vue integration not found.\n' +
175
- 'The htlkg integration should have added it automatically.\n' +
176
- 'If you see this warning, there may be an integration ordering issue.',
177
- );
178
- } else {
179
- logger.info('Vue app setup with Amplify configuration enabled');
89
+ // Inject middleware (only for full setup)
90
+ if (useFullVueSetup) {
91
+ addMiddleware({ entrypoint: '@htlkg/astro/middleware', order: 'pre' });
92
+ logger.info('Authentication middleware injected');
180
93
  }
181
94
 
182
- // 7. Verify Tailwind integration
183
- const hasTailwind = config.integrations.some(
184
- (i) =>
185
- i.name === '@astrojs/tailwind' || i.name === 'astro:tailwind',
186
- );
187
-
188
- if (hasTailwind) {
189
- logger.info('Tailwind CSS integration configured');
190
- }
191
-
192
- // 8. Inject login page route if enabled
193
- if (loginPage !== false) {
194
- try {
195
- const loginPath = loginPage.path || '/login';
196
- injectRoute({
197
- pattern: loginPath,
198
- entrypoint: '@htlkg/astro/auth/LoginPage.astro',
199
- prerender: false,
200
- });
201
- logger.info(`Injected default login page at ${loginPath}`);
202
- } catch (error) {
203
- const errorMsg =
204
- error instanceof Error ? error.message : 'Unknown error';
205
- logger.warn(`Failed to inject login page: ${errorMsg}`);
206
- }
95
+ // Inject login page (only for full setup)
96
+ if (loginPage !== false && useFullVueSetup) {
97
+ const loginPath = loginPage.path || '/login';
98
+ injectRoute({
99
+ pattern: loginPath,
100
+ entrypoint: '@htlkg/astro/auth/LoginPage.astro',
101
+ prerender: false,
102
+ });
103
+ logger.info(`Login page injected at ${loginPath}`);
207
104
  }
208
105
 
209
- logger.info('htlkg integration configured successfully');
106
+ logger.info('htlkg integration configured');
210
107
  } catch (error) {
211
- const errorMsg =
212
- error instanceof Error ? error.message : 'Unknown error';
213
- logger.error(
214
- `Failed to configure htlkg integration: ${errorMsg}`,
215
- );
108
+ const msg = error instanceof Error ? error.message : 'Unknown error';
109
+ logger.error(`htlkg configuration failed: ${msg}`);
216
110
  throw error;
217
111
  }
218
112
  },
219
113
  'astro:config:done': ({ injectTypes }) => {
220
- // Inject TypeScript types for Astro.locals and virtual module
221
114
  injectTypes({
222
115
  filename: 'htlkg.d.ts',
223
116
  content: `
@@ -240,6 +133,5 @@ export {};
240
133
  },
241
134
  });
242
135
 
243
- // Return single integration or array based on what was added
244
- return integrations.length === 1 ? integrations[0] : integrations;
136
+ return integrations;
245
137
  }
package/src/index.ts CHANGED
@@ -32,6 +32,9 @@ export { isAuthenticatedUser } from './htlkg/config.js';
32
32
  // Utils
33
33
  export * from './utils';
34
34
 
35
+ // Page Factories
36
+ export * from './factories';
37
+
35
38
  // Middleware is not exported from main index to avoid bundling astro:middleware
36
39
  // It's loaded by Astro at runtime via the entrypoint specified in addMiddleware
37
40
  // Import from '@htlkg/astro/middleware' if needed
@@ -2,13 +2,14 @@
2
2
  /**
3
3
  * Admin Layout
4
4
  *
5
- * Admin layout using uiWrapper from @hotelinking/ui with sidebar, topbar, and content area.
6
- * Implements the same logic as the old AdminWrapper.vue but directly in the layout.
7
- *
8
- * This layout will be fully implemented in a future task.
9
- * For now, it provides a basic structure.
5
+ * Admin layout using AdminWrapper component with uiWrapper, sidebar, and topbar.
6
+ * Simple admin layout without authentication - pages handle their own auth.
7
+ * Uses nanostores for user data (no props needed for user).
10
8
  */
11
9
 
10
+ import { AdminWrapper, setUser } from "@htlkg/components";
11
+ import { uiViewHeader as UiViewHeader } from "@hotelinking/ui";
12
+
12
13
  interface User {
13
14
  username?: string;
14
15
  email?: string;
@@ -18,12 +19,6 @@ interface User {
18
19
  };
19
20
  }
20
21
 
21
- interface BreadcrumbItem {
22
- label: string;
23
- routeName: string;
24
- current?: boolean;
25
- }
26
-
27
22
  interface SidebarItem {
28
23
  name: string;
29
24
  routeName: string;
@@ -42,51 +37,86 @@ interface SelectItem {
42
37
  id: string;
43
38
  }
44
39
 
45
- interface Props {
40
+ interface BreadcrumbPage {
41
+ name: string;
42
+ routeName: string;
43
+ current?: boolean;
44
+ }
45
+
46
+ interface HeaderProps {
46
47
  title: string;
48
+ subtitle?: string;
47
49
  description?: string;
50
+ breadcrumbs?: BreadcrumbPage[];
51
+ button?: {
52
+ text: string;
53
+ color?: "primary" | "secondary" | "red" | "yellow" | "green";
54
+ size?: "small" | "medium" | "big";
55
+ loading?: boolean;
56
+ disabled?: boolean;
57
+ };
58
+ }
59
+
60
+ interface SidebarProps {
61
+ logo: string;
62
+ title: string;
63
+ items: SidebarItem[];
48
64
  currentPage?: string;
49
- user?: User;
50
- showBreadcrumbs?: boolean;
51
- breadcrumbs?: BreadcrumbItem[];
52
- sidebarLogo?: string;
53
- sidebarTitle?: string;
54
- sidebarItems?: SidebarItem[];
55
- topbarActions?: TopbarAction[];
65
+ openByDefault?: boolean;
66
+ }
67
+
68
+ interface TopbarProps {
69
+ actions?: TopbarAction[];
56
70
  selectItems?: SelectItem[];
57
71
  selectedItem?: SelectItem;
58
- sidebarOpenByDefault?: boolean;
59
72
  }
60
73
 
74
+ interface Props {
75
+ header: HeaderProps;
76
+ sidebar: SidebarProps;
77
+ topbar?: TopbarProps;
78
+ user?: User;
79
+ }
80
+
81
+ const { header, sidebar, topbar, user } = Astro.props;
82
+
83
+ // Extract header props
84
+ const { title, subtitle, description, breadcrumbs = [], button } = header;
85
+
86
+ // Extract sidebar props
61
87
  const {
62
- title,
63
- description,
88
+ logo: sidebarLogo,
89
+ title: sidebarTitle,
90
+ items: sidebarItems,
64
91
  currentPage = "dashboard",
65
- user,
66
- showBreadcrumbs = false,
67
- breadcrumbs = [],
68
- sidebarLogo = "https://images.hotelinking.com/login/logo.png",
69
- sidebarTitle = "Admin",
70
- sidebarItems = [],
71
- topbarActions = [],
92
+ openByDefault: sidebarOpenByDefault = true,
93
+ } = sidebar;
94
+
95
+ // Extract topbar props
96
+ const {
97
+ actions: topbarActions = [],
72
98
  selectItems = [],
73
99
  selectedItem = { name: "", id: "" },
74
- sidebarOpenByDefault = true,
75
- } = Astro.props;
76
-
77
- // Format user data for display
78
- const userData = user
79
- ? {
80
- username: user.username || "Admin",
81
- email: user.attributes?.email || user.email || "admin@example.com",
82
- avatar: `https://ui-avatars.com/api/?name=${encodeURIComponent(user.username || "Admin")}&background=3B82F6&color=fff`,
83
- }
84
- : {
85
- username: "Admin",
86
- email: "admin@example.com",
87
- avatar:
88
- "https://ui-avatars.com/api/?name=Admin&background=3B82F6&color=fff",
89
- };
100
+ } = topbar || {};
101
+
102
+ // Initialize user nanostore (server-side)
103
+ if (user) {
104
+ const userData = {
105
+ username: user.username || "Admin",
106
+ email: user.attributes?.email || user.email || "admin@example.com",
107
+ avatar: `https://ui-avatars.com/api/?name=${encodeURIComponent(user.username || "Admin")}&background=3B82F6&color=fff`,
108
+ isAdmin: user.isAdmin,
109
+ attributes: user.attributes,
110
+ };
111
+ setUser(userData);
112
+ } else {
113
+ setUser({
114
+ username: "Admin",
115
+ email: "admin@example.com",
116
+ avatar:
117
+ "https://ui-avatars.com/api/?name=Admin&background=3B82F6&color=fff",
118
+ });
119
+ }
90
120
 
91
121
  // Build products sidebar based on user admin status
92
122
  const baseProducts = [
@@ -130,55 +160,36 @@ import { ClientRouter } from "astro:transitions";
130
160
  </head>
131
161
  <body>
132
162
  <div id="app">
133
- <!-- TODO: Implement uiWrapper integration -->
134
- <!-- This will be implemented in task 7.1 -->
163
+ <!-- Admin Wrapper with uiWrapper, sidebar, and topbar -->
164
+ <!-- User data comes from nanostore (no props needed!) -->
165
+ <AdminWrapper
166
+ client:load
167
+ sidebarLogo={sidebarLogo}
168
+ currentPage={currentPage}
169
+ sidebarTitle={sidebarTitle}
170
+ sidebarItems={sidebarItems}
171
+ topbarActions={topbarActions}
172
+ selectItems={selectItems}
173
+ selectedItem={selectedItem}
174
+ productsSidebar={productsSidebar}
175
+ sidebarOpenByDefault={sidebarOpenByDefault}
176
+ >
177
+ <!-- View Header with breadcrumbs, title, and optional button -->
178
+ <UiViewHeader
179
+ client:load
180
+ pages={breadcrumbs}
181
+ title={title}
182
+ subtitle={subtitle}
183
+ description={description}
184
+ buttonText={button?.text}
185
+ buttonColor={button?.color || "primary"}
186
+ buttonSize={button?.size || "medium"}
187
+ buttonLoading={button?.loading || false}
188
+ buttonDisabled={button?.disabled || false}
189
+ />
135
190
 
136
- <!-- Breadcrumbs Section (if enabled) -->
137
- {
138
- showBreadcrumbs && breadcrumbs.length > 0 && (
139
- <div class="breadcrumbs-section mb-6 px-6 pt-6">
140
- <nav class="flex" aria-label="Breadcrumb">
141
- {breadcrumbs.map((crumb, index) => (
142
- <>
143
- {crumb.routeName ? (
144
- <a
145
- href={crumb.routeName}
146
- class="text-gray-500 hover:text-gray-700"
147
- >
148
- {crumb.label}
149
- </a>
150
- ) : (
151
- <span class="text-gray-900">{crumb.label}</span>
152
- )}
153
- {index < breadcrumbs.length - 1 && (
154
- <span class="mx-2 text-gray-400">/</span>
155
- )}
156
- </>
157
- ))}
158
- </nav>
159
- </div>
160
- )
161
- }
162
-
163
- <!-- Page Header -->
164
- <div class="px-6 mb-6">
165
- <div class="flex items-center justify-between">
166
- <div>
167
- <h1 class="text-2xl font-bold leading-7 sm:text-4xl sm:truncate">
168
- {title}
169
- </h1>
170
- {description && <p class="text-gray-600 mt-2">{description}</p>}
171
- </div>
172
- <div class="flex items-center gap-3">
173
- <slot name="actions" />
174
- </div>
175
- </div>
176
- </div>
177
-
178
- <!-- Page Content -->
179
- <div class="px-6">
180
191
  <slot />
181
- </div>
192
+ </AdminWrapper>
182
193
  </div>
183
194
  </body>
184
195
  </html>
@@ -0,0 +1,87 @@
1
+ # Layouts Module
2
+
3
+ Pre-built page layouts for common page types.
4
+
5
+ ## Installation
6
+
7
+ ```typescript
8
+ import { AdminLayout } from '@htlkg/astro/layouts';
9
+ import { BrandLayout } from '@htlkg/astro/layouts';
10
+ import { AuthLayout } from '@htlkg/astro/layouts';
11
+ import { PublicLayout } from '@htlkg/astro/layouts';
12
+ ```
13
+
14
+ ## AdminLayout
15
+
16
+ Admin portal pages with sidebar navigation.
17
+
18
+ ```astro
19
+ ---
20
+ import { AdminLayout } from '@htlkg/astro/layouts';
21
+
22
+ const user = Astro.locals.user;
23
+ ---
24
+
25
+ <AdminLayout user={user} title="Dashboard">
26
+ <DashboardContent />
27
+ </AdminLayout>
28
+ ```
29
+
30
+ ## BrandLayout
31
+
32
+ Brand-specific pages with brand context.
33
+
34
+ ```astro
35
+ ---
36
+ import { BrandLayout } from '@htlkg/astro/layouts';
37
+
38
+ const { brandId } = Astro.params;
39
+ const brand = await getBrand(brandId);
40
+ ---
41
+
42
+ <BrandLayout brand={brand} title="Brand Settings">
43
+ <BrandSettings />
44
+ </BrandLayout>
45
+ ```
46
+
47
+ ## AuthLayout
48
+
49
+ Authentication pages (login, signup).
50
+
51
+ ```astro
52
+ ---
53
+ import { AuthLayout } from '@htlkg/astro/layouts';
54
+ ---
55
+
56
+ <AuthLayout title="Login">
57
+ <LoginForm />
58
+ </AuthLayout>
59
+ ```
60
+
61
+ ## PublicLayout
62
+
63
+ Public-facing pages without authentication.
64
+
65
+ ```astro
66
+ ---
67
+ import { PublicLayout } from '@htlkg/astro/layouts';
68
+ ---
69
+
70
+ <PublicLayout title="Welcome">
71
+ <LandingPage />
72
+ </PublicLayout>
73
+ ```
74
+
75
+ ## DefaultLayout
76
+
77
+ Base layout with minimal styling.
78
+
79
+ ```astro
80
+ ---
81
+ import { DefaultLayout } from '@htlkg/astro/layouts';
82
+ ---
83
+
84
+ <DefaultLayout title="Page Title">
85
+ <slot />
86
+ </DefaultLayout>
87
+ ```
@@ -2,13 +2,52 @@
2
2
  * Authentication middleware for htlkg integration
3
3
  *
4
4
  * This middleware:
5
+ * - Configures AWS Amplify for server-side auth
5
6
  * - Retrieves the authenticated user from AWS Amplify
6
7
  * - Injects the user into Astro.locals for use in pages and API routes
7
8
  * - Handles authentication errors gracefully
8
9
  */
9
10
 
10
11
  import type { MiddlewareHandler } from 'astro';
12
+ import { Amplify } from 'aws-amplify';
11
13
  import { getUser } from '@htlkg/core/auth';
14
+ import { globalSettings } from '@htlkg/core/amplify-astro-adapter';
15
+ import { amplifyConfig } from 'virtual:htlkg-config';
16
+
17
+ // Track if Amplify has been configured
18
+ let amplifyConfigured = false;
19
+
20
+ /**
21
+ * Configure Amplify on first request
22
+ */
23
+ function ensureAmplifyConfigured(): void {
24
+ if (amplifyConfigured) return;
25
+
26
+ try {
27
+ if (!amplifyConfig) {
28
+ console.warn('[htlkg Auth] No Amplify configuration provided');
29
+ return;
30
+ }
31
+
32
+ // Configure Amplify with SSR support
33
+ Amplify.configure(amplifyConfig as any, { ssr: true });
34
+
35
+ // Enable server-side auth in the adapter
36
+ globalSettings.enableServerSideAuth();
37
+
38
+ // Check if we're on HTTPS (for secure cookies)
39
+ const isSSL = typeof window !== 'undefined'
40
+ ? window.location.protocol === 'https:'
41
+ : process.env.NODE_ENV === 'production';
42
+ globalSettings.setIsSSLOrigin(isSSL);
43
+
44
+ amplifyConfigured = true;
45
+ console.info('[htlkg Auth] Amplify configured for server-side auth');
46
+ } catch (error) {
47
+ const errorMsg = error instanceof Error ? error.message : 'Unknown error';
48
+ console.error(`[htlkg Auth] Failed to configure Amplify: ${errorMsg}`);
49
+ }
50
+ }
12
51
 
13
52
  /**
14
53
  * Auth middleware - retrieves authenticated user and injects into locals
@@ -27,6 +66,9 @@ import { getUser } from '@htlkg/core/auth';
27
66
  export const authMiddleware: MiddlewareHandler = async (context, next) => {
28
67
  const { locals } = context;
29
68
 
69
+ // Ensure Amplify is configured before attempting auth
70
+ ensureAmplifyConfigured();
71
+
30
72
  try {
31
73
  const user = await getUser(context);
32
74
  locals.user = user;