@danubedata/cli 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/README.md +138 -0
- package/bin/danube +2 -0
- package/dist/commands/deploy.d.ts +4 -0
- package/dist/commands/deploy.js +72 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/deployments.d.ts +2 -0
- package/dist/commands/deployments.js +52 -0
- package/dist/commands/deployments.js.map +1 -0
- package/dist/commands/domains.d.ts +2 -0
- package/dist/commands/domains.js +89 -0
- package/dist/commands/domains.js.map +1 -0
- package/dist/commands/link.d.ts +2 -0
- package/dist/commands/link.js +58 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +50 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.js +10 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.js +16 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-client.d.ts +11 -0
- package/dist/lib/api-client.js +63 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/config.d.ts +9 -0
- package/dist/lib/config.js +38 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/errors.d.ts +11 -0
- package/dist/lib/errors.js +23 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/output.d.ts +4 -0
- package/dist/lib/output.js +36 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/packager.d.ts +5 -0
- package/dist/lib/packager.js +85 -0
- package/dist/lib/packager.js.map +1 -0
- package/dist/lib/project.d.ts +13 -0
- package/dist/lib/project.js +39 -0
- package/dist/lib/project.js.map +1 -0
- package/dist/lib/sleep.d.ts +1 -0
- package/dist/lib/sleep.js +4 -0
- package/dist/lib/sleep.js.map +1 -0
- package/dist/lib/version.d.ts +9 -0
- package/dist/lib/version.js +80 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/types/api.d.ts +89 -0
- package/dist/types/api.js +2 -0
- package/dist/types/api.js.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# @danubedata/cli
|
|
2
|
+
|
|
3
|
+
Deploy static sites to [DanubeData](https://danubedata.ro) from the terminal.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @danubedata/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Node.js 18 or later.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Authenticate with your API token
|
|
17
|
+
danube login
|
|
18
|
+
|
|
19
|
+
# Link your project to a static site
|
|
20
|
+
danube pages link
|
|
21
|
+
|
|
22
|
+
# Deploy the current directory
|
|
23
|
+
danube pages deploy
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Authentication
|
|
27
|
+
|
|
28
|
+
Generate an API token from your [DanubeData dashboard](https://danubedata.ro) and authenticate:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
danube login
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
You can also pass the token directly:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
danube login --token <your-token>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or set the `DANUBE_TOKEN` environment variable for CI/CD:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
export DANUBE_TOKEN=your-token
|
|
44
|
+
danube pages deploy
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Commands
|
|
48
|
+
|
|
49
|
+
### `danube login`
|
|
50
|
+
|
|
51
|
+
Authenticate with DanubeData. Prompts for an API token or accepts `--token <token>`.
|
|
52
|
+
|
|
53
|
+
### `danube logout`
|
|
54
|
+
|
|
55
|
+
Remove stored authentication credentials.
|
|
56
|
+
|
|
57
|
+
### `danube whoami`
|
|
58
|
+
|
|
59
|
+
Show the currently authenticated user and their teams.
|
|
60
|
+
|
|
61
|
+
### `danube pages link`
|
|
62
|
+
|
|
63
|
+
Link the current directory to a DanubeData static site. Prompts you to select a team and site (or create a new one). Writes configuration to `.danube/project.json`.
|
|
64
|
+
|
|
65
|
+
### `danube pages deploy`
|
|
66
|
+
|
|
67
|
+
Deploy your site to DanubeData.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
danube pages deploy # Deploy current directory
|
|
71
|
+
danube pages deploy --dir dist # Deploy a specific directory
|
|
72
|
+
danube pages deploy --no-wait # Don't wait for build to complete
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The command packages your files into a ZIP archive, uploads them, and polls the build status until deployment is live.
|
|
76
|
+
|
|
77
|
+
### `danube pages deployments ls`
|
|
78
|
+
|
|
79
|
+
List all deployments for the linked site. Shows revision, status, trigger method, and timestamps.
|
|
80
|
+
|
|
81
|
+
### `danube pages deployments rollback <revision>`
|
|
82
|
+
|
|
83
|
+
Activate a previous deployment by revision number.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
danube pages deployments rollback 3
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### `danube pages domains ls`
|
|
90
|
+
|
|
91
|
+
List all domains configured for the linked site.
|
|
92
|
+
|
|
93
|
+
### `danube pages domains add <domain>`
|
|
94
|
+
|
|
95
|
+
Add a custom domain. Returns DNS verification instructions if required.
|
|
96
|
+
|
|
97
|
+
### `danube pages domains remove <domain>`
|
|
98
|
+
|
|
99
|
+
Remove a custom domain.
|
|
100
|
+
|
|
101
|
+
### `danube pages domains verify <domain>`
|
|
102
|
+
|
|
103
|
+
Trigger DNS verification for a custom domain.
|
|
104
|
+
|
|
105
|
+
## Configuration
|
|
106
|
+
|
|
107
|
+
### Project Configuration
|
|
108
|
+
|
|
109
|
+
Running `danube pages link` creates a `.danube/project.json` file in your project directory. This file is required for all `pages` subcommands.
|
|
110
|
+
|
|
111
|
+
### `danube.json`
|
|
112
|
+
|
|
113
|
+
You can optionally create a `danube.json` file in your project root to configure deployments:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"outputDir": "dist",
|
|
118
|
+
"ignore": ["*.map", "test/**"]
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- **`outputDir`** - Directory to deploy (overridden by `--dir` flag)
|
|
123
|
+
- **`ignore`** - Additional file patterns to exclude from deployment
|
|
124
|
+
|
|
125
|
+
Files matching `.gitignore` patterns are automatically excluded, along with `.git`, `node_modules`, and `.danube` directories.
|
|
126
|
+
|
|
127
|
+
## Environment Variables
|
|
128
|
+
|
|
129
|
+
| Variable | Description |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `DANUBE_TOKEN` | API token (alternative to `danube login`) |
|
|
132
|
+
| `DANUBE_API_BASE` | Override the API base URL |
|
|
133
|
+
| `CI` | Suppresses update notifications |
|
|
134
|
+
| `DANUBE_NO_UPDATE_CHECK` | Suppresses update notifications |
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT
|
package/bin/danube
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { access } from 'node:fs/promises';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { ApiClient } from '../lib/api-client.js';
|
|
7
|
+
import { readProjectConfig, readDanubeJson } from '../lib/project.js';
|
|
8
|
+
import { NotLinkedError } from '../lib/errors.js';
|
|
9
|
+
import { packageDirectory } from '../lib/packager.js';
|
|
10
|
+
import { formatBytes, statusColor } from '../lib/output.js';
|
|
11
|
+
import { sleep } from '../lib/sleep.js';
|
|
12
|
+
export const POLL_INTERVAL = 2000;
|
|
13
|
+
export const POLL_TIMEOUT = 5 * 60 * 1000;
|
|
14
|
+
export const deployCommand = new Command('deploy')
|
|
15
|
+
.description('Deploy your site to DanubeData')
|
|
16
|
+
.option('--dir <directory>', 'Directory to deploy (overrides danube.json)')
|
|
17
|
+
.option('--no-wait', 'Skip waiting for deployment to complete')
|
|
18
|
+
.action(async (opts) => {
|
|
19
|
+
const project = await readProjectConfig();
|
|
20
|
+
if (!project)
|
|
21
|
+
throw new NotLinkedError();
|
|
22
|
+
const api = await ApiClient.create();
|
|
23
|
+
const danubeJson = await readDanubeJson();
|
|
24
|
+
// Resolve deploy directory
|
|
25
|
+
const deployDir = resolve(opts.dir || danubeJson?.outputDir || '.');
|
|
26
|
+
// Verify directory exists
|
|
27
|
+
try {
|
|
28
|
+
await access(deployDir);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
console.error(chalk.red(`Directory not found: ${deployDir}`));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
// Package files
|
|
35
|
+
const packSpinner = ora('Packaging files...').start();
|
|
36
|
+
const { buffer, fileCount } = await packageDirectory(deployDir, danubeJson?.ignore);
|
|
37
|
+
packSpinner.succeed(`Packaged ${fileCount} files (${formatBytes(buffer.length)})`);
|
|
38
|
+
// Upload
|
|
39
|
+
const uploadSpinner = ora('Uploading...').start();
|
|
40
|
+
const deployRes = await api.upload(`/api/v1/static-sites/${project.siteId}/deploy`, buffer, 'deploy.zip');
|
|
41
|
+
uploadSpinner.succeed('Uploaded');
|
|
42
|
+
if (!opts.wait) {
|
|
43
|
+
console.log(chalk.green(`\nDeployment started. Status: ${deployRes.status}`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Poll build status
|
|
47
|
+
const pollSpinner = ora('Building...').start();
|
|
48
|
+
const startTime = Date.now();
|
|
49
|
+
while (Date.now() - startTime < POLL_TIMEOUT) {
|
|
50
|
+
await sleep(POLL_INTERVAL);
|
|
51
|
+
const buildRes = await api.get(`/api/v1/static-sites/${project.siteId}/builds/latest`);
|
|
52
|
+
const build = buildRes.data;
|
|
53
|
+
if (!build)
|
|
54
|
+
continue;
|
|
55
|
+
pollSpinner.text = `Status: ${build.status}...`;
|
|
56
|
+
if (build.status === 'succeeded') {
|
|
57
|
+
pollSpinner.succeed(`Deployed! Build #${build.build_number} ${statusColor('succeeded')}`);
|
|
58
|
+
const domain = project.defaultDomain || `${project.siteName}.pages.danubedata.ro`;
|
|
59
|
+
console.log(chalk.green(`\nLive at: ${chalk.bold(`https://${domain}`)}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (build.status === 'failed' || build.status === 'cancelled') {
|
|
63
|
+
pollSpinner.fail(`Deployment failed`);
|
|
64
|
+
if (build.error_message) {
|
|
65
|
+
console.error(chalk.red(build.error_message));
|
|
66
|
+
}
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
pollSpinner.warn('Timed out waiting for deployment. Check status with `danube deployments ls`.');
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=deploy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { ApiClient } from '../lib/api-client.js';
|
|
5
|
+
import { readProjectConfig } from '../lib/project.js';
|
|
6
|
+
import { NotLinkedError } from '../lib/errors.js';
|
|
7
|
+
import { formatTable, statusColor, formatDate } from '../lib/output.js';
|
|
8
|
+
const lsCommand = new Command('ls')
|
|
9
|
+
.description('List deployments')
|
|
10
|
+
.action(async () => {
|
|
11
|
+
const project = await readProjectConfig();
|
|
12
|
+
if (!project)
|
|
13
|
+
throw new NotLinkedError();
|
|
14
|
+
const api = await ApiClient.create();
|
|
15
|
+
const res = await api.get(`/api/v1/static-sites/${project.siteId}/deployments`);
|
|
16
|
+
if (res.data.length === 0) {
|
|
17
|
+
console.log('No deployments yet.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const rows = res.data.map(d => [
|
|
21
|
+
`#${d.revision_number}`,
|
|
22
|
+
statusColor(d.status) + (d.is_current ? chalk.cyan(' (current)') : ''),
|
|
23
|
+
d.trigger_type,
|
|
24
|
+
d.deployed_at ? formatDate(d.deployed_at) : '-',
|
|
25
|
+
formatDate(d.created_at),
|
|
26
|
+
]);
|
|
27
|
+
console.log(formatTable(['REVISION', 'STATUS', 'TRIGGER', 'DEPLOYED', 'CREATED'], rows));
|
|
28
|
+
});
|
|
29
|
+
const rollbackCommand = new Command('rollback')
|
|
30
|
+
.description('Activate a previous deployment')
|
|
31
|
+
.argument('<revision>', 'Deployment revision number')
|
|
32
|
+
.action(async (revision) => {
|
|
33
|
+
const project = await readProjectConfig();
|
|
34
|
+
if (!project)
|
|
35
|
+
throw new NotLinkedError();
|
|
36
|
+
const api = await ApiClient.create();
|
|
37
|
+
// Find deployment by revision
|
|
38
|
+
const deploymentsRes = await api.get(`/api/v1/static-sites/${project.siteId}/deployments`);
|
|
39
|
+
const deployment = deploymentsRes.data.find(d => d.revision_number === Number(revision));
|
|
40
|
+
if (!deployment) {
|
|
41
|
+
console.error(chalk.red(`Deployment revision ${revision} not found.`));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
const spinner = ora(`Rolling back to revision ${revision}...`).start();
|
|
45
|
+
await api.post(`/api/v1/static-sites/${project.siteId}/deployments/${deployment.id}/activate`);
|
|
46
|
+
spinner.succeed(`Rolled back to revision ${revision}`);
|
|
47
|
+
});
|
|
48
|
+
export const deploymentsCommand = new Command('deployments')
|
|
49
|
+
.description('Manage deployments')
|
|
50
|
+
.addCommand(lsCommand)
|
|
51
|
+
.addCommand(rollbackCommand);
|
|
52
|
+
//# sourceMappingURL=deployments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployments.js","sourceRoot":"","sources":["../../src/commands/deployments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { ApiClient } from '../lib/api-client.js';
|
|
5
|
+
import { readProjectConfig } from '../lib/project.js';
|
|
6
|
+
import { NotLinkedError } from '../lib/errors.js';
|
|
7
|
+
import { formatTable, statusColor } from '../lib/output.js';
|
|
8
|
+
const lsCommand = new Command('ls')
|
|
9
|
+
.description('List domains')
|
|
10
|
+
.action(async () => {
|
|
11
|
+
const project = await readProjectConfig();
|
|
12
|
+
if (!project)
|
|
13
|
+
throw new NotLinkedError();
|
|
14
|
+
const api = await ApiClient.create();
|
|
15
|
+
const res = await api.get(`/api/v1/static-sites/${project.siteId}/domains`);
|
|
16
|
+
if (res.data.length === 0) {
|
|
17
|
+
console.log('No domains configured.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const rows = res.data.map(d => [
|
|
21
|
+
d.domain,
|
|
22
|
+
d.type,
|
|
23
|
+
statusColor(d.status),
|
|
24
|
+
d.verified_at || '-',
|
|
25
|
+
]);
|
|
26
|
+
console.log(formatTable(['DOMAIN', 'TYPE', 'STATUS', 'VERIFIED'], rows));
|
|
27
|
+
});
|
|
28
|
+
const addCommand = new Command('add')
|
|
29
|
+
.description('Add a custom domain')
|
|
30
|
+
.argument('<domain>', 'Domain name to add')
|
|
31
|
+
.action(async (domain) => {
|
|
32
|
+
const project = await readProjectConfig();
|
|
33
|
+
if (!project)
|
|
34
|
+
throw new NotLinkedError();
|
|
35
|
+
const api = await ApiClient.create();
|
|
36
|
+
const spinner = ora(`Adding ${domain}...`).start();
|
|
37
|
+
const res = await api.post(`/api/v1/static-sites/${project.siteId}/domains`, { domain });
|
|
38
|
+
spinner.succeed(`Added ${chalk.bold(domain)}`);
|
|
39
|
+
if (res.data.verification_record) {
|
|
40
|
+
console.log(`\nAdd a CNAME record to verify ownership:`);
|
|
41
|
+
console.log(chalk.cyan(` ${res.data.verification_record}`));
|
|
42
|
+
console.log(`\nThen run: ${chalk.bold(`danube pages domains verify ${domain}`)}`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const removeCommand = new Command('remove')
|
|
46
|
+
.description('Remove a custom domain')
|
|
47
|
+
.argument('<domain>', 'Domain name to remove')
|
|
48
|
+
.action(async (domain) => {
|
|
49
|
+
const project = await readProjectConfig();
|
|
50
|
+
if (!project)
|
|
51
|
+
throw new NotLinkedError();
|
|
52
|
+
const api = await ApiClient.create();
|
|
53
|
+
// Find domain by name
|
|
54
|
+
const domainsRes = await api.get(`/api/v1/static-sites/${project.siteId}/domains`);
|
|
55
|
+
const domainObj = domainsRes.data.find(d => d.domain === domain);
|
|
56
|
+
if (!domainObj) {
|
|
57
|
+
console.error(chalk.red(`Domain ${domain} not found.`));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const spinner = ora(`Removing ${domain}...`).start();
|
|
61
|
+
await api.delete(`/api/v1/static-sites/${project.siteId}/domains/${domainObj.id}`);
|
|
62
|
+
spinner.succeed(`Removed ${domain}`);
|
|
63
|
+
});
|
|
64
|
+
const verifyCommand = new Command('verify')
|
|
65
|
+
.description('Verify a custom domain')
|
|
66
|
+
.argument('<domain>', 'Domain name to verify')
|
|
67
|
+
.action(async (domain) => {
|
|
68
|
+
const project = await readProjectConfig();
|
|
69
|
+
if (!project)
|
|
70
|
+
throw new NotLinkedError();
|
|
71
|
+
const api = await ApiClient.create();
|
|
72
|
+
// Find domain by name
|
|
73
|
+
const domainsRes = await api.get(`/api/v1/static-sites/${project.siteId}/domains`);
|
|
74
|
+
const domainObj = domainsRes.data.find(d => d.domain === domain);
|
|
75
|
+
if (!domainObj) {
|
|
76
|
+
console.error(chalk.red(`Domain ${domain} not found.`));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
const spinner = ora(`Verifying ${domain}...`).start();
|
|
80
|
+
await api.post(`/api/v1/static-sites/${project.siteId}/domains/${domainObj.id}/verify`);
|
|
81
|
+
spinner.succeed(`Verification started for ${chalk.bold(domain)}`);
|
|
82
|
+
});
|
|
83
|
+
export const domainsCommand = new Command('domains')
|
|
84
|
+
.description('Manage custom domains')
|
|
85
|
+
.addCommand(lsCommand)
|
|
86
|
+
.addCommand(addCommand)
|
|
87
|
+
.addCommand(removeCommand)
|
|
88
|
+
.addCommand(verifyCommand);
|
|
89
|
+
//# sourceMappingURL=domains.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domains.js","sourceRoot":"","sources":["../../src/commands/domains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG5D,MAAM,SAAS,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;KAChC,WAAW,CAAC,cAAc,CAAC;KAC3B,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAC1C,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,cAAc,EAAE,CAAC;IAEzC,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;IACrC,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CACvB,wBAAwB,OAAO,CAAC,MAAM,UAAU,CACjD,CAAC;IAEF,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,IAAI;QACN,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;QACrB,CAAC,CAAC,WAAW,IAAI,GAAG;KACrB,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC;AAEL,MAAM,UAAU,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC;KAClC,WAAW,CAAC,qBAAqB,CAAC;KAClC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;KAC1C,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;IAC/B,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAC1C,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,cAAc,EAAE,CAAC;IAEzC,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,MAAM,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;IAEnD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CACxB,wBAAwB,OAAO,CAAC,MAAM,UAAU,EAChD,EAAE,MAAM,EAAE,CACX,CAAC;IAEF,OAAO,CAAC,OAAO,CAAC,SAAS,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAE/C,IAAI,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,IAAI,CAAC,+BAA+B,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KACxC,WAAW,CAAC,wBAAwB,CAAC;KACrC,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAC;KAC7C,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;IAC/B,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAC1C,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,cAAc,EAAE,CAAC;IAEzC,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;IAErC,sBAAsB;IACtB,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,GAAG,CAC9B,wBAAwB,OAAO,CAAC,MAAM,UAAU,CACjD,CAAC;IAEF,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACjE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,MAAM,aAAa,CAAC,CAAC,CAAC;QACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,MAAM,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;IACrD,MAAM,GAAG,CAAC,MAAM,CACd,wBAAwB,OAAO,CAAC,MAAM,YAAY,SAAS,CAAC,EAAE,EAAE,CACjE,CAAC;IAEF,OAAO,CAAC,OAAO,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC;AAEL,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KACxC,WAAW,CAAC,wBAAwB,CAAC;KACrC,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAC;KAC7C,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;IAC/B,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAC1C,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,cAAc,EAAE,CAAC;IAEzC,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;IAErC,sBAAsB;IACtB,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,GAAG,CAC9B,wBAAwB,OAAO,CAAC,MAAM,UAAU,CACjD,CAAC;IAEF,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACjE,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,MAAM,aAAa,CAAC,CAAC,CAAC;QACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,MAAM,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;IACtD,MAAM,GAAG,CAAC,IAAI,CACZ,wBAAwB,OAAO,CAAC,MAAM,YAAY,SAAS,CAAC,EAAE,SAAS,CACxE,CAAC;IAEF,OAAO,CAAC,OAAO,CAAC,4BAA4B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACpE,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC;KACjD,WAAW,CAAC,uBAAuB,CAAC;KACpC,UAAU,CAAC,SAAS,CAAC;KACrB,UAAU,CAAC,UAAU,CAAC;KACtB,UAAU,CAAC,aAAa,CAAC;KACzB,UAAU,CAAC,aAAa,CAAC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { select, input } from '@inquirer/prompts';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { ApiClient } from '../lib/api-client.js';
|
|
5
|
+
import { writeProjectConfig } from '../lib/project.js';
|
|
6
|
+
export const linkCommand = new Command('link')
|
|
7
|
+
.description('Link current directory to a DanubeData static site')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
const api = await ApiClient.create();
|
|
10
|
+
// 1. Fetch teams
|
|
11
|
+
const teamsRes = await api.get('/api/v1/user/teams');
|
|
12
|
+
const teams = teamsRes.data;
|
|
13
|
+
let teamId;
|
|
14
|
+
if (teams.length === 1) {
|
|
15
|
+
teamId = teams[0].id;
|
|
16
|
+
console.log(`Team: ${chalk.bold(teams[0].name)}`);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
teamId = await select({
|
|
20
|
+
message: 'Select a team:',
|
|
21
|
+
choices: teams.map(t => ({ name: t.name, value: t.id })),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
// 2. Fetch existing sites
|
|
25
|
+
const sitesRes = await api.get(`/api/v1/teams/${teamId}/static-sites`);
|
|
26
|
+
const CREATE_NEW = -1;
|
|
27
|
+
const choices = [
|
|
28
|
+
...sitesRes.data.map(s => ({ name: `${s.name} (${s.default_domain})`, value: s.id })),
|
|
29
|
+
{ name: chalk.cyan('+ Create new site'), value: CREATE_NEW },
|
|
30
|
+
];
|
|
31
|
+
const siteChoice = await select({
|
|
32
|
+
message: 'Select a site to link:',
|
|
33
|
+
choices,
|
|
34
|
+
});
|
|
35
|
+
let site;
|
|
36
|
+
if (siteChoice === CREATE_NEW) {
|
|
37
|
+
const name = await input({
|
|
38
|
+
message: 'Site name:',
|
|
39
|
+
validate: (v) => v.trim().length > 0 || 'Name is required',
|
|
40
|
+
});
|
|
41
|
+
const res = await api.post(`/api/v1/teams/${teamId}/static-sites`, { name: name.trim() });
|
|
42
|
+
site = res.data;
|
|
43
|
+
console.log(chalk.green(`Created site: ${site.name}`));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
site = sitesRes.data.find(s => s.id === siteChoice);
|
|
47
|
+
}
|
|
48
|
+
// 3. Write project config
|
|
49
|
+
await writeProjectConfig({
|
|
50
|
+
siteId: site.id,
|
|
51
|
+
teamId: teamId,
|
|
52
|
+
siteName: site.name,
|
|
53
|
+
defaultDomain: site.default_domain,
|
|
54
|
+
});
|
|
55
|
+
console.log(chalk.green(`\nLinked to ${chalk.bold(site.name)} (${site.default_domain})`));
|
|
56
|
+
console.log(`Config saved to ${chalk.dim('.danube/project.json')}`);
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { password } from '@inquirer/prompts';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { writeConfig, getApiBase } from '../lib/config.js';
|
|
5
|
+
import { ApiError } from '../lib/errors.js';
|
|
6
|
+
export const loginCommand = new Command('login')
|
|
7
|
+
.description('Authenticate with DanubeData')
|
|
8
|
+
.option('--token <token>', 'API token (or paste interactively)')
|
|
9
|
+
.action(async (opts) => {
|
|
10
|
+
let token = opts.token;
|
|
11
|
+
if (!token) {
|
|
12
|
+
console.log(chalk.bold('Log in to DanubeData\n'));
|
|
13
|
+
console.log(`Create an API token at: ${chalk.cyan(`${getApiBase()}/user/api-tokens`)}\n`);
|
|
14
|
+
token = await password({
|
|
15
|
+
message: 'Paste your API token:',
|
|
16
|
+
mask: '*',
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
if (!token?.trim()) {
|
|
20
|
+
console.error(chalk.red('No token provided.'));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
token = token.trim();
|
|
24
|
+
// Validate token by calling /api/user
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(`${getApiBase()}/api/user`, {
|
|
27
|
+
headers: {
|
|
28
|
+
'Accept': 'application/json',
|
|
29
|
+
'Authorization': `Bearer ${token}`,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
if (!res.ok) {
|
|
33
|
+
if (res.status === 401) {
|
|
34
|
+
console.error(chalk.red('Invalid token.'));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
throw new ApiError(res.status, `Validation failed with status ${res.status}`);
|
|
38
|
+
}
|
|
39
|
+
const user = (await res.json());
|
|
40
|
+
await writeConfig({ token, apiBase: getApiBase() });
|
|
41
|
+
console.log(chalk.green(`\nAuthenticated as ${chalk.bold(user.name)} (${user.email})`));
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof ApiError)
|
|
45
|
+
throw err;
|
|
46
|
+
console.error(chalk.red('Failed to connect to DanubeData API.'));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,8BAA8B,CAAC;KAC3C,MAAM,CAAC,iBAAiB,EAAE,oCAAoC,CAAC;KAC/D,MAAM,CAAC,KAAK,EAAE,IAAwB,EAAE,EAAE;IACzC,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAEvB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAE1F,KAAK,GAAG,MAAM,QAAQ,CAAC;YACrB,OAAO,EAAE,uBAAuB;YAChC,IAAI,EAAE,GAAG;SACV,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAErB,sCAAsC;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,EAAE,WAAW,EAAE;YAClD,OAAO,EAAE;gBACP,QAAQ,EAAE,kBAAkB;gBAC5B,eAAe,EAAE,UAAU,KAAK,EAAE;aACnC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;gBAC3C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,iCAAiC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAS,CAAC;QACxC,MAAM,WAAW,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;QAEpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,sBAAsB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC1F,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ;YAAE,MAAM,GAAG,CAAC;QACvC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC,CAAC;QACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { deleteConfig } from '../lib/config.js';
|
|
4
|
+
export const logoutCommand = new Command('logout')
|
|
5
|
+
.description('Remove stored authentication')
|
|
6
|
+
.action(async () => {
|
|
7
|
+
await deleteConfig();
|
|
8
|
+
console.log(chalk.green('Logged out.'));
|
|
9
|
+
});
|
|
10
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,8BAA8B,CAAC;KAC3C,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,YAAY,EAAE,CAAC;IACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { ApiClient } from '../lib/api-client.js';
|
|
4
|
+
export const whoamiCommand = new Command('whoami')
|
|
5
|
+
.description('Show current authenticated user')
|
|
6
|
+
.action(async () => {
|
|
7
|
+
const api = await ApiClient.create();
|
|
8
|
+
const [user, teams] = await Promise.all([
|
|
9
|
+
api.get('/api/user'),
|
|
10
|
+
api.get('/api/v1/user/teams'),
|
|
11
|
+
]);
|
|
12
|
+
console.log(chalk.bold(user.name));
|
|
13
|
+
console.log(`Email: ${user.email}`);
|
|
14
|
+
console.log(`Teams: ${teams.data.map(t => t.name).join(', ')}`);
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=whoami.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGjD,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,iCAAiC,CAAC;KAC9C,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,MAAM,EAAE,CAAC;IAErC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACtC,GAAG,CAAC,GAAG,CAAO,WAAW,CAAC;QAC1B,GAAG,CAAC,GAAG,CAAgB,oBAAoB,CAAC;KAC7C,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { loginCommand } from './commands/login.js';
|
|
4
|
+
import { logoutCommand } from './commands/logout.js';
|
|
5
|
+
import { whoamiCommand } from './commands/whoami.js';
|
|
6
|
+
import { linkCommand } from './commands/link.js';
|
|
7
|
+
import { deployCommand } from './commands/deploy.js';
|
|
8
|
+
import { deploymentsCommand } from './commands/deployments.js';
|
|
9
|
+
import { domainsCommand } from './commands/domains.js';
|
|
10
|
+
import { NotAuthenticatedError, NotLinkedError, ApiError } from './lib/errors.js';
|
|
11
|
+
import { getCurrentVersion, checkForUpdate, printUpdateNotification } from './lib/version.js';
|
|
12
|
+
const program = new Command()
|
|
13
|
+
.name('danube')
|
|
14
|
+
.description('DanubeData CLI')
|
|
15
|
+
.version(getCurrentVersion());
|
|
16
|
+
program.addCommand(loginCommand);
|
|
17
|
+
program.addCommand(logoutCommand);
|
|
18
|
+
program.addCommand(whoamiCommand);
|
|
19
|
+
const pagesCommand = new Command('pages')
|
|
20
|
+
.description('Manage static sites');
|
|
21
|
+
pagesCommand.addCommand(linkCommand);
|
|
22
|
+
pagesCommand.addCommand(deployCommand);
|
|
23
|
+
pagesCommand.addCommand(deploymentsCommand);
|
|
24
|
+
pagesCommand.addCommand(domainsCommand);
|
|
25
|
+
program.addCommand(pagesCommand);
|
|
26
|
+
// Global error handler
|
|
27
|
+
program.hook('postAction', () => { });
|
|
28
|
+
process.on('unhandledRejection', (err) => {
|
|
29
|
+
if (err instanceof NotAuthenticatedError) {
|
|
30
|
+
console.error(chalk.red(err.message));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
if (err instanceof NotLinkedError) {
|
|
34
|
+
console.error(chalk.red(err.message));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
if (err instanceof ApiError) {
|
|
38
|
+
console.error(chalk.red(`API Error (${err.statusCode}): ${err.message}`));
|
|
39
|
+
if (err.errors) {
|
|
40
|
+
for (const [field, messages] of Object.entries(err.errors)) {
|
|
41
|
+
for (const msg of messages) {
|
|
42
|
+
console.error(chalk.red(` ${field}: ${msg}`));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
if (err instanceof Error) {
|
|
49
|
+
console.error(chalk.red(err.message));
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
console.error(chalk.red('An unexpected error occurred.'));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
});
|
|
55
|
+
program.parseAsync()
|
|
56
|
+
.then(async () => {
|
|
57
|
+
const result = await checkForUpdate();
|
|
58
|
+
if (result?.updateAvailable) {
|
|
59
|
+
printUpdateNotification(result.current, result.latest);
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
.catch((err) => {
|
|
63
|
+
if (err instanceof NotAuthenticatedError || err instanceof NotLinkedError) {
|
|
64
|
+
console.error(chalk.red(err.message));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
if (err instanceof ApiError) {
|
|
68
|
+
console.error(chalk.red(`API Error (${err.statusCode}): ${err.message}`));
|
|
69
|
+
if (err.errors) {
|
|
70
|
+
for (const [field, messages] of Object.entries(err.errors)) {
|
|
71
|
+
for (const msg of messages) {
|
|
72
|
+
console.error(chalk.red(` ${field}: ${msg}`));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
console.error(chalk.red(err instanceof Error ? err.message : 'An unexpected error occurred.'));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=index.js.map
|