0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-02-03 22:29:08 -05:00

improve link command (#10257)

This commit is contained in:
Fred K. Schott 2024-02-28 21:25:02 -08:00 committed by GitHub
parent 3df811a3bd
commit 2ecead463d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,36 +1,60 @@
import { mkdir, writeFile } from 'node:fs/promises';
import type { AstroConfig } from 'astro';
import { slug } from 'github-slugger';
import { bgRed, cyan } from 'kleur/colors';
import { mkdir, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { basename } from 'node:path';
import ora from 'ora';
import prompts from 'prompts';
import type { Arguments } from 'yargs-parser';
import { MISSING_SESSION_ID_ERROR } from '../../../errors.js';
import { PROJECT_ID_FILE, getSessionIdFromFile } from '../../../tokens.js';
import { getAstroStudioUrl } from '../../../utils.js';
export async function cmd({ flags }: { config: AstroConfig; flags: Arguments }) {
const linkUrl = new URL(getAstroStudioUrl() + '/auth/cli/link');
export async function cmd({}: { config: AstroConfig; flags: Arguments }) {
const sessionToken = await getSessionIdFromFile();
if (!sessionToken) {
console.error(MISSING_SESSION_ID_ERROR);
process.exit(1);
}
let body = { id: flags._[4] } as {
id?: string;
projectIdName?: string;
workspaceIdName?: string;
};
if (!body.id) {
const workspaceIdName = await promptWorkspaceName();
const projectIdName = await promptProjectName();
body = { projectIdName, workspaceIdName };
const getWorkspaceIdAsync = getWorkspaceId();
await promptBegin();
const isLinkExisting = await promptLinkExisting();
if (isLinkExisting) {
const workspaceId = await getWorkspaceIdAsync;
const existingProjectData = await promptExistingProjectName({workspaceId});
return await linkProject(existingProjectData.id);
}
const isLinkNew = await promptLinkNew();
if (isLinkNew) {
const workspaceId = await getWorkspaceIdAsync;
const newProjectName = await promptNewProjectName();
const newProjectRegion = await promptNewProjectRegion();
const spinner = ora('Creating new project...').start();
const newProjectData = await createNewProject({workspaceId, name: newProjectName, region: newProjectRegion});
// TODO(fks): Actually listen for project creation before continuing
// This is just a dumb spinner that roughly matches database creation time.
await new Promise((r) => setTimeout(r, 4000));
spinner.succeed('Project created!');
return await linkProject(newProjectData.id);
}
}
async function linkProject(id: string) {
await mkdir(new URL('.', PROJECT_ID_FILE), { recursive: true });
await writeFile(PROJECT_ID_FILE, `${id}`);
console.info('Project linked.');
}
async function getWorkspaceId(): Promise<string> {
const linkUrl = new URL(getAstroStudioUrl() + '/api/cli/workspaces.list');
const response = await fetch(linkUrl, {
method: 'POST',
headers: {
Authorization: `Bearer ${await getSessionIdFromFile()}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!response.ok) {
// Unauthorized
@ -42,38 +66,164 @@ export async function cmd({ flags }: { config: AstroConfig; flags: Arguments })
);
process.exit(1);
}
console.error(`Failed to link project: ${response.status} ${response.statusText}`);
console.error(`Failed to fetch user workspace: ${response.status} ${response.statusText}`);
process.exit(1);
}
const { data } = await response.json();
await mkdir(new URL('.', PROJECT_ID_FILE), { recursive: true });
await writeFile(PROJECT_ID_FILE, `${data.id}`);
console.info('Project linked.');
const { data, success } = await response.json() as {success: false, data: unknown} | {success: true, data: {id: string}[]};
if (!success) {
console.error(`Failed to fetch user's workspace.`);
process.exit(1);
}
return data[0].id;
}
export async function promptProjectName(defaultName?: string): Promise<string> {
const { projectName } = await prompts({
type: 'text',
name: 'projectName',
message: 'Project ID',
initial: defaultName,
export async function createNewProject({workspaceId, name, region}: {workspaceId: string; name: string, region: string}) {
const linkUrl = new URL(getAstroStudioUrl() + '/api/cli/projects.create');
const response = await fetch(linkUrl, {
method: 'POST',
headers: {
Authorization: `Bearer ${await getSessionIdFromFile()}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ workspaceId, name, region }),
});
if (typeof projectName !== 'string') {
process.exit(0);
if (!response.ok) {
// Unauthorized
if (response.status === 401) {
console.error(
`${bgRed('Unauthorized')}\n\n Are you logged in?\n Run ${cyan(
'astro db login'
)} to authenticate and then try linking again.\n\n`
);
process.exit(1);
}
console.error(`Failed to create project: ${response.status} ${response.statusText}`);
process.exit(1);
}
return projectName;
const { data, success } = await response.json() as {success: false, data: unknown} | {success: true, data: {id: string; idName: string}};
if (!success) {
console.error(`Failed to create project.`);
process.exit(1);
}
return {id: data.id, idName: data.idName};
}
export async function promptWorkspaceName(defaultName?: string): Promise<string> {
const { workspaceName } = await prompts({
type: 'text',
name: 'workspaceName',
message: 'Workspace ID',
initial: defaultName,
export async function promptExistingProjectName({workspaceId}: {workspaceId: string}) {
const linkUrl = new URL(getAstroStudioUrl() + '/api/cli/projects.list');
const response = await fetch(linkUrl, {
method: 'POST',
headers: {
Authorization: `Bearer ${await getSessionIdFromFile()}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({workspaceId}),
});
if (typeof workspaceName !== 'string') {
if (!response.ok) {
// Unauthorized
if (response.status === 401) {
console.error(
`${bgRed('Unauthorized')}\n\n Are you logged in?\n Run ${cyan(
'astro db login'
)} to authenticate and then try linking again.\n\n`
);
process.exit(1);
}
console.error(`Failed to fetch projects: ${response.status} ${response.statusText}`);
process.exit(1);
}
const { data, success } = await response.json() as {success: false, data: unknown} | {success: true, data: {id: string; idName: string}[]};
if (!success) {
console.error(`Failed to fetch projects.`);
process.exit(1);
}
const { projectId } = await prompts({
type: 'autocomplete',
name: 'projectId',
message: 'What is your project name?',
limit: 5,
choices: data.map((p: any) => ({title: p.name, value: p.id})),
});
if (typeof projectId !== 'string') {
console.log('Canceled.')
process.exit(0);
}
return workspaceName;
const selectedProjectData = data.find((p: any) => p.id === projectId)!;
return selectedProjectData;
}
export async function promptBegin(): Promise<void> {
// Get the current working directory relative to the user's home directory
const prettyCwd = process.cwd().replace(homedir(), '~');
// prompt
const { begin } = await prompts({
type: 'confirm',
name: 'begin',
message: `Link "${prettyCwd}" with Astro Studio?`,
initial: true,
});
if (!begin) {
console.log('Canceled.')
process.exit(0);
};
}
export async function promptLinkExisting(): Promise<boolean> {
// prompt
const { linkExisting } = await prompts({
type: 'confirm',
name: 'linkExisting',
message: `Link with an existing project in Astro Studio?`,
initial: true,
});
return !!linkExisting;
}
export async function promptLinkNew(): Promise<boolean> {
// prompt
const { linkNew } = await prompts({
type: 'confirm',
name: 'linkNew',
message: `Create a new project in Astro Studio?`,
initial: true,
});
if (!linkNew) {
console.log('Canceled.')
process.exit(0);
};
return true;
}
export async function promptNewProjectName(): Promise<string> {
const { newProjectName } = await prompts({
type: 'text',
name: 'newProjectName',
message: `What is your new project's name?`,
initial: basename(process.cwd()),
format: (val) => slug(val),
});
if (!newProjectName) {
console.log('Canceled.')
process.exit(0);
};
return newProjectName;
}
export async function promptNewProjectRegion(): Promise<string> {
const { newProjectRegion } = await prompts({
type: 'select',
name: 'newProjectRegion',
message: `Where should your new database live?`,
choices: [
{title: 'North America (East)', value: 'NorthAmericaEast'},
{title: 'North America (West)', value: 'NorthAmericaWest'}
],
initial: 0,
});
if (!newProjectRegion) {
console.log('Canceled.')
process.exit(0);
};
return newProjectRegion;
}