astro-tractstack 2.0.30 → 2.0.32

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.
@@ -1,258 +0,0 @@
1
- ---
2
- import Layout from '@/layouts/Layout.astro';
3
- import { getBrandConfig } from '@/utils/api/brandConfig';
4
- import { preHealthCheck } from '@/utils/backend';
5
-
6
- const tenantId = 'default'; // For registration, always use default
7
-
8
- const healthCheckRedirect = await preHealthCheck(tenantId);
9
- if (healthCheckRedirect !== undefined) {
10
- return healthCheckRedirect;
11
- }
12
-
13
- const goBackend = import.meta.env.PUBLIC_GO_BACKEND || 'http://localhost:8080';
14
-
15
- // Check if multi-tenant is enabled
16
- const isMultiTenantEnabled =
17
- import.meta.env.PUBLIC_ENABLE_MULTI_TENANT === 'true';
18
- if (!isMultiTenantEnabled) {
19
- return Astro.redirect('/?error=multi-tenant-disabled');
20
- }
21
-
22
- // Dev mode detection
23
- const isDev = import.meta.env.DEV;
24
-
25
- // Get activation token from URL
26
- const url = new URL(Astro.request.url);
27
- const token = url.searchParams.get('token');
28
-
29
- let activationResult = {
30
- success: false,
31
- error: null as string | null,
32
- tenantId: null as string | null,
33
- };
34
-
35
- if (!token) {
36
- activationResult.error =
37
- 'Missing activation token. Please check your email link.';
38
- console.log('ERROR: No token provided');
39
- } else {
40
- // Attempt activation
41
- try {
42
- // Get tenantId BEFORE calling activation
43
- let tenantId: string;
44
-
45
- if (isDev) {
46
- // In dev mode, get tenant ID from URL param or use default
47
- tenantId = url.searchParams.get('tenantId') || 'localhost';
48
- } else {
49
- // Extract tenant ID from current domain in production
50
- const hostname = Astro.request.headers.get('host') || '';
51
- const parts = hostname.split('.');
52
- if (parts.length >= 4 && parts[1] === 'sandbox') {
53
- tenantId = parts[0];
54
- } else {
55
- throw new Error('Could not determine tenant ID from domain');
56
- }
57
- }
58
-
59
- // Make direct fetch call to Go backend (like other .astro files)
60
- const response = await fetch(`${goBackend}/api/v1/tenant/activation`, {
61
- method: 'POST',
62
- headers: {
63
- 'Content-Type': 'application/json',
64
- 'X-Tenant-ID': tenantId,
65
- },
66
- body: JSON.stringify({ token }),
67
- });
68
-
69
- const responseText = await response.text();
70
-
71
- if (response.ok) {
72
- activationResult.success = true;
73
- activationResult.tenantId = tenantId;
74
- } else {
75
- let errorData;
76
- try {
77
- errorData = JSON.parse(responseText);
78
- } catch (e) {
79
- errorData = { error: responseText };
80
- }
81
- activationResult.error = errorData.error || 'Activation failed';
82
- console.log('FAILED: Activation error:', activationResult.error);
83
- }
84
- } catch (error) {
85
- activationResult.error =
86
- error instanceof Error ? error.message : 'Activation failed';
87
- console.log('EXCEPTION:', error);
88
- }
89
- }
90
-
91
- const brandConfig = await getBrandConfig('default');
92
- const title = activationResult.success
93
- ? 'Tenant Activated | TractStack'
94
- : 'Activation Error | TractStack';
95
-
96
- // Build dashboard URL based on environment
97
- const getDashboardUrl = (tenantId: string) => {
98
- if (isDev) {
99
- return '/storykeep';
100
- }
101
- return `https://${tenantId}.sandbox.freewebpress.com/storykeep`;
102
- };
103
-
104
- // Build display URL based on environment
105
- const getDisplayUrl = (tenantId: string) => {
106
- if (isDev) {
107
- return 'localhost:4321';
108
- }
109
- return `https://${tenantId}.sandbox.freewebpress.com`;
110
- };
111
- ---
112
-
113
- <Layout title={title} slug="tenant-activate" brandConfig={brandConfig}>
114
- <main
115
- id="main-content"
116
- class="flex min-h-screen items-center justify-center bg-gray-50"
117
- >
118
- <div class="mx-auto max-w-2xl p-6">
119
- {
120
- activationResult.success ? (
121
- <div class="rounded-lg bg-white p-8 text-center shadow-lg">
122
- <div class="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-green-100">
123
- <svg
124
- class="h-8 w-8 text-green-600"
125
- fill="none"
126
- stroke="currentColor"
127
- viewBox="0 0 24 24"
128
- >
129
- <path
130
- stroke-linecap="round"
131
- stroke-linejoin="round"
132
- stroke-width="2"
133
- d="M5 13l4 4L19 7"
134
- />
135
- </svg>
136
- </div>
137
-
138
- <h2 class="mb-4 text-2xl font-bold text-gray-900">
139
- 🎉 Tenant Activated Successfully!
140
- </h2>
141
-
142
- <p class="mb-6 text-gray-600">
143
- Your TractStack tenant has been activated and is ready to use.
144
- </p>
145
-
146
- {activationResult.tenantId && (
147
- <div class="mb-6 rounded-lg border border-orange-200 bg-orange-50 p-4">
148
- <p class="mb-2 text-sm font-bold text-orange-800">
149
- Your tenant URL:
150
- </p>
151
- <p class="break-all font-mono text-orange-700">
152
- {getDisplayUrl(activationResult.tenantId)}
153
- </p>
154
- </div>
155
- )}
156
-
157
- <div class="space-y-4">
158
- {activationResult.tenantId ? (
159
- <a
160
- href={getDashboardUrl(activationResult.tenantId)}
161
- class="inline-block rounded-lg bg-orange-600 px-6 py-3 font-bold text-white transition-colors hover:bg-orange-700"
162
- id="dashboard-button"
163
- >
164
- Access Your Dashboard
165
- </a>
166
- ) : (
167
- <div class="text-gray-500">
168
- <p class="mb-2">Unable to determine tenant URL</p>
169
- <p class="text-sm">Please contact support</p>
170
- </div>
171
- )}
172
- </div>
173
- </div>
174
- ) : (
175
- <div class="rounded-lg bg-white p-8 text-center shadow-lg">
176
- <div class="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-red-100">
177
- <svg
178
- class="h-8 w-8 text-red-600"
179
- fill="none"
180
- stroke="currentColor"
181
- viewBox="0 0 24 24"
182
- >
183
- <path
184
- stroke-linecap="round"
185
- stroke-linejoin="round"
186
- stroke-width="2"
187
- d="M6 18L18 6M6 6l12 12"
188
- />
189
- </svg>
190
- </div>
191
-
192
- <h2 class="mb-4 text-2xl font-bold text-gray-900">
193
- Activation Failed
194
- </h2>
195
-
196
- <p class="mb-6 text-gray-600">{activationResult.error}</p>
197
-
198
- <div class="mb-6 rounded-lg border border-yellow-200 bg-yellow-50 p-4">
199
- <h3 class="mb-2 text-sm font-bold text-yellow-800">
200
- Common Issues:
201
- </h3>
202
- <ul class="space-y-1 text-left text-sm text-yellow-700">
203
- <li>• The activation link may have expired</li>
204
- <li>• The token may have already been used</li>
205
- <li>• There may be a temporary server issue</li>
206
- </ul>
207
- </div>
208
-
209
- <div class="space-y-4">
210
- <a
211
- href="/sandbox/register"
212
- class="inline-block rounded-lg bg-orange-600 px-6 py-3 font-bold text-white transition-colors hover:bg-orange-700"
213
- >
214
- Register New Tenant
215
- </a>
216
-
217
- <div class="text-center">
218
- <a
219
- href="mailto:support@tractstack.com"
220
- class="text-sm text-orange-600 underline hover:text-orange-800"
221
- >
222
- Contact Support
223
- </a>
224
- </div>
225
- </div>
226
- </div>
227
- )
228
- }
229
- </div>
230
- </main>
231
- </Layout>
232
-
233
- <script>
234
- // Auto-redirect to dashboard after successful activation with countdown
235
- const dashboardButton = document.getElementById('dashboard-button');
236
- if (dashboardButton) {
237
- let countdown = 5;
238
-
239
- // Update button text with countdown
240
- const updateButton = () => {
241
- dashboardButton.textContent = `Access Your Dashboard (${countdown}s)`;
242
- countdown--;
243
-
244
- if (countdown < 0) {
245
- dashboardButton.click();
246
- }
247
- };
248
-
249
- // Start countdown immediately
250
- updateButton();
251
- const interval = setInterval(updateButton, 1000);
252
-
253
- // Clear interval if user clicks button manually
254
- dashboardButton.addEventListener('click', () => {
255
- clearInterval(interval);
256
- });
257
- }
258
- </script>
@@ -1,44 +0,0 @@
1
- ---
2
- import Layout from '@/layouts/Layout.astro';
3
- import RegistrationForm from '@/components/tenant/RegistrationForm';
4
- import { getBrandConfig } from '@/utils/api/brandConfig';
5
- import { preHealthCheck } from '@/utils/backend';
6
-
7
- const tenantId = 'default'; // For registration, always use default
8
-
9
- const healthCheckRedirect = await preHealthCheck(tenantId);
10
- if (healthCheckRedirect !== undefined) {
11
- return healthCheckRedirect;
12
- }
13
- // Check if multi-tenant is enabled
14
- const isMultiTenantEnabled =
15
- import.meta.env.PUBLIC_ENABLE_MULTI_TENANT === 'true';
16
- if (!isMultiTenantEnabled) {
17
- return Astro.redirect('/storykeep?error=multi-tenant-disabled');
18
- }
19
-
20
- const brandConfig = await getBrandConfig('default');
21
- const title = 'Register New Tenant | TractStack';
22
- ---
23
-
24
- <Layout title={title} slug="sandbox-register" brandConfig={brandConfig}>
25
- <main id="main-content" class="min-h-screen bg-gray-50">
26
- <div class="py-12">
27
- <RegistrationForm
28
- client:load
29
- onSuccess={(tenantId) => {
30
- if (tenantId)
31
- window.location.href = `/sandbox/success?tenant=${encodeURIComponent(tenantId)}`;
32
- }}
33
- onCapacityFull={() => {
34
- window.location.href = '/sandbox/capacity-full';
35
- }}
36
- />
37
- </div>
38
- </main>
39
- </Layout>
40
-
41
- <script>
42
- // Add any additional client-side logic here if needed
43
- console.log('Tenant registration page loaded');
44
- </script>
@@ -1,179 +0,0 @@
1
- ---
2
- import Layout from '@/layouts/Layout.astro';
3
- import { getBrandConfig } from '@/utils/api/brandConfig';
4
- import { preHealthCheck } from '@/utils/backend';
5
-
6
- // Get tenant ID from URL params
7
- const url = new URL(Astro.request.url);
8
- const tenantId = url.searchParams.get('tenant');
9
-
10
- const healthCheckRedirect = await preHealthCheck(tenantId || `default`);
11
- if (healthCheckRedirect !== undefined) {
12
- return healthCheckRedirect;
13
- }
14
-
15
- // Check if multi-tenant is enabled
16
- const isMultiTenantEnabled =
17
- import.meta.env.PUBLIC_ENABLE_MULTI_TENANT === 'true';
18
- if (!isMultiTenantEnabled) {
19
- return Astro.redirect('/?error=multi-tenant-disabled');
20
- }
21
-
22
- if (!tenantId) {
23
- return Astro.redirect('/sandbox/register?error=missing-tenant-id');
24
- }
25
-
26
- // Get backend URL
27
- const brandConfig = await getBrandConfig('default');
28
- const title = 'Registration Successful | TractStack';
29
- ---
30
-
31
- <Layout title={title} slug="tenant-success" brandConfig={brandConfig}>
32
- <main
33
- id="main-content"
34
- class="flex min-h-screen items-center justify-center bg-gray-50"
35
- >
36
- <div class="mx-auto max-w-2xl p-6">
37
- <div class="rounded-lg bg-white p-8 text-center shadow-lg">
38
- <div
39
- class="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-orange-100"
40
- >
41
- <svg
42
- class="h-8 w-8 text-orange-600"
43
- fill="none"
44
- stroke="currentColor"
45
- viewBox="0 0 24 24"
46
- >
47
- <path
48
- stroke-linecap="round"
49
- stroke-linejoin="round"
50
- stroke-width="2"
51
- d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
52
- ></path>
53
- </svg>
54
- </div>
55
-
56
- <h2 class="mb-4 text-2xl font-bold text-gray-900">
57
- 📧 Check Your Email
58
- </h2>
59
-
60
- <p class="mb-6 text-gray-600">
61
- Your tenant <strong class="font-mono text-orange-600"
62
- >{tenantId}</strong
63
- > has been created successfully!
64
- </p>
65
-
66
- <div class="mb-6 rounded-lg border border-orange-200 bg-orange-50 p-6">
67
- <h3 class="mb-3 text-lg font-bold text-orange-800">
68
- What happens next:
69
- </h3>
70
- <div class="space-y-3 text-left">
71
- <div class="flex items-start space-x-3">
72
- <div
73
- class="mt-0.5 flex h-6 w-6 items-center justify-center rounded-full bg-orange-600 text-sm font-bold text-white"
74
- >
75
- 1
76
- </div>
77
- <div>
78
- <p class="font-bold text-orange-800">Check your email inbox</p>
79
- <p class="text-sm text-orange-600">
80
- We've sent an activation link to complete setup
81
- </p>
82
- </div>
83
- </div>
84
-
85
- <div class="flex items-start space-x-3">
86
- <div
87
- class="mt-0.5 flex h-6 w-6 items-center justify-center rounded-full bg-orange-600 text-sm font-bold text-white"
88
- >
89
- 2
90
- </div>
91
- <div>
92
- <p class="font-bold text-orange-800">
93
- Click the activation link
94
- </p>
95
- <p class="text-sm text-orange-600">
96
- This will finalize your tenant setup
97
- </p>
98
- </div>
99
- </div>
100
-
101
- <div class="flex items-start space-x-3">
102
- <div
103
- class="mt-0.5 flex h-6 w-6 items-center justify-center rounded-full bg-orange-600 text-sm font-bold text-white"
104
- >
105
- 3
106
- </div>
107
- <div>
108
- <p class="font-bold text-orange-800">Access your dashboard</p>
109
- <p class="text-sm text-orange-600">
110
- Visit <strong class="font-mono"
111
- >{tenantId}.sandbox.freewebpress.com</strong
112
- >
113
- </p>
114
- </div>
115
- </div>
116
- </div>
117
- </div>
118
-
119
- <div class="mb-6 rounded-lg border border-yellow-200 bg-yellow-50 p-4">
120
- <p class="text-sm text-yellow-800">
121
- <strong>⏰ Important:</strong> The activation link will expire in 48
122
- hours. If you don't see the email, check your spam folder.
123
- </p>
124
- </div>
125
-
126
- <div class="space-y-4">
127
- <div class="text-center">
128
- <p class="mb-4 text-sm text-gray-500">
129
- Once activated, your tenant URL will be:
130
- </p>
131
- <p
132
- class="break-all rounded border bg-gray-100 p-3 font-mono text-lg"
133
- >
134
- https://{tenantId}.sandbox.freewebpress.com
135
- </p>
136
- </div>
137
-
138
- <div class="flex flex-col justify-center gap-4 sm:flex-row">
139
- <a
140
- href="/sandbox/register"
141
- class="rounded-lg border border-gray-300 px-6 py-2 text-gray-700 transition-colors hover:bg-gray-50"
142
- >
143
- Register Another Tenant
144
- </a>
145
-
146
- <a
147
- href="https://docs.tractstack.com"
148
- class="rounded-lg bg-orange-600 px-6 py-2 text-white transition-colors hover:bg-orange-700"
149
- target="_blank"
150
- rel="noopener noreferrer"
151
- >
152
- View Documentation
153
- </a>
154
- </div>
155
-
156
- <div class="border-t pt-4 text-center">
157
- <p class="text-sm text-gray-500">
158
- Need help?
159
- <a
160
- href="mailto:support@tractstack.com"
161
- class="text-orange-600 underline hover:text-orange-800"
162
- >
163
- Contact Support
164
- </a>
165
- </p>
166
- </div>
167
- </div>
168
- </div>
169
- </div>
170
- </main>
171
- </Layout>
172
-
173
- <script>
174
- // Add email resend functionality (placeholder for future implementation)
175
- console.log(
176
- 'Registration success page loaded for tenant:',
177
- document.querySelector('[data-tenant-id]')?.textContent
178
- );
179
- </script>
@@ -1,97 +0,0 @@
1
- // Tenant configuration API utilities following TractStack v2 patterns
2
- import { TractStackAPI } from '../api';
3
- import type {
4
- TenantProvisioningData,
5
- TenantCapacity,
6
- TenantProvisioningResponse,
7
- } from '@/types/multiTenant';
8
- import type { TenantActivationRequest } from '@/types/multiTenant';
9
-
10
- /**
11
- * Check tenant capacity and existing tenants
12
- */
13
- export async function checkTenantCapacity(
14
- tenantId: string
15
- ): Promise<TenantCapacity> {
16
- const api = new TractStackAPI(tenantId);
17
- try {
18
- const response = await api.get<TenantCapacity>('/api/v1/tenant/capacity');
19
-
20
- if (!response.success || !response.data) {
21
- throw new Error(response.error || 'No data received from server');
22
- }
23
-
24
- const data = response.data;
25
-
26
- // Validate response structure to match backend
27
- if (
28
- typeof data.available !== 'boolean' ||
29
- typeof data.currentTenants !== 'number' ||
30
- typeof data.maxTenants !== 'number' ||
31
- typeof data.availableSlots !== 'number'
32
- ) {
33
- throw new Error('Invalid response format from server');
34
- }
35
-
36
- return data;
37
- } catch (error) {
38
- console.error('Failed to fetch tenant capacity:', error);
39
- throw new Error('Failed to load tenant capacity information');
40
- }
41
- }
42
-
43
- /**
44
- * Provision a new tenant with reserved status
45
- */
46
- export async function provisionTenant(
47
- tenantId: string,
48
- data: TenantProvisioningData
49
- ): Promise<TenantProvisioningResponse> {
50
- const api = new TractStackAPI(tenantId);
51
- try {
52
- const response = await api.post<TenantProvisioningResponse>(
53
- '/api/v1/tenant/provision',
54
- data
55
- );
56
-
57
- if (!response.success || !response.data) {
58
- throw new Error(response.error || 'Failed to provision tenant');
59
- }
60
-
61
- return response.data;
62
- } catch (error) {
63
- console.error('Failed to provision tenant:', error);
64
-
65
- if (error instanceof Error) {
66
- throw error;
67
- }
68
-
69
- throw new Error('Failed to provision tenant');
70
- }
71
- }
72
-
73
- /**
74
- * Activate a tenant using the activation token
75
- */
76
- export async function activateTenant(
77
- tenantId: string,
78
- token: string
79
- ): Promise<void> {
80
- const api = new TractStackAPI(tenantId);
81
- try {
82
- const request: TenantActivationRequest = { token };
83
- const response = await api.post('/api/v1/activate-tenant', request);
84
-
85
- if (!response.success) {
86
- throw new Error(response.error || 'Failed to activate tenant');
87
- }
88
- } catch (error) {
89
- console.error('Failed to activate tenant:', error);
90
-
91
- if (error instanceof Error) {
92
- throw error;
93
- }
94
-
95
- throw new Error('Failed to activate tenant');
96
- }
97
- }