dbdiagram 0.1.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/COPYRIGHT.md +3 -0
- package/README.md +189 -0
- package/dist/actions/auth/auth-login.action.js +44 -0
- package/dist/actions/auth/auth-logout.action.js +13 -0
- package/dist/actions/auth/auth-status.action.js +38 -0
- package/dist/actions/build/build-document.action.js +142 -0
- package/dist/actions/delete.action.js +67 -0
- package/dist/actions/init.action.js +105 -0
- package/dist/actions/list/list-document.action.js +130 -0
- package/dist/actions/list/list.action.js +103 -0
- package/dist/actions/pull.action.js +58 -0
- package/dist/actions/push.action.js +91 -0
- package/dist/actions/tokens/token-delete.action.js +50 -0
- package/dist/actions/tokens/token-generate.action.js +52 -0
- package/dist/actions/tokens/token-list.action.js +52 -0
- package/dist/assets/callback-error.html +109 -0
- package/dist/assets/callback-success.html +73 -0
- package/dist/commands/auth/auth-login.command.js +7 -0
- package/dist/commands/auth/auth-logout.command.js +7 -0
- package/dist/commands/auth/auth-status.command.js +8 -0
- package/dist/commands/auth/auth.command.js +16 -0
- package/dist/commands/build/build-document.command.js +23 -0
- package/dist/commands/build/build.command.js +10 -0
- package/dist/commands/command.loader.js +18 -0
- package/dist/commands/delete.command.js +12 -0
- package/dist/commands/init.command.js +14 -0
- package/dist/commands/list/list-document.command.js +11 -0
- package/dist/commands/list/list.command.js +15 -0
- package/dist/commands/pull.command.js +11 -0
- package/dist/commands/push.command.js +15 -0
- package/dist/commands/tokens/token-delete.command.js +9 -0
- package/dist/commands/tokens/token-generate.command.js +9 -0
- package/dist/commands/tokens/token-list.command.js +8 -0
- package/dist/commands/tokens/tokens.command.js +15 -0
- package/dist/config/credential-manager.js +52 -0
- package/dist/config/settings-manager.js +44 -0
- package/dist/config.js +14 -0
- package/dist/config.staging.js +14 -0
- package/dist/constants/auth-message.constant.js +1 -0
- package/dist/constants/document.constant.js +8 -0
- package/dist/constants/dot-dbdiagram-dir.constant.js +9 -0
- package/dist/constants/viz-read-error.constant.js +6 -0
- package/dist/errors/portal-api.error.js +37 -0
- package/dist/errors/portal-error-codes.js +23 -0
- package/dist/hooks/build-document.hook.js +30 -0
- package/dist/hooks/delete.hook.js +12 -0
- package/dist/hooks/global.hook.js +6 -0
- package/dist/hooks/list.hook.js +23 -0
- package/dist/hooks/pull.hook.js +15 -0
- package/dist/hooks/push.hook.js +19 -0
- package/dist/index.js +12 -0
- package/dist/integrations/portal/portal-http.integration.js +27 -0
- package/dist/integrations/portal/portal.integration.js +68 -0
- package/dist/libs/portal/client.js +114 -0
- package/dist/libs/portal/errors.js +20 -0
- package/dist/libs/portal/index.js +2 -0
- package/dist/libs/portal/server.js +70 -0
- package/dist/program.js +16 -0
- package/dist/services/dbml/dbml.service.js +30 -0
- package/dist/services/dbml/dbml.worker.js +40 -0
- package/dist/services/file.service.js +6 -0
- package/dist/services/viz/diagram-viz-converter.service.js +67 -0
- package/dist/services/viz/diagram-viz-file.service.js +43 -0
- package/dist/types/config.type.js +1 -0
- package/dist/types/diagram-viz.type.js +6 -0
- package/dist/types/integrations/portal.type.js +1 -0
- package/dist/utils/dbml.util.js +30 -0
- package/dist/utils/diagram-viz-error.util.js +11 -0
- package/dist/utils/logger.util.js +3 -0
- package/dist/utils/output.util.js +18 -0
- package/dist/utils/portal-error.util.js +18 -0
- package/dist/utils/table.util.js +10 -0
- package/dist/utils/validation.util.js +32 -0
- package/package.json +56 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { log, spinner } from '@clack/prompts';
|
|
2
|
+
import { DBDOCS_CONFIGS } from '../../config.staging.js';
|
|
3
|
+
import { getCredential } from '../../config/credential-manager.js';
|
|
4
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../../constants/auth-message.constant.js';
|
|
5
|
+
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
6
|
+
import { logConsole } from '../../utils/logger.util.js';
|
|
7
|
+
import { printJson, printTable } from '../../utils/output.util.js';
|
|
8
|
+
import { isPortalApiError, getPortalApiErrorMessage } from '../../utils/portal-error.util.js';
|
|
9
|
+
import { computeColWidths } from '../../utils/table.util.js';
|
|
10
|
+
function mapRole(role) {
|
|
11
|
+
switch (role) {
|
|
12
|
+
case 'admin':
|
|
13
|
+
return 'Administrator';
|
|
14
|
+
case 'editor':
|
|
15
|
+
return 'View and edit';
|
|
16
|
+
case 'viewer':
|
|
17
|
+
return 'View only';
|
|
18
|
+
default:
|
|
19
|
+
return role;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function mapAccessControl(ac) {
|
|
23
|
+
switch (ac) {
|
|
24
|
+
case 'public':
|
|
25
|
+
return 'Public';
|
|
26
|
+
case 'protected':
|
|
27
|
+
return 'Password-protected';
|
|
28
|
+
case 'restricted':
|
|
29
|
+
return 'Private';
|
|
30
|
+
default:
|
|
31
|
+
return 'N/A';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export async function listDocumentAction(opts) {
|
|
35
|
+
const credential = getCredential();
|
|
36
|
+
if (!credential) {
|
|
37
|
+
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const s = spinner();
|
|
42
|
+
s.start('Fetching documents...');
|
|
43
|
+
try {
|
|
44
|
+
const currentUser = await portalIntegration.getCurrentUser();
|
|
45
|
+
let workspaceUrlNames;
|
|
46
|
+
if (opts.workspace) {
|
|
47
|
+
workspaceUrlNames = [opts.workspace];
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const workspaces = await portalIntegration.listWorkspaces();
|
|
51
|
+
workspaceUrlNames = [...workspaces.owned, ...workspaces.member, ...workspaces.guested].map((w) => w.urlName);
|
|
52
|
+
}
|
|
53
|
+
const results = await Promise.allSettled(workspaceUrlNames.map(async (urlName) => {
|
|
54
|
+
const data = await portalIntegration.listWorkspaceProjects(urlName);
|
|
55
|
+
const projects = [
|
|
56
|
+
...data.projects,
|
|
57
|
+
...(data.shared ?? []),
|
|
58
|
+
...(data.private ?? []),
|
|
59
|
+
];
|
|
60
|
+
return { urlName, projects };
|
|
61
|
+
}));
|
|
62
|
+
const workspacesWithProjects = [];
|
|
63
|
+
for (let i = 0; i < results.length; i++) {
|
|
64
|
+
const result = results[i];
|
|
65
|
+
if (result.status === 'fulfilled') {
|
|
66
|
+
if (result.value.projects.length > 0) {
|
|
67
|
+
workspacesWithProjects.push(result.value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
if (opts.workspace) {
|
|
72
|
+
throw result.reason;
|
|
73
|
+
}
|
|
74
|
+
const urlName = workspaceUrlNames[i];
|
|
75
|
+
log.warn(`Failed to fetch documents for workspace "${urlName}". Skipping.`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (workspacesWithProjects.length === 0) {
|
|
79
|
+
s.stop('No documents found.');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const totalCount = workspacesWithProjects.reduce((sum, w) => sum + w.projects.length, 0);
|
|
83
|
+
s.stop(`Fetched ${totalCount} documents.`);
|
|
84
|
+
if (opts.json) {
|
|
85
|
+
const allMappedData = workspacesWithProjects.flatMap((w) => w.projects.map((p) => {
|
|
86
|
+
const ownerLabel = p.owner.email === currentUser.email ? `${p.owner.email} (you)` : p.owner.email;
|
|
87
|
+
return {
|
|
88
|
+
workspace: w.urlName,
|
|
89
|
+
name: p.name,
|
|
90
|
+
owner: ownerLabel,
|
|
91
|
+
permission: mapRole(p.role),
|
|
92
|
+
accessControl: mapAccessControl(p.accessControl),
|
|
93
|
+
url: `${DBDOCS_CONFIGS.baseUrl}/${w.urlName}/${p.urlName}`,
|
|
94
|
+
updatedAt: p.updatedAt,
|
|
95
|
+
};
|
|
96
|
+
}));
|
|
97
|
+
printJson(allMappedData);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
for (const w of workspacesWithProjects) {
|
|
101
|
+
logConsole(`\n${w.urlName}`);
|
|
102
|
+
printTable(w.projects, {
|
|
103
|
+
head: ['Name', 'Owner', 'Permission', 'Access Control', 'Url', 'Last updated'],
|
|
104
|
+
colWidths: computeColWidths([0.14, 0.22, 0.12, 0.14, 0.24, 0.14]),
|
|
105
|
+
rowMapper: (p) => {
|
|
106
|
+
const ownerLabel = p.owner.email === currentUser.email ? `${p.owner.email} (you)` : p.owner.email;
|
|
107
|
+
return [
|
|
108
|
+
p.name,
|
|
109
|
+
ownerLabel,
|
|
110
|
+
mapRole(p.role),
|
|
111
|
+
mapAccessControl(p.accessControl),
|
|
112
|
+
`${DBDOCS_CONFIGS.baseUrl}/${w.urlName}/${p.urlName}`,
|
|
113
|
+
new Date(p.updatedAt).toLocaleString(),
|
|
114
|
+
];
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
s.stop('Failed to fetch documents.');
|
|
122
|
+
if (isPortalApiError(error)) {
|
|
123
|
+
log.error(getPortalApiErrorMessage(error));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
log.error(error instanceof Error ? error.message : String(error));
|
|
127
|
+
}
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { log, spinner } from '@clack/prompts';
|
|
2
|
+
import { DBDIAGRAM_CONFIGS } from '../../config.staging.js';
|
|
3
|
+
import { getCredential } from '../../config/credential-manager.js';
|
|
4
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../../constants/auth-message.constant.js';
|
|
5
|
+
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
6
|
+
import { isPortalApiError, getPortalApiErrorMessage } from '../../utils/portal-error.util.js';
|
|
7
|
+
import { computeColWidths } from '../../utils/table.util.js';
|
|
8
|
+
import { printJson, printTable } from '../../utils/output.util.js';
|
|
9
|
+
function mapStatus(status) {
|
|
10
|
+
switch (status) {
|
|
11
|
+
case 'public':
|
|
12
|
+
return 'Public';
|
|
13
|
+
case 'protected':
|
|
14
|
+
return 'Password-protected';
|
|
15
|
+
case 'private':
|
|
16
|
+
return 'Private';
|
|
17
|
+
default:
|
|
18
|
+
return status;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function ownerLabel(owner, currentUserEmail) {
|
|
22
|
+
return owner.email === currentUserEmail ? `${owner.email} (you)` : owner.email;
|
|
23
|
+
}
|
|
24
|
+
export async function listAction(opts) {
|
|
25
|
+
const credential = getCredential();
|
|
26
|
+
if (!credential) {
|
|
27
|
+
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const s = spinner();
|
|
32
|
+
s.start('Fetching diagrams...');
|
|
33
|
+
try {
|
|
34
|
+
if (opts.workspace) {
|
|
35
|
+
const [currentUser, data] = await Promise.all([
|
|
36
|
+
portalIntegration.getCurrentUser(),
|
|
37
|
+
portalIntegration.listDiagrams(opts.workspace),
|
|
38
|
+
]);
|
|
39
|
+
const allDiagrams = [
|
|
40
|
+
...data.diagrams,
|
|
41
|
+
...data.shared,
|
|
42
|
+
...data.private,
|
|
43
|
+
];
|
|
44
|
+
if (allDiagrams.length === 0) {
|
|
45
|
+
s.stop('No diagrams found.');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
s.stop(`Fetched ${allDiagrams.length} diagrams.`);
|
|
49
|
+
const mappedData = allDiagrams.map((d) => ({
|
|
50
|
+
name: d.name,
|
|
51
|
+
id: d.id,
|
|
52
|
+
owner: ownerLabel(d.owner, currentUser.email),
|
|
53
|
+
status: mapStatus(d.status),
|
|
54
|
+
url: `${DBDIAGRAM_CONFIGS.baseUrl}/d/${d.id}`,
|
|
55
|
+
updatedAt: new Date(d.updatedAt).toLocaleString(),
|
|
56
|
+
}));
|
|
57
|
+
if (opts.json) {
|
|
58
|
+
printJson(mappedData);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
printTable(mappedData, {
|
|
62
|
+
head: ['Name', 'ID', 'Owner', 'Status', 'Url', 'Last updated'],
|
|
63
|
+
colWidths: computeColWidths([0.18, 0.22, 0.14, 0.09, 0.26, 0.11]),
|
|
64
|
+
rowMapper: (d) => [d.name, d.id, d.owner, d.status, d.url, d.updatedAt],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const data = await portalIntegration.listDiagrams();
|
|
70
|
+
if (data.diagrams.length === 0) {
|
|
71
|
+
s.stop('No diagrams found.');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
s.stop(`Fetched ${data.diagrams.length} diagrams.`);
|
|
75
|
+
const mappedData = data.diagrams.map((d) => ({
|
|
76
|
+
name: d.name,
|
|
77
|
+
id: d.id,
|
|
78
|
+
url: `${DBDIAGRAM_CONFIGS.baseUrl}/d/${d.id}`,
|
|
79
|
+
updatedAt: new Date(d.updatedAt).toLocaleString(),
|
|
80
|
+
}));
|
|
81
|
+
if (opts.json) {
|
|
82
|
+
printJson(mappedData);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
printTable(mappedData, {
|
|
86
|
+
head: ['Name', 'ID', 'Url', 'Last updated'],
|
|
87
|
+
colWidths: computeColWidths([0.25, 0.18, 0.35, 0.22]),
|
|
88
|
+
rowMapper: (d) => [d.name, d.id, d.url, d.updatedAt],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
s.stop('Failed to fetch diagrams.');
|
|
95
|
+
if (isPortalApiError(error)) {
|
|
96
|
+
log.error(getPortalApiErrorMessage(error));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
log.error(error instanceof Error ? error.message : String(error));
|
|
100
|
+
}
|
|
101
|
+
process.exitCode = 1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { log, spinner } from '@clack/prompts';
|
|
2
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../constants/auth-message.constant.js';
|
|
3
|
+
import { getCredential } from '../config/credential-manager.js';
|
|
4
|
+
import { writeFileWithDir } from '../services/file.service.js';
|
|
5
|
+
import { portalIntegration } from '../integrations/portal/portal.integration.js';
|
|
6
|
+
import { deriveDiagramVizPathFromDBML, readDiagramVizFile, writeDiagramVizFile, } from '../services/viz/diagram-viz-file.service.js';
|
|
7
|
+
import { getVizReadErrorMessage } from '../utils/diagram-viz-error.util.js';
|
|
8
|
+
import { fromServerFormat } from '../services/viz/diagram-viz-converter.service.js';
|
|
9
|
+
import { logConsole } from '../utils/logger.util.js';
|
|
10
|
+
import { getPortalApiErrorMessage, isPortalApiError } from '../utils/portal-error.util.js';
|
|
11
|
+
export async function pullAction(options) {
|
|
12
|
+
const credential = getCredential();
|
|
13
|
+
if (!credential) {
|
|
14
|
+
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (!options.diagramId) {
|
|
19
|
+
log.error('Missing required flag: --diagram-id');
|
|
20
|
+
process.exitCode = 1;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const s = spinner();
|
|
24
|
+
s.start('Fetching diagram...');
|
|
25
|
+
try {
|
|
26
|
+
const diagram = await portalIntegration.getDiagram(options.diagramId);
|
|
27
|
+
if (options.outFile) {
|
|
28
|
+
await writeFileWithDir(options.outFile, diagram.content);
|
|
29
|
+
if (diagram.vizData) {
|
|
30
|
+
const vizPath = deriveDiagramVizPathFromDBML(options.outFile);
|
|
31
|
+
const existingResult = await readDiagramVizFile(vizPath);
|
|
32
|
+
if (!existingResult.ok) {
|
|
33
|
+
const message = getVizReadErrorMessage(existingResult.errorCode, vizPath);
|
|
34
|
+
log.warn(message);
|
|
35
|
+
}
|
|
36
|
+
const state = fromServerFormat(diagram.vizData, existingResult.ok ? existingResult.state : undefined);
|
|
37
|
+
await writeDiagramVizFile(vizPath, state);
|
|
38
|
+
}
|
|
39
|
+
const label = diagram.name ? `"${diagram.name}"` : 'your diagram';
|
|
40
|
+
s.stop(`Saved ${label} to ${options.outFile}`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
s.stop('Diagram fetched successfully.');
|
|
44
|
+
logConsole(diagram.content);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
s.stop('Failed to fetch diagram.');
|
|
49
|
+
if (isPortalApiError(error)) {
|
|
50
|
+
log.error(getPortalApiErrorMessage(error));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
54
|
+
log.error(message);
|
|
55
|
+
}
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { log, spinner } from '@clack/prompts';
|
|
3
|
+
import { getCredential } from '../config/credential-manager.js';
|
|
4
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../constants/auth-message.constant.js';
|
|
5
|
+
import { portalIntegration } from '../integrations/portal/portal.integration.js';
|
|
6
|
+
import { deriveDiagramVizPathFromDBML, readDiagramVizFile } from '../services/viz/diagram-viz-file.service.js';
|
|
7
|
+
import { getVizReadErrorMessage } from '../utils/diagram-viz-error.util.js';
|
|
8
|
+
import { toServerFormat } from '../services/viz/diagram-viz-converter.service.js';
|
|
9
|
+
import { preparePushDiagramData } from '../services/dbml/dbml.service.js';
|
|
10
|
+
import { DBDIAGRAM_CONFIGS } from '../config.js';
|
|
11
|
+
import { isPortalApiError, getPortalApiErrorMessage } from '../utils/portal-error.util.js';
|
|
12
|
+
import { printJson } from '../utils/output.util.js';
|
|
13
|
+
import { formatDbmlError } from '../utils/dbml.util.js';
|
|
14
|
+
export async function pushAction(filepath, options, thisCommand) {
|
|
15
|
+
const credential = getCredential();
|
|
16
|
+
if (!credential) {
|
|
17
|
+
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const resolvedFilepath = thisCommand.args[0] || filepath;
|
|
22
|
+
if (!resolvedFilepath) {
|
|
23
|
+
log.error('Missing required argument: filepath. Provide it as an argument or set dbml.entry in settings.');
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
let dbml;
|
|
28
|
+
try {
|
|
29
|
+
dbml = await readFile(resolvedFilepath, 'utf8');
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33
|
+
log.error(`Failed to read file: ${message}`);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
let preparedPushData;
|
|
38
|
+
try {
|
|
39
|
+
preparedPushData = await preparePushDiagramData(dbml);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
log.error(`Failed to parse DBML: ${formatDbmlError(error)}`);
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const vizPath = deriveDiagramVizPathFromDBML(resolvedFilepath);
|
|
47
|
+
const vizResult = await readDiagramVizFile(vizPath);
|
|
48
|
+
let vizData;
|
|
49
|
+
if (vizResult.ok) {
|
|
50
|
+
const { diagramViews } = preparedPushData;
|
|
51
|
+
vizData = toServerFormat(vizResult.state, diagramViews);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const message = getVizReadErrorMessage(vizResult.errorCode, vizPath);
|
|
55
|
+
log.warn(message);
|
|
56
|
+
}
|
|
57
|
+
const s = spinner();
|
|
58
|
+
s.start('Pushing diagram to dbdiagram...');
|
|
59
|
+
try {
|
|
60
|
+
let diagramId;
|
|
61
|
+
if (!options.new && options.diagramId) {
|
|
62
|
+
({ diagramId } = await portalIntegration.updateDiagram({
|
|
63
|
+
diagramId: options.diagramId, name: options.name, dbml, vizData,
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
({ diagramId } = await portalIntegration.createDiagram({
|
|
68
|
+
name: options.name, dbml, workspaceId: options.workspace, vizData,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
const diagramUrl = `${DBDIAGRAM_CONFIGS.baseUrl}/d/${diagramId}`;
|
|
72
|
+
if (options.json) {
|
|
73
|
+
s.stop();
|
|
74
|
+
printJson({ diagramId, url: diagramUrl });
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
s.stop(`Diagram pushed successfully. Open the following link to see your diagram: ${diagramUrl}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
s.stop('Failed to push diagram.');
|
|
82
|
+
if (isPortalApiError(error)) {
|
|
83
|
+
log.error(getPortalApiErrorMessage(error));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
87
|
+
log.error(message);
|
|
88
|
+
}
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { confirm, isCancel, log, spinner } from '@clack/prompts';
|
|
2
|
+
import { getCredential } from '../../config/credential-manager.js';
|
|
3
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../../constants/auth-message.constant.js';
|
|
4
|
+
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
5
|
+
import { isPortalApiError, getPortalApiErrorMessage } from '../../utils/portal-error.util.js';
|
|
6
|
+
import { printJson } from '../../utils/output.util.js';
|
|
7
|
+
export async function tokenDeleteAction(tokenId, options) {
|
|
8
|
+
const credential = getCredential();
|
|
9
|
+
if (!credential) {
|
|
10
|
+
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (!options.force) {
|
|
15
|
+
if (!process.stdout.isTTY) {
|
|
16
|
+
log.error('Use -f to force delete in non-interactive environment.');
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const shouldDelete = await confirm({
|
|
21
|
+
message: `Delete token '${tokenId}'?`,
|
|
22
|
+
initialValue: false,
|
|
23
|
+
});
|
|
24
|
+
if (isCancel(shouldDelete) || !shouldDelete) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const s = spinner();
|
|
29
|
+
s.start('Deleting token...');
|
|
30
|
+
try {
|
|
31
|
+
await portalIntegration.deleteToken(tokenId);
|
|
32
|
+
if (options.json) {
|
|
33
|
+
s.stop();
|
|
34
|
+
printJson({ id: tokenId, deleted: true });
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
s.stop(`Token ${tokenId} deleted.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
s.stop('Failed to delete token.');
|
|
42
|
+
if (isPortalApiError(error)) {
|
|
43
|
+
log.error(getPortalApiErrorMessage(error));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
log.error(error instanceof Error ? error.message : String(error));
|
|
47
|
+
}
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { log, spinner } from '@clack/prompts';
|
|
3
|
+
import packageJson from '../../../package.json' with { type: 'json' };
|
|
4
|
+
import { getCredential } from '../../config/credential-manager.js';
|
|
5
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../../constants/auth-message.constant.js';
|
|
6
|
+
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
7
|
+
import { isPortalApiError, getPortalApiErrorMessage } from '../../utils/portal-error.util.js';
|
|
8
|
+
import { printJson } from '../../utils/output.util.js';
|
|
9
|
+
import { logConsole } from '../../utils/logger.util.js';
|
|
10
|
+
const PLATFORM_NAMES = {
|
|
11
|
+
darwin: 'macOS',
|
|
12
|
+
win32: 'Windows',
|
|
13
|
+
linux: 'Linux',
|
|
14
|
+
};
|
|
15
|
+
export function generateDefaultTokenName() {
|
|
16
|
+
const osName = PLATFORM_NAMES[process.platform] ?? process.platform;
|
|
17
|
+
return `dbdiagram-cli (${os.hostname()}, ${osName} ${process.arch}, v${packageJson.version})`;
|
|
18
|
+
}
|
|
19
|
+
export async function tokenGenerateAction(opts) {
|
|
20
|
+
const credential = getCredential();
|
|
21
|
+
if (!credential) {
|
|
22
|
+
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
23
|
+
process.exitCode = 1;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const name = opts.name ?? generateDefaultTokenName();
|
|
27
|
+
const s = spinner();
|
|
28
|
+
s.start('Generating token...');
|
|
29
|
+
try {
|
|
30
|
+
const { id, name: tokenName, token } = await portalIntegration.generateToken({ name });
|
|
31
|
+
if (opts.json) {
|
|
32
|
+
s.stop();
|
|
33
|
+
printJson({ id, name: tokenName, token });
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
s.stop('Success! Token created. Save the token value below — it will not be shown again.');
|
|
37
|
+
logConsole(`Value: ${token}`);
|
|
38
|
+
logConsole(`ID: ${id}`);
|
|
39
|
+
logConsole(`Name: ${tokenName}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
s.stop('Failed to generate token.');
|
|
44
|
+
if (isPortalApiError(error)) {
|
|
45
|
+
log.error(getPortalApiErrorMessage(error));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
log.error(error instanceof Error ? error.message : String(error));
|
|
49
|
+
}
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { log, spinner } from '@clack/prompts';
|
|
2
|
+
import { DateTime } from 'luxon';
|
|
3
|
+
import { getCredential } from '../../config/credential-manager.js';
|
|
4
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../../constants/auth-message.constant.js';
|
|
5
|
+
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
6
|
+
import { isPortalApiError, getPortalApiErrorMessage } from '../../utils/portal-error.util.js';
|
|
7
|
+
import { computeColWidths } from '../../utils/table.util.js';
|
|
8
|
+
import { printJson, printTable } from '../../utils/output.util.js';
|
|
9
|
+
export async function tokenListAction(opts) {
|
|
10
|
+
const credential = getCredential();
|
|
11
|
+
if (!credential) {
|
|
12
|
+
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const s = spinner();
|
|
17
|
+
s.start('Fetching tokens...');
|
|
18
|
+
try {
|
|
19
|
+
const tokens = await portalIntegration.listTokens();
|
|
20
|
+
if (tokens.length === 0) {
|
|
21
|
+
s.stop('No tokens found. Generate one with `dbdiagram tokens generate`.');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
s.stop(`Fetched ${tokens.length} tokens.`);
|
|
25
|
+
const mappedData = tokens.map((t) => ({
|
|
26
|
+
id: t.id,
|
|
27
|
+
name: t.name,
|
|
28
|
+
createdAt: DateTime.fromISO(t.createdAt).toLocaleString(DateTime.DATETIME_SHORT),
|
|
29
|
+
lastUsedAt: t.lastUsedAt ? DateTime.fromISO(t.lastUsedAt).toLocaleString(DateTime.DATETIME_SHORT) : 'Never',
|
|
30
|
+
}));
|
|
31
|
+
if (opts.json) {
|
|
32
|
+
printJson(mappedData);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
printTable(mappedData, {
|
|
36
|
+
head: ['ID', 'Name', 'Created at', 'Last used'],
|
|
37
|
+
colWidths: computeColWidths([0.2, 0.45, 0.17, 0.18]),
|
|
38
|
+
rowMapper: (t) => [t.id, t.name, t.createdAt, t.lastUsedAt],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
s.stop('Failed to fetch tokens.');
|
|
44
|
+
if (isPortalApiError(error)) {
|
|
45
|
+
log.error(getPortalApiErrorMessage(error));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
log.error(error instanceof Error ? error.message : String(error));
|
|
49
|
+
}
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>dbdiagram CLI - Authentication Failed</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
min-height: 100vh;
|
|
20
|
+
background: #f9fafb;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.container {
|
|
24
|
+
background: white;
|
|
25
|
+
border-radius: 6px;
|
|
26
|
+
border: 1px solid #eff0f3;
|
|
27
|
+
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
28
|
+
padding: 32px;
|
|
29
|
+
width: 100%;
|
|
30
|
+
max-width: 400px;
|
|
31
|
+
text-align: center;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.logo-container {
|
|
35
|
+
margin-bottom: 16px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.logo {
|
|
39
|
+
height: 48px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
h1 {
|
|
43
|
+
color: #ff4f46;
|
|
44
|
+
font-size: 24px;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
margin-bottom: 20px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.error-box {
|
|
50
|
+
background: #ffeef1;
|
|
51
|
+
border: 1px solid #fea5a6;
|
|
52
|
+
border-radius: 6px;
|
|
53
|
+
padding: 16px;
|
|
54
|
+
text-align: left;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.error-title {
|
|
58
|
+
font-weight: 600;
|
|
59
|
+
color: #ff4f46;
|
|
60
|
+
margin-bottom: 8px;
|
|
61
|
+
font-size: 16px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.error-message {
|
|
65
|
+
font-size: 14px;
|
|
66
|
+
color: #ff4f46;
|
|
67
|
+
line-height: 1.5;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
p {
|
|
71
|
+
color: #718096;
|
|
72
|
+
line-height: 1.6;
|
|
73
|
+
font-size: 15px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
strong {
|
|
77
|
+
color: #2d3748;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.footer {
|
|
81
|
+
margin-top: 24px;
|
|
82
|
+
padding-top: 24px;
|
|
83
|
+
border-top: 1px solid #e2e8f0;
|
|
84
|
+
color: #a0aec0;
|
|
85
|
+
font-size: 13px;
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
88
|
+
</head>
|
|
89
|
+
<body>
|
|
90
|
+
<div class="container">
|
|
91
|
+
<div class="logo-container">
|
|
92
|
+
<svg class="logo" width="155" height="155" viewBox="0 0 155 155" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
93
|
+
<path d="M155 29C155 12.9837 142.016 0 126 0H29C12.9837 0 0 12.9837 0 29V126C0 142.016 12.9837 155 29 155H126C142.016 155 155 142.016 155 126V29Z" fill="#0246CC"/>
|
|
94
|
+
<path d="M77.5 54.6521C102.065 54.6521 121.978 47.8098 121.978 39.3694C121.978 30.929 102.065 24.0867 77.5 24.0867C52.9354 24.0867 33.0218 30.929 33.0218 39.3694C33.0218 47.8098 52.9354 54.6521 77.5 54.6521Z" fill="white"/>
|
|
95
|
+
<path d="M33.0399 76.4867V91.6759C33.0399 96.6868 50.3603 103.965 77.5001 103.965C104.64 103.965 121.96 96.6868 121.96 91.6759V76.4867C112.817 82.4469 95.0968 85.5314 77.5001 85.5314C59.9034 85.5314 42.1828 82.4469 33.0399 76.4867Z" fill="#287EFF"/>
|
|
96
|
+
<path d="M33.0399 50.5847V67.5282C33.0399 72.5391 50.3603 79.8172 77.5001 79.8172C104.64 79.8172 121.96 72.5391 121.96 67.5282V50.5847C113.453 57.1317 97.1229 61.3837 77.5001 61.3837C57.8772 61.3837 41.5476 57.1317 33.0399 50.5847Z" fill="#96C0FF"/>
|
|
97
|
+
<path d="M33.0399 101.065V116.254C33.0399 121.265 50.3603 128.543 77.5001 128.543C104.64 128.543 121.96 121.265 121.96 116.254V101.065C112.817 107.025 95.0968 110.11 77.5001 110.11C59.9034 110.11 42.1828 107.025 33.0399 101.065Z" fill="#0258ED"/>
|
|
98
|
+
</svg>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<h1>Authentication Failed</h1>
|
|
102
|
+
|
|
103
|
+
<div class="error-box">
|
|
104
|
+
<p class="error-title">Invalid redirect URL</p>
|
|
105
|
+
<p class="error-message">The authentication callback could not be completed.</p>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|