Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ static-studio backups create <siteId>
static-studio backups restore <siteId> --backup-id <backupId>

static-studio redirects create <siteId> /old /new
static-studio redirects update <siteId> <redirectId> --from-path /old --to-path /new
static-studio redirects disable <siteId> <redirectId>
static-studio redirects enable <siteId> <redirectId>
static-studio redirects bulk-create <siteId> redirects.json

static-studio users list <siteId>
Expand Down
46 changes: 43 additions & 3 deletions docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@ List redirect rules.
static-studio redirects list <siteId> [--pull-zone-id <id>]
```

Use DB-backed redirect IDs from the `dbRedirects` response for update, enable, disable, and DB delete operations.

### `redirects create`

Create a 301 redirect.
Expand All @@ -485,15 +487,53 @@ static-studio redirects create <siteId> <fromPath> <toPath> [--pull-zone-id <id>

By default, the pull zone and domain are resolved from the site.

### `redirects update`

Update a DB-backed redirect. `redirects edit` is an alias.

```bash
static-studio redirects update <siteId> <redirectId> [--from-path <path>] [--to-path <path>] [--active|--inactive]
```

At least one field is required. Default redirects can only be enabled or disabled.

### `redirects enable`

Enable a DB-backed redirect.

```bash
static-studio redirects enable <siteId> <redirectId>
```

### `redirects disable`

Disable a DB-backed redirect.

```bash
static-studio redirects disable <siteId> <redirectId>
```

### `redirects delete`

Disable a redirect edge rule.
Delete a DB-backed redirect, or disable a legacy CDN edge rule.

```bash
static-studio redirects delete <siteId> <redirectId> [--db] [--edge-rule] [--pull-zone-id <id>]
```

By default, the CLI treats the ID as a DB redirect ID and falls back to a legacy edge rule when the DB redirect is not found. Use `--edge-rule` to force the legacy CDN path.

After deleting or disabling the rule, the CLI refreshes edge rules for the site.

### `redirects refresh`

Refresh redirect edge rules from stored redirects.

```bash
static-studio redirects delete <siteId> <ruleId> [--pull-zone-id <id>]
static-studio redirects refresh <siteId> [--skip-import-existing]
```

After disabling the rule, the CLI refreshes edge rules for the site.
By default, existing Studio-owned CDN redirects are imported before the refresh so older redirects can be managed through DB-backed commands. Use `--skip-import-existing` after out-of-band cleanup when you do not want CDN rules imported.

### `redirects bulk-create`

Expand Down
11 changes: 11 additions & 0 deletions docs/input-formats-and-limits.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ Rules:

By default, the CLI resolves `pullZoneId` and redirect domain from the site. Use `--pull-zone-id` or `--domain` to override them.

## Redirect Updates

`redirects update` accepts `--from-path`, `--to-path`, `--active`, and `--inactive`.

Rules:

- At least one update field must be provided.
- `--active` and `--inactive` are mutually exclusive.
- `--from-path` and `--to-path` are trimmed, cannot be empty, and must be 2048 characters or fewer.
- Default redirects can only be enabled or disabled.

## Team Email Files

`team invite --file <path>` and `team bulk-invite <file>` accept files up to 64 KiB.
Expand Down
16 changes: 15 additions & 1 deletion docs/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,21 @@ List and remove redirect rules:

```bash
static-studio redirects list <siteId>
static-studio redirects delete <siteId> <ruleId>
static-studio redirects delete <siteId> <redirectId>
```

Edit and toggle DB-backed redirects:

```bash
static-studio redirects update <siteId> <redirectId> --from-path /old-page --to-path /new-page
static-studio redirects disable <siteId> <redirectId>
static-studio redirects enable <siteId> <redirectId>
```

Import older Studio-owned CDN redirect rules so they can be managed by ID:

```bash
static-studio redirects refresh <siteId>
```

## Users and Team Members
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@simply-static/studio-cli",
"version": "0.1.2",
"version": "0.1.3",
"description": "Command-line interface for Static Studio hosting workflows.",
"license": "MIT",
"type": "module",
Expand Down
80 changes: 75 additions & 5 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ import { printValue } from "./output.js";
import { getSiteStatistics, listPerformanceReports, runPerformanceTest } from "./performance.js";
import { confirm } from "./prompt.js";
import { deleteSshKey, getSshInfo, listBackups, queueBackup, queueSshKey } from "./queues.js";
import { bulkCreateRedirects, createRedirect, deleteRedirect, listRedirects } from "./redirects.js";
import {
bulkCreateRedirects,
createRedirect,
deleteRedirect,
listRedirects,
refreshRedirectRules,
setRedirectActive,
updateRedirect,
} from "./redirects.js";
import {
clearCache,
createSite,
Expand Down Expand Up @@ -88,6 +96,19 @@ function debugLogOptions(opts: ParsedOptions) {
};
}

function redirectUpdateOptions(opts: ParsedOptions): { fromPath?: string; toPath?: string; isActive?: boolean } {
if (opts.active && opts.inactive) {
throw new CliError("Use only one of --active or --inactive.");
}

return {
...(opts.fromPath !== undefined ? { fromPath: opts.fromPath } : {}),
...(opts.toPath !== undefined ? { toPath: opts.toPath } : {}),
...(opts.active ? { isActive: true } : {}),
...(opts.inactive ? { isActive: false } : {}),
};
}

function printDebugLogResult(cmd: Command, result: Awaited<ReturnType<typeof getDebugLog>>, outputFile?: string): void {
if (globals(cmd).json) {
print(cmd, result);
Expand Down Expand Up @@ -725,12 +746,61 @@ redirects
});

redirects
.command("delete <siteId> <ruleId>")
.description("disable a redirect edge rule")
.command("update <siteId> <redirectId>")
.alias("edit")
.description("update a DB-backed redirect")
.option("--from-path <path>", "new source path")
.option("--to-path <path>", "new target path")
.option("--active", "enable the redirect")
.option("--inactive", "disable the redirect")
.action(async (siteId: string, redirectId: string, opts: ParsedOptions, cmd: Command) => {
await withAuth(cmd, async ({ supabase }) => {
print(cmd, await updateRedirect(supabase, siteId, redirectId, redirectUpdateOptions(opts)));
});
});

redirects
.command("enable <siteId> <redirectId>")
.description("enable a DB-backed redirect")
.action(async (siteId: string, redirectId: string, _localOpts: ParsedOptions, cmd: Command) => {
await withAuth(cmd, async ({ supabase }) => {
print(cmd, await setRedirectActive(supabase, siteId, redirectId, true));
});
});

redirects
.command("disable <siteId> <redirectId>")
.description("disable a DB-backed redirect")
.action(async (siteId: string, redirectId: string, _localOpts: ParsedOptions, cmd: Command) => {
await withAuth(cmd, async ({ supabase }) => {
print(cmd, await setRedirectActive(supabase, siteId, redirectId, false));
});
});

redirects
.command("delete <siteId> <redirectId>")
.description("delete a DB-backed redirect or disable a legacy edge rule")
.option("--pull-zone-id <id>", "override pull zone ID")
.action(async (siteId: string, ruleId: string, opts: ParsedOptions, cmd: Command) => {
.option("--db", "treat redirectId as a DB redirect ID")
.option("--edge-rule", "treat redirectId as a legacy CDN edge rule ID")
.action(async (siteId: string, redirectId: string, opts: ParsedOptions, cmd: Command) => {
await withAuth(cmd, async ({ supabase }) => {
print(cmd, await deleteRedirect(supabase, siteId, redirectId, opts));
});
});

redirects
.command("refresh <siteId>")
.description("refresh redirect edge rules from stored redirects")
.option("--skip-import-existing", "do not import existing Studio-owned CDN redirects before refresh")
.action(async (siteId: string, opts: ParsedOptions, cmd: Command) => {
await withAuth(cmd, async ({ supabase }) => {
print(cmd, await deleteRedirect(supabase, siteId, ruleId, opts.pullZoneId));
print(
cmd,
await refreshRedirectRules(supabase, siteId, {
importExistingRedirects: !opts.skipImportExisting,
}),
);
});
});

Expand Down
Loading
Loading