-
Notifications
You must be signed in to change notification settings - Fork 435
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
355 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import {randomBytes} from 'node:crypto' | ||
|
||
import {debug} from '../../debug' | ||
import {type CliCommandArguments, type CliCommandContext} from '../../types' | ||
import {API_VERSION, fetchRoles, selectProject} from './tokenUtils' | ||
|
||
export async function createToken( | ||
args: CliCommandArguments, | ||
context: CliCommandContext, | ||
): Promise<void> { | ||
const {output, cliConfig, prompt, apiClient} = context | ||
const {print} = output | ||
const client = apiClient({requireUser: true, requireProject: false}) | ||
|
||
const projectId = cliConfig?.api?.projectId || (await selectProject(client, prompt)) | ||
const roles = await fetchRoles(client, projectId) | ||
|
||
debug('Filtering roles that apply to robots') | ||
const robotRoles = roles.filter((role) => role.appliesToRobots) | ||
const roleChoices = robotRoles.map((role) => ({ | ||
value: role.name, | ||
name: `${role.title} (${role.name})`, | ||
})) | ||
|
||
debug('Prompting for token role') | ||
const roleName = await prompt.single({ | ||
message: 'Choose access level for the token:', | ||
type: 'list', | ||
choices: roleChoices, | ||
default: 'viewer', | ||
}) | ||
|
||
debug('Prompting for token label') | ||
const selectedRole = robotRoles.find((role) => role.name === roleName) | ||
const label = await prompt.single({ | ||
message: 'Give this token a descriptive name:', | ||
type: 'input', | ||
default: `${selectedRole?.title} token (${randomBytes(2).toString('hex')})`, | ||
}) | ||
|
||
debug('Creating token') | ||
const {key} = await client.config({apiVersion: API_VERSION}).request<{key: string}>({ | ||
uri: `/projects/${projectId}/tokens`, | ||
method: 'POST', | ||
body: {label, roleName}, | ||
}) | ||
|
||
print("New token created. Make sure to copy it now - you won't see it again:") | ||
print('') | ||
print(key) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import {debug} from '../../debug' | ||
import {type CliCommandArguments, type CliCommandContext} from '../../types' | ||
import {fetchTokens, selectProject} from './tokenUtils' | ||
|
||
export async function deleteToken( | ||
args: CliCommandArguments, | ||
context: CliCommandContext, | ||
): Promise<void> { | ||
const {output, cliConfig, prompt, apiClient} = context | ||
const {print} = output | ||
const client = apiClient({requireUser: true, requireProject: false}) | ||
|
||
const projectId = cliConfig?.api?.projectId || (await selectProject(client, prompt)) | ||
let tokenId = args.argsWithoutOptions[0] | ||
|
||
if (!tokenId) { | ||
const tokens = await fetchTokens(client, projectId) | ||
|
||
if (tokens.length === 0) { | ||
print('No tokens found in project') | ||
return | ||
} | ||
|
||
tokens.sort((a, b) => b.createdAt.localeCompare(a.createdAt)) | ||
|
||
debug('No token ID provided, showing list of choices') | ||
const tokenChoices = tokens.map((token) => ({ | ||
value: token.id, | ||
name: `${token.label} (created ${new Date(token.createdAt).toLocaleString()})`, | ||
})) | ||
|
||
tokenId = await prompt.single({ | ||
message: 'Select token to delete', | ||
type: 'list', | ||
choices: tokenChoices, | ||
}) | ||
} | ||
|
||
const confirm = await prompt.single({ | ||
message: 'Are you sure you want to delete this token?', | ||
type: 'confirm', | ||
}) | ||
|
||
if (!confirm) { | ||
print('Token deletion cancelled') | ||
return | ||
} | ||
|
||
debug('Deleting token:', tokenId) | ||
await client.config({apiVersion: 'v2021-06-07'}).request({ | ||
uri: `/projects/${projectId}/tokens/${tokenId}`, | ||
method: 'DELETE', | ||
}) | ||
|
||
print('Token deleted successfully') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import Table from 'cli-table3' | ||
|
||
import {type CliCommandArguments, type CliCommandContext} from '../../types' | ||
import {fetchTokens, selectProject} from './tokenUtils' | ||
|
||
export async function listTokens( | ||
args: CliCommandArguments, | ||
context: CliCommandContext, | ||
): Promise<void> { | ||
const {output, cliConfig, prompt, apiClient} = context | ||
const {print} = output | ||
const client = apiClient({requireUser: true, requireProject: false}) | ||
|
||
const projectId = cliConfig?.api?.projectId || (await selectProject(client, prompt)) | ||
const tokens = await fetchTokens(client, projectId) | ||
|
||
if (tokens.length === 0) { | ||
print('No tokens found in project') | ||
return | ||
} | ||
|
||
const sortedTokens = tokens.sort( | ||
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), | ||
) | ||
|
||
const table = new Table({ | ||
head: ['ID', 'Label', 'Roles', 'Created'], | ||
colWidths: [16, 32, 18, 14], | ||
style: {head: []}, // Remove the default header style | ||
}) | ||
|
||
for (const token of sortedTokens) { | ||
const roles = token.roles.map((role) => role.title).join(', ') | ||
const date = new Date(token.createdAt).toLocaleDateString() | ||
table.push([token.id, token.label, roles, date]) | ||
} | ||
|
||
print(table.toString()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import {type SanityClient} from '@sanity/client' | ||
|
||
import {debug} from '../../debug' | ||
import {type CliPrompter} from '../../types' | ||
|
||
export const API_VERSION = 'v2021-06-07' | ||
|
||
export type Token = { | ||
id: string | ||
label: string | ||
projectUserId: string | ||
createdAt: string | ||
roles: Array<{ | ||
name: string | ||
title: string | ||
}> | ||
} | ||
|
||
export type Role = { | ||
name: string | ||
title: string | ||
description: string | ||
isCustom: boolean | ||
projectId: string | ||
appliesToUsers: boolean | ||
appliesToRobots: boolean | ||
} | ||
|
||
export async function selectProject(client: SanityClient, prompt: CliPrompter): Promise<string> { | ||
debug('No project ID in config, fetching projects list') | ||
const projects = await client.projects | ||
.list({includeMembers: false}) | ||
.then((allProjects) => allProjects.sort((a, b) => b.createdAt.localeCompare(a.createdAt))) | ||
|
||
debug(`User has ${projects.length} project(s) already, showing list of choices`) | ||
|
||
const projectChoices = projects.map((project) => ({ | ||
value: project.id, | ||
name: `${project.displayName} [${project.id}]`, | ||
})) | ||
|
||
return prompt.single({ | ||
message: 'Select project to use', | ||
type: 'list', | ||
choices: projectChoices, | ||
}) | ||
} | ||
|
||
export async function fetchTokens(client: SanityClient, projectId: string): Promise<Token[]> { | ||
debug('Fetching tokens for project:', projectId) | ||
return client.config({apiVersion: API_VERSION}).request<Token[]>({ | ||
uri: `/projects/${projectId}/tokens`, | ||
method: 'GET', | ||
}) | ||
} | ||
|
||
export async function fetchRoles(client: SanityClient, projectId: string): Promise<Role[]> { | ||
debug('Fetching roles for project:', projectId) | ||
return client.config({apiVersion: API_VERSION}).request<Role[]>({ | ||
uri: `/projects/${projectId}/roles`, | ||
method: 'GET', | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
packages/@sanity/cli/src/commands/token/tokenCreateCommand.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import {createToken} from '../../actions/token/createToken' | ||
import {type CliCommandDefinition} from '../../types' | ||
|
||
const tokenCommand: CliCommandDefinition = { | ||
name: 'create', | ||
group: 'token', | ||
signature: '', | ||
helpText: 'Create a new API token for project', | ||
description: 'Create a new API token for project', | ||
action: createToken, | ||
} | ||
|
||
export default tokenCommand |
13 changes: 13 additions & 0 deletions
13
packages/@sanity/cli/src/commands/token/tokenDeleteCommand.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import {deleteToken} from '../../actions/token/deleteToken' | ||
import {type CliCommandDefinition} from '../../types' | ||
|
||
const tokenDeleteCommand: CliCommandDefinition = { | ||
name: 'delete', | ||
group: 'token', | ||
signature: '[id]', | ||
helpText: 'Delete an API token from project', | ||
description: 'Delete an API token from project', | ||
action: deleteToken, | ||
} | ||
|
||
export default tokenDeleteCommand |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import {type CliCommandGroupDefinition} from '../../types' | ||
|
||
const tokenGroup: CliCommandGroupDefinition = { | ||
name: 'token', | ||
signature: '[COMMAND]', | ||
isGroupRoot: true, | ||
description: 'Manages project API tokens', | ||
} | ||
|
||
export default tokenGroup |
24 changes: 24 additions & 0 deletions
24
packages/@sanity/cli/src/commands/token/tokenListCommand.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import {listTokens} from '../../actions/token/listTokens' | ||
import {type CliCommandDefinition} from '../../types' | ||
|
||
type Token = { | ||
id: string | ||
label: string | ||
projectUserId: string | ||
createdAt: string | ||
roles: Array<{ | ||
name: string | ||
title: string | ||
}> | ||
} | ||
|
||
const tokenListCommand: CliCommandDefinition = { | ||
name: 'list', | ||
group: 'token', | ||
signature: '', | ||
helpText: 'List all API tokens in project', | ||
description: 'List all API tokens in project', | ||
action: listTokens, | ||
} | ||
|
||
export default tokenListCommand |
Oops, something went wrong.