@promptbook/cli 0.112.0-96 → 0.112.0-98
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/apps/agents-server/playwright.config.ts +2 -1
- package/apps/agents-server/src/app/admin/cli-access/CliAccessClient.tsx +99 -0
- package/apps/agents-server/src/app/admin/cli-access/page.tsx +14 -0
- package/apps/agents-server/src/app/admin/code-runners/CodeRunnersClient.tsx +124 -34
- package/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +46 -505
- package/apps/agents-server/src/app/admin/servers/ServersClient.tsx +23 -11
- package/apps/agents-server/src/app/admin/servers/ServersRegistryApi.ts +5 -0
- package/apps/agents-server/src/app/admin/servers/ServersRegistryDnsTypes.ts +87 -0
- package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +258 -128
- package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +46 -334
- package/apps/agents-server/src/app/admin/servers/useServersRegistryState.ts +26 -2
- package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +435 -0
- package/apps/agents-server/src/app/admin/update/page.tsx +14 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +24 -0
- package/apps/agents-server/src/app/api/admin/cli-access/route.ts +137 -0
- package/apps/agents-server/src/app/api/admin/code-runners/authentication/route.ts +140 -0
- package/apps/agents-server/src/app/api/admin/code-runners/route.ts +4 -35
- package/apps/agents-server/src/app/api/admin/servers/[serverId]/route.ts +7 -2
- package/apps/agents-server/src/app/api/admin/servers/route.ts +95 -4
- package/apps/agents-server/src/app/api/admin/update/route.ts +52 -0
- package/apps/agents-server/src/app/api/auth/login/route.ts +8 -0
- package/apps/agents-server/src/app/api/auth/logout/route.ts +10 -2
- package/apps/agents-server/src/app/api/chat/export/pdf/route.ts +63 -0
- package/apps/agents-server/src/app/page.tsx +10 -0
- package/apps/agents-server/src/components/AdminTerminal/AdminTerminalCard.tsx +279 -0
- package/apps/agents-server/src/components/AdminTerminal/useAdminTerminalSession.ts +336 -0
- package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +10 -0
- package/apps/agents-server/src/languages/ServerTranslationKeys.ts +2 -0
- package/apps/agents-server/src/languages/translations/czech.yaml +2 -0
- package/apps/agents-server/src/languages/translations/english.yaml +2 -0
- package/apps/agents-server/src/middleware.ts +32 -0
- package/apps/agents-server/src/tools/BrowserConnectionProvider.ts +1 -1
- package/apps/agents-server/src/utils/chatExport/downloadChatPdfFromServer.ts +59 -0
- package/apps/agents-server/src/utils/chatExport/renderHtmlToPdfOnServer.ts +37 -0
- package/apps/agents-server/src/utils/codeRunnerAuthentication.ts +234 -0
- package/apps/agents-server/src/utils/codeRunnerConfiguration.ts +67 -0
- package/apps/agents-server/src/utils/createInteractiveTerminalEventStream.ts +84 -0
- package/apps/agents-server/src/utils/interactiveTerminalSession.ts +442 -0
- package/apps/agents-server/src/utils/serverCliAccess.ts +221 -0
- package/apps/agents-server/src/utils/serverManagement/standaloneVpsServerMetadata.ts +145 -0
- package/apps/agents-server/src/utils/serverRegistry.ts +3 -2
- package/apps/agents-server/src/utils/session.ts +37 -9
- package/apps/agents-server/src/utils/shibboleth/createShibbolethAuthenticationLogPayload.ts +173 -0
- package/apps/agents-server/src/utils/shibboleth/writeShibbolethAuthenticationLog.ts +27 -0
- package/apps/agents-server/src/utils/standaloneVpsDnsDiagnostics.ts +258 -0
- package/apps/agents-server/src/utils/standaloneVpsRawIpBootstrap.ts +87 -0
- package/apps/agents-server/src/utils/vpsConfiguration.ts +87 -13
- package/apps/agents-server/src/utils/vpsSelfUpdate.ts +664 -0
- package/esm/apps/agents-server/src/utils/serverRegistry.d.ts +1 -1
- package/esm/index.es.js +7 -5
- package/esm/index.es.js.map +1 -1
- package/esm/src/book-components/Chat/Chat/ChatActionsBar.d.ts +2 -0
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +6 -0
- package/esm/src/book-components/Chat/save/_common/ChatSaveFormatHandler.d.ts +35 -0
- package/esm/src/book-components/Chat/save/_common/createChatExportFilename.d.ts +11 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/book-components/Chat/Chat/Chat.tsx +2 -0
- package/src/book-components/Chat/Chat/ChatActionsBar.tsx +17 -9
- package/src/book-components/Chat/Chat/ChatProps.tsx +7 -0
- package/src/book-components/Chat/save/_common/ChatSaveFormatHandler.ts +40 -0
- package/src/book-components/Chat/save/_common/createChatExportFilename.ts +20 -0
- package/src/book-components/Chat/utils/renderMarkdown.ts +1 -3
- package/src/other/templates/getTemplatesPipelineCollection.ts +718 -790
- package/src/scrapers/document/DocumentScraper.ts +1 -1
- package/src/scrapers/document-legacy/LegacyDocumentScraper.ts +1 -1
- package/src/version.ts +2 -2
- package/src/versions.txt +2 -0
- package/umd/apps/agents-server/src/utils/serverRegistry.d.ts +1 -1
- package/umd/index.umd.js +7 -5
- package/umd/index.umd.js.map +1 -1
- package/umd/src/book-components/Chat/Chat/ChatActionsBar.d.ts +2 -0
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +6 -0
- package/umd/src/book-components/Chat/save/_common/ChatSaveFormatHandler.d.ts +35 -0
- package/umd/src/book-components/Chat/save/_common/createChatExportFilename.d.ts +11 -0
- package/umd/src/version.d.ts +1 -1
- package/src/conversion/validation/_importPipeline.ts +0 -88
- /package/esm/src/conversion/validation/{_importPipeline.d.ts → _importPipeline.test.d.ts} +0 -0
- /package/umd/src/conversion/validation/{_importPipeline.d.ts → _importPipeline.test.d.ts} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { Plus } from 'lucide-react';
|
|
4
4
|
import { useSearchParams } from 'next/navigation';
|
|
5
|
-
import { useCallback, useEffect } from 'react';
|
|
5
|
+
import { useCallback, useEffect, useMemo } from 'react';
|
|
6
6
|
import { Card } from '../../../components/Homepage/Card';
|
|
7
7
|
import { Section } from '../../../components/Homepage/Section';
|
|
8
8
|
import { useUnsavedChangesGuard } from '../../../components/utils/useUnsavedChangesGuard';
|
|
@@ -35,6 +35,7 @@ export function ServersClient() {
|
|
|
35
35
|
deletingServerId,
|
|
36
36
|
error,
|
|
37
37
|
hasDirtyServerDrafts,
|
|
38
|
+
isStandaloneVps,
|
|
38
39
|
isServerDraftDirty,
|
|
39
40
|
loading,
|
|
40
41
|
migrateServer,
|
|
@@ -52,6 +53,10 @@ export function ServersClient() {
|
|
|
52
53
|
onServerCreated: reloadServers,
|
|
53
54
|
});
|
|
54
55
|
const hasUnsavedChanges = hasDirtyServerDrafts || (createServerWizard.isDialogOpen && createServerWizard.isDirty);
|
|
56
|
+
const serversWithDnsIssues = useMemo(
|
|
57
|
+
() => servers.filter((server) => server.dnsDiagnostic && server.dnsDiagnostic.status !== 'verified'),
|
|
58
|
+
[servers],
|
|
59
|
+
);
|
|
55
60
|
const { confirmBeforeNavigation, allowNextNavigation } = useUnsavedChangesGuard({
|
|
56
61
|
hasUnsavedChanges,
|
|
57
62
|
preventInAppNavigation: true,
|
|
@@ -117,11 +122,28 @@ export function ServersClient() {
|
|
|
117
122
|
</Card>
|
|
118
123
|
) : null}
|
|
119
124
|
|
|
125
|
+
{isStandaloneVps && serversWithDnsIssues.length > 0 ? (
|
|
126
|
+
<Card className="border-amber-200 bg-amber-50 hover:border-amber-200 hover:shadow-md">
|
|
127
|
+
<div className="space-y-2 text-sm text-amber-900">
|
|
128
|
+
<p className="font-semibold">
|
|
129
|
+
DNS setup is incomplete for {serversWithDnsIssues.length}{' '}
|
|
130
|
+
{serversWithDnsIssues.length === 1 ? 'domain' : 'domains'}.
|
|
131
|
+
</p>
|
|
132
|
+
<p>
|
|
133
|
+
Automatic nginx and SSL setup already ran, but these domains do not resolve to this VPS yet.
|
|
134
|
+
Review the DNS instructions in the affected server rows below, update the records at your DNS
|
|
135
|
+
provider, and refresh this page after propagation.
|
|
136
|
+
</p>
|
|
137
|
+
</div>
|
|
138
|
+
</Card>
|
|
139
|
+
) : null}
|
|
140
|
+
|
|
120
141
|
<Section title="Registered servers" gridClassName="grid gap-6">
|
|
121
142
|
<Card className="hover:border-gray-200 hover:shadow-md">
|
|
122
143
|
<ServersRegistryTable
|
|
123
144
|
currentServerId={currentServerId}
|
|
124
145
|
canEdit={canEdit}
|
|
146
|
+
isStandaloneVps={isStandaloneVps}
|
|
125
147
|
loading={loading}
|
|
126
148
|
migratingServerId={migratingServerId}
|
|
127
149
|
navigatingServerId={navigatingServerId}
|
|
@@ -139,27 +161,17 @@ export function ServersClient() {
|
|
|
139
161
|
|
|
140
162
|
{canEdit ? (
|
|
141
163
|
<CreateServerDialog
|
|
142
|
-
addAdditionalUser={createServerWizard.addAdditionalUser}
|
|
143
|
-
derivedWizardTablePrefix={createServerWizard.derivedWizardTablePrefix}
|
|
144
164
|
handleCreateServer={createServerWizard.handleCreateServer}
|
|
145
165
|
handleIconUpload={createServerWizard.handleIconUpload}
|
|
146
|
-
handleWizardBack={createServerWizard.handleWizardBack}
|
|
147
|
-
handleWizardNext={createServerWizard.handleWizardNext}
|
|
148
|
-
handleWizardStepSelection={createServerWizard.handleWizardStepSelection}
|
|
149
166
|
iconInputRef={createServerWizard.iconInputRef}
|
|
150
167
|
isCreatingServer={createServerWizard.isCreatingServer}
|
|
151
168
|
isOpen={createServerWizard.isDialogOpen}
|
|
152
169
|
isUploadingIcon={createServerWizard.isUploadingIcon}
|
|
153
|
-
removeAdditionalUser={createServerWizard.removeAdditionalUser}
|
|
154
170
|
requestClose={createServerWizard.requestClose}
|
|
155
171
|
resetWizard={createServerWizard.resetWizard}
|
|
156
|
-
updateAdditionalUser={createServerWizard.updateAdditionalUser}
|
|
157
|
-
updateAdminUser={createServerWizard.updateAdminUser}
|
|
158
|
-
updateInitialSetting={createServerWizard.updateInitialSetting}
|
|
159
172
|
updateWizardField={createServerWizard.updateWizardField}
|
|
160
173
|
wizardError={createServerWizard.wizardError}
|
|
161
174
|
wizardState={createServerWizard.wizardState}
|
|
162
|
-
wizardStep={createServerWizard.wizardStep}
|
|
163
175
|
/>
|
|
164
176
|
) : null}
|
|
165
177
|
|
|
@@ -22,6 +22,11 @@ type ManagedServersReadResponse = {
|
|
|
22
22
|
*/
|
|
23
23
|
readonly canEdit: boolean;
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Whether the registry is backed by standalone VPS `SERVERS` configuration.
|
|
27
|
+
*/
|
|
28
|
+
readonly isStandaloneVps?: boolean;
|
|
29
|
+
|
|
25
30
|
/**
|
|
26
31
|
* Optional failure message returned by the API.
|
|
27
32
|
*/
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported DNS verification states shown for standalone VPS domains.
|
|
3
|
+
*
|
|
4
|
+
* @private shared type for the `/admin/servers` registry flow
|
|
5
|
+
*/
|
|
6
|
+
export type ManagedServerDnsStatus = 'verified' | 'pending' | 'misconfigured' | 'unavailable';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* One DNS record instruction rendered in the admin UI.
|
|
10
|
+
*
|
|
11
|
+
* @private shared type for the `/admin/servers` registry flow
|
|
12
|
+
*/
|
|
13
|
+
export type ManagedServerDnsExpectedRecord = {
|
|
14
|
+
/**
|
|
15
|
+
* DNS record type.
|
|
16
|
+
*/
|
|
17
|
+
readonly type: 'A' | 'AAAA' | 'CNAME';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hostname/record name that should be configured.
|
|
21
|
+
*/
|
|
22
|
+
readonly name: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Expected record target value.
|
|
26
|
+
*/
|
|
27
|
+
readonly value: string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Optional note clarifying when to use the record.
|
|
31
|
+
*/
|
|
32
|
+
readonly note: string | null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* One external DNS-provider help link shown with the setup guidance.
|
|
37
|
+
*
|
|
38
|
+
* @private shared type for the `/admin/servers` registry flow
|
|
39
|
+
*/
|
|
40
|
+
export type ManagedServerDnsProviderGuide = {
|
|
41
|
+
/**
|
|
42
|
+
* Human-readable provider label.
|
|
43
|
+
*/
|
|
44
|
+
readonly label: string;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Official provider help URL.
|
|
48
|
+
*/
|
|
49
|
+
readonly href: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* DNS diagnostic payload returned for one standalone VPS domain.
|
|
54
|
+
*
|
|
55
|
+
* @private shared type for the `/admin/servers` registry flow
|
|
56
|
+
*/
|
|
57
|
+
export type ManagedServerDnsDiagnostic = {
|
|
58
|
+
/**
|
|
59
|
+
* Overall DNS verification state for the domain.
|
|
60
|
+
*/
|
|
61
|
+
readonly status: ManagedServerDnsStatus;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Short human-readable explanation of the current state.
|
|
65
|
+
*/
|
|
66
|
+
readonly summary: string;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Public IP address expected for direct DNS records.
|
|
70
|
+
*/
|
|
71
|
+
readonly publicIpAddress: string | null;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Addresses currently returned by DNS for the configured domain.
|
|
75
|
+
*/
|
|
76
|
+
readonly resolvedAddresses: ReadonlyArray<string>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* DNS records that the user can add at their provider.
|
|
80
|
+
*/
|
|
81
|
+
readonly expectedRecords: ReadonlyArray<ManagedServerDnsExpectedRecord>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Provider documentation links for updating DNS records.
|
|
85
|
+
*/
|
|
86
|
+
readonly providerGuides: ReadonlyArray<ManagedServerDnsProviderGuide>;
|
|
87
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { Fragment } from 'react';
|
|
3
4
|
import { ArrowRightLeft, Loader2, RefreshCcw, Save } from 'lucide-react';
|
|
4
5
|
import moment from 'moment';
|
|
5
6
|
import {
|
|
@@ -55,6 +56,11 @@ type ServersRegistryTableProps = {
|
|
|
55
56
|
*/
|
|
56
57
|
readonly loading: boolean;
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Whether rows are standalone VPS domains instead of database `_Server` records.
|
|
61
|
+
*/
|
|
62
|
+
readonly isStandaloneVps: boolean;
|
|
63
|
+
|
|
58
64
|
/**
|
|
59
65
|
* Server id currently running migrations.
|
|
60
66
|
*/
|
|
@@ -116,6 +122,7 @@ type ServersRegistryTableRowProps = {
|
|
|
116
122
|
readonly currentServerId: number | null;
|
|
117
123
|
readonly draft: ServerDraft | undefined;
|
|
118
124
|
readonly isDirty: boolean;
|
|
125
|
+
readonly isStandaloneVps: boolean;
|
|
119
126
|
readonly isMigrating: boolean;
|
|
120
127
|
readonly isNavigating: boolean;
|
|
121
128
|
readonly isSaving: boolean;
|
|
@@ -172,6 +179,7 @@ function ServersRegistryTableRow(props: ServersRegistryTableRowProps) {
|
|
|
172
179
|
canEdit,
|
|
173
180
|
draft,
|
|
174
181
|
isDirty,
|
|
182
|
+
isStandaloneVps,
|
|
175
183
|
isMigrating,
|
|
176
184
|
isNavigating,
|
|
177
185
|
isSaving,
|
|
@@ -182,109 +190,213 @@ function ServersRegistryTableRow(props: ServersRegistryTableRowProps) {
|
|
|
182
190
|
server,
|
|
183
191
|
} = props;
|
|
184
192
|
const isCurrent = server.id === currentServerId;
|
|
193
|
+
const dnsDiagnostic = server.dnsDiagnostic || null;
|
|
194
|
+
const hasDnsIssue = Boolean(dnsDiagnostic && dnsDiagnostic.status !== 'verified');
|
|
195
|
+
const columnCount = isStandaloneVps ? 6 : 8;
|
|
185
196
|
|
|
186
197
|
return (
|
|
187
|
-
|
|
188
|
-
<
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
{
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
<
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
198
|
+
<>
|
|
199
|
+
<tr className={isCurrent ? 'bg-blue-50/40' : 'hover:bg-gray-50'}>
|
|
200
|
+
<td className="px-4 py-3 align-top">
|
|
201
|
+
<input
|
|
202
|
+
type="text"
|
|
203
|
+
value={draft?.name || ''}
|
|
204
|
+
onChange={(event) => onUpdateServerDraft(server.id, 'name', event.target.value)}
|
|
205
|
+
className={INPUT_CLASS_NAME}
|
|
206
|
+
disabled={!canEdit}
|
|
207
|
+
aria-label={`Server name for ${server.name}`}
|
|
208
|
+
/>
|
|
209
|
+
</td>
|
|
210
|
+
{!isStandaloneVps ? (
|
|
211
|
+
<td className="px-4 py-3 align-top">
|
|
212
|
+
<select
|
|
213
|
+
value={draft?.environment || server.environment}
|
|
214
|
+
onChange={(event) =>
|
|
215
|
+
onUpdateServerDraft(
|
|
216
|
+
server.id,
|
|
217
|
+
'environment',
|
|
218
|
+
event.target.value as ManagedServerEnvironment,
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
className={INPUT_CLASS_NAME}
|
|
222
|
+
disabled={!canEdit}
|
|
223
|
+
aria-label={`Environment for ${server.name}`}
|
|
224
|
+
>
|
|
225
|
+
{MANAGED_SERVER_ENVIRONMENT_OPTIONS.map((environment) => (
|
|
226
|
+
<option key={environment} value={environment}>
|
|
227
|
+
{environment}
|
|
228
|
+
</option>
|
|
229
|
+
))}
|
|
230
|
+
</select>
|
|
231
|
+
</td>
|
|
232
|
+
) : null}
|
|
233
|
+
<td className="px-4 py-3 align-top">
|
|
234
|
+
<input
|
|
235
|
+
type="text"
|
|
236
|
+
value={draft?.domain || ''}
|
|
237
|
+
onChange={(event) => onUpdateServerDraft(server.id, 'domain', event.target.value)}
|
|
238
|
+
className={INPUT_CLASS_NAME}
|
|
239
|
+
disabled={!canEdit}
|
|
240
|
+
aria-label={`Domain for ${server.name}`}
|
|
241
|
+
/>
|
|
242
|
+
</td>
|
|
243
|
+
{!isStandaloneVps ? (
|
|
244
|
+
<td className="px-4 py-3 align-top">
|
|
245
|
+
<input
|
|
246
|
+
type="text"
|
|
247
|
+
value={draft?.tablePrefix || ''}
|
|
248
|
+
onChange={(event) => onUpdateServerDraft(server.id, 'tablePrefix', event.target.value)}
|
|
249
|
+
className={`${INPUT_CLASS_NAME} font-mono`}
|
|
250
|
+
disabled={!canEdit}
|
|
251
|
+
aria-label={`Table prefix for ${server.name}`}
|
|
252
|
+
/>
|
|
253
|
+
</td>
|
|
254
|
+
) : null}
|
|
255
|
+
<td className="px-4 py-3 align-top">
|
|
256
|
+
<div className="flex flex-wrap gap-2">
|
|
257
|
+
{isCurrent ? <ServerStatusBadge label="Current" tone="green" /> : null}
|
|
258
|
+
{isDirty ? <ServerStatusBadge label="Unsaved" tone="blue" /> : null}
|
|
259
|
+
{dnsDiagnostic?.status === 'verified' ? (
|
|
260
|
+
<ServerStatusBadge label="DNS ready" tone="green" />
|
|
261
|
+
) : null}
|
|
262
|
+
{hasDnsIssue ? <ServerStatusBadge label="DNS issue" tone="amber" /> : null}
|
|
263
|
+
{!isCurrent && !isDirty && !dnsDiagnostic ? <span className="text-xs text-gray-400">-</span> : null}
|
|
264
|
+
</div>
|
|
265
|
+
</td>
|
|
266
|
+
<td className="px-4 py-3 align-top text-xs text-gray-600">
|
|
267
|
+
<span className="whitespace-nowrap font-mono">{formatDateTime(server.createdAt)}</span>
|
|
268
|
+
</td>
|
|
269
|
+
<td className="px-4 py-3 align-top text-xs text-gray-600">
|
|
270
|
+
<span className="whitespace-nowrap font-mono">{formatDateTime(server.updatedAt)}</span>
|
|
271
|
+
</td>
|
|
272
|
+
<td className="px-4 py-3 align-top">
|
|
273
|
+
<div className="flex flex-wrap justify-end gap-2">
|
|
274
|
+
<button
|
|
275
|
+
type="button"
|
|
276
|
+
onClick={() => void onSaveServer(server.id)}
|
|
277
|
+
disabled={!canEdit || !isDirty || isSaving}
|
|
278
|
+
className={`${PRIMARY_BUTTON_CLASS_NAME} px-2 py-1 text-xs`}
|
|
279
|
+
>
|
|
280
|
+
{isSaving ? (
|
|
281
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
282
|
+
) : (
|
|
283
|
+
<Save className="h-3.5 w-3.5" />
|
|
284
|
+
)}
|
|
285
|
+
Save
|
|
286
|
+
</button>
|
|
287
|
+
<button
|
|
288
|
+
type="button"
|
|
289
|
+
onClick={() => void onMigrateServer(server.id)}
|
|
290
|
+
disabled={!canEdit || isMigrating}
|
|
291
|
+
className={`${SECONDARY_BUTTON_CLASS_NAME} px-2 py-1 text-xs`}
|
|
292
|
+
>
|
|
293
|
+
{isMigrating ? (
|
|
294
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
295
|
+
) : (
|
|
296
|
+
<RefreshCcw className="h-3.5 w-3.5" />
|
|
297
|
+
)}
|
|
298
|
+
Migrate
|
|
299
|
+
</button>
|
|
300
|
+
<button
|
|
301
|
+
type="button"
|
|
302
|
+
onClick={() => void onSwitchServer(server)}
|
|
303
|
+
disabled={isNavigating}
|
|
304
|
+
className={`${SECONDARY_BUTTON_CLASS_NAME} px-2 py-1 text-xs`}
|
|
305
|
+
>
|
|
306
|
+
{isNavigating ? (
|
|
307
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
308
|
+
) : (
|
|
309
|
+
<ArrowRightLeft className="h-3.5 w-3.5" />
|
|
310
|
+
)}
|
|
311
|
+
Switch
|
|
312
|
+
</button>
|
|
313
|
+
</div>
|
|
314
|
+
</td>
|
|
315
|
+
</tr>
|
|
316
|
+
{hasDnsIssue ? (
|
|
317
|
+
<tr className="bg-amber-50/70">
|
|
318
|
+
<td colSpan={columnCount} className="px-4 py-4">
|
|
319
|
+
<div className="space-y-4 rounded-xl border border-amber-200 bg-amber-50 px-4 py-4 text-sm text-amber-900">
|
|
320
|
+
<div className="space-y-1">
|
|
321
|
+
<p className="font-semibold">
|
|
322
|
+
DNS setup needs attention for <span className="font-mono">{server.domain}</span>
|
|
323
|
+
</p>
|
|
324
|
+
<p>{dnsDiagnostic?.summary}</p>
|
|
325
|
+
{dnsDiagnostic?.resolvedAddresses.length ? (
|
|
326
|
+
<p className="text-xs text-amber-800">
|
|
327
|
+
Currently resolves to:{' '}
|
|
328
|
+
<span className="font-mono">{dnsDiagnostic.resolvedAddresses.join(', ')}</span>
|
|
329
|
+
</p>
|
|
330
|
+
) : null}
|
|
331
|
+
</div>
|
|
332
|
+
|
|
333
|
+
{dnsDiagnostic?.expectedRecords.length ? (
|
|
334
|
+
<div className="overflow-x-auto rounded-lg border border-amber-200 bg-white">
|
|
335
|
+
<table className="min-w-full divide-y divide-amber-100 text-xs">
|
|
336
|
+
<thead className="bg-amber-100/60 text-amber-900">
|
|
337
|
+
<tr>
|
|
338
|
+
<th className="px-3 py-2 text-left font-semibold">Type</th>
|
|
339
|
+
<th className="px-3 py-2 text-left font-semibold">Name</th>
|
|
340
|
+
<th className="px-3 py-2 text-left font-semibold">Value</th>
|
|
341
|
+
<th className="px-3 py-2 text-left font-semibold">When to use</th>
|
|
342
|
+
</tr>
|
|
343
|
+
</thead>
|
|
344
|
+
<tbody className="divide-y divide-amber-100">
|
|
345
|
+
{dnsDiagnostic.expectedRecords.map((record) => (
|
|
346
|
+
<tr key={`${record.type}-${record.name}-${record.value}`}>
|
|
347
|
+
<td className="px-3 py-2 font-mono font-semibold text-amber-900">
|
|
348
|
+
{record.type}
|
|
349
|
+
</td>
|
|
350
|
+
<td className="px-3 py-2 font-mono text-slate-800">
|
|
351
|
+
{record.name}
|
|
352
|
+
</td>
|
|
353
|
+
<td className="px-3 py-2 font-mono text-slate-800">
|
|
354
|
+
{record.value}
|
|
355
|
+
</td>
|
|
356
|
+
<td className="px-3 py-2 text-amber-900">
|
|
357
|
+
{record.note || 'Required.'}
|
|
358
|
+
</td>
|
|
359
|
+
</tr>
|
|
360
|
+
))}
|
|
361
|
+
</tbody>
|
|
362
|
+
</table>
|
|
363
|
+
</div>
|
|
364
|
+
) : null}
|
|
365
|
+
|
|
366
|
+
<div className="space-y-2">
|
|
367
|
+
<p className="font-medium">How to fix it</p>
|
|
368
|
+
<ol className="list-decimal space-y-1 pl-5 text-amber-900">
|
|
369
|
+
<li>Open your DNS provider for this domain.</li>
|
|
370
|
+
<li>
|
|
371
|
+
Add one of the records above for{' '}
|
|
372
|
+
<span className="font-mono">{server.domain}</span>.
|
|
373
|
+
</li>
|
|
374
|
+
<li>Remove conflicting A, AAAA, or CNAME records for the same hostname.</li>
|
|
375
|
+
<li>Wait for DNS propagation, then refresh this page.</li>
|
|
376
|
+
</ol>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<div className="space-y-2">
|
|
380
|
+
<p className="font-medium">Provider guides</p>
|
|
381
|
+
<div className="flex flex-wrap gap-3 text-xs">
|
|
382
|
+
{dnsDiagnostic?.providerGuides.map((guide) => (
|
|
383
|
+
<a
|
|
384
|
+
key={guide.href}
|
|
385
|
+
href={guide.href}
|
|
386
|
+
target="_blank"
|
|
387
|
+
rel="noreferrer"
|
|
388
|
+
className="font-semibold text-amber-700 underline decoration-amber-400 underline-offset-2 hover:text-amber-900"
|
|
389
|
+
>
|
|
390
|
+
{guide.label}
|
|
391
|
+
</a>
|
|
392
|
+
))}
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
</td>
|
|
397
|
+
</tr>
|
|
398
|
+
) : null}
|
|
399
|
+
</>
|
|
288
400
|
);
|
|
289
401
|
}
|
|
290
402
|
|
|
@@ -300,6 +412,7 @@ export function ServersRegistryTable(props: ServersRegistryTableProps) {
|
|
|
300
412
|
const {
|
|
301
413
|
currentServerId,
|
|
302
414
|
canEdit,
|
|
415
|
+
isStandaloneVps,
|
|
303
416
|
isServerDraftDirty,
|
|
304
417
|
loading,
|
|
305
418
|
migratingServerId,
|
|
@@ -329,22 +442,37 @@ export function ServersRegistryTable(props: ServersRegistryTableProps) {
|
|
|
329
442
|
) : (
|
|
330
443
|
<div className="mt-4 overflow-x-auto rounded-xl border border-gray-200">
|
|
331
444
|
<table className="min-w-full table-fixed divide-y divide-gray-200 text-sm">
|
|
332
|
-
|
|
333
|
-
<
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
445
|
+
{isStandaloneVps ? (
|
|
446
|
+
<colgroup>
|
|
447
|
+
<col className="w-[16rem]" />
|
|
448
|
+
<col className="w-[18rem]" />
|
|
449
|
+
<col className="w-[10rem]" />
|
|
450
|
+
<col className="w-[11rem]" />
|
|
451
|
+
<col className="w-[11rem]" />
|
|
452
|
+
<col className="w-[12rem]" />
|
|
453
|
+
</colgroup>
|
|
454
|
+
) : (
|
|
455
|
+
<colgroup>
|
|
456
|
+
<col className="w-[16rem]" />
|
|
457
|
+
<col className="w-[10rem]" />
|
|
458
|
+
<col className="w-[18rem]" />
|
|
459
|
+
<col className="w-[13rem]" />
|
|
460
|
+
<col className="w-[10rem]" />
|
|
461
|
+
<col className="w-[11rem]" />
|
|
462
|
+
<col className="w-[11rem]" />
|
|
463
|
+
<col className="w-[12rem]" />
|
|
464
|
+
</colgroup>
|
|
465
|
+
)}
|
|
342
466
|
<thead className="bg-gray-50 text-xs uppercase tracking-wide text-gray-500">
|
|
343
467
|
<tr>
|
|
344
468
|
<th className="px-4 py-3 text-left font-semibold">Name</th>
|
|
345
|
-
|
|
469
|
+
{!isStandaloneVps ? (
|
|
470
|
+
<th className="px-4 py-3 text-left font-semibold">Environment</th>
|
|
471
|
+
) : null}
|
|
346
472
|
<th className="px-4 py-3 text-left font-semibold">Domain</th>
|
|
347
|
-
|
|
473
|
+
{!isStandaloneVps ? (
|
|
474
|
+
<th className="px-4 py-3 text-left font-semibold">Table prefix</th>
|
|
475
|
+
) : null}
|
|
348
476
|
<th className="px-4 py-3 text-left font-semibold">Status</th>
|
|
349
477
|
<th className="px-4 py-3 text-left font-semibold">Created</th>
|
|
350
478
|
<th className="px-4 py-3 text-left font-semibold">Updated</th>
|
|
@@ -353,21 +481,23 @@ export function ServersRegistryTable(props: ServersRegistryTableProps) {
|
|
|
353
481
|
</thead>
|
|
354
482
|
<tbody className="divide-y divide-gray-200 bg-white">
|
|
355
483
|
{servers.map((server) => (
|
|
356
|
-
<
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
484
|
+
<Fragment key={server.id}>
|
|
485
|
+
<ServersRegistryTableRow
|
|
486
|
+
currentServerId={currentServerId}
|
|
487
|
+
canEdit={canEdit}
|
|
488
|
+
draft={serverDrafts[server.id]}
|
|
489
|
+
isDirty={isServerDraftDirty(server)}
|
|
490
|
+
isStandaloneVps={isStandaloneVps}
|
|
491
|
+
isMigrating={migratingServerId === server.id}
|
|
492
|
+
isNavigating={navigatingServerId === server.id}
|
|
493
|
+
isSaving={savingServerId === server.id}
|
|
494
|
+
onMigrateServer={onMigrateServer}
|
|
495
|
+
onSaveServer={onSaveServer}
|
|
496
|
+
onSwitchServer={onSwitchServer}
|
|
497
|
+
onUpdateServerDraft={onUpdateServerDraft}
|
|
498
|
+
server={server}
|
|
499
|
+
/>
|
|
500
|
+
</Fragment>
|
|
371
501
|
))}
|
|
372
502
|
</tbody>
|
|
373
503
|
</table>
|