toystack
Version:

216 lines (194 loc) • 5.51 kB
text/typescript
import { ApolloClient, HttpLink, InMemoryCache, gql } from '@apollo/client';
import { outro } from '@clack/prompts';
import axios from 'axios';
import dotenv from 'dotenv';
import FormData from 'form-data';
import { createReadStream } from 'fs';
import process from 'process';
import { checkAuth, checkForToken } from '..';
dotenv.config();
const API_BASE_URL = process.env.API_BASE_URL || 'https://core.toystack.ai';
const listAllRepositories = async () => {
const token = await checkAuth();
const client = new ApolloClient({
link: new HttpLink({
uri: `${API_BASE_URL}/graphql`,
fetch,
headers: { Authorization: `Bearer ${token}` },
}),
cache: new InMemoryCache(),
});
const { data } = await client.query({
query: gql`
query {
me {
repositories {
nodes {
id
name
}
}
}
}
`,
});
return data.me.repositories.nodes;
};
const deployBranch = async (repositoryName: string, branchName: string) => {
const token = await checkAuth();
const client = new ApolloClient({
link: new HttpLink({
uri: `${API_BASE_URL}/graphql`,
fetch,
headers: { Authorization: `Bearer ${token}` },
}),
cache: new InMemoryCache(),
});
const { data } = await client.mutate({
mutation: gql`
mutation DeployBranchViaCLI($repositoryName: String!, $branchName: String!) {
deployBranchViaCLI(repositoryName: $repositoryName, branchName: $branchName) {
id
}
}
`,
variables: {
repositoryName,
branchName,
},
});
return data;
};
const uploadUserCode = async (filePath: string, repositoryName?: string, branchName?: string) => {
const data = new FormData();
const operations = JSON.stringify({
query: `
mutation UploadUserCode($file: Upload!, $repositoryName: String, $branchName: String) {
uploadUserCode(file: $file, repositoryName: $repositoryName, branchName: $branchName) {
deploymentId
}
}
`,
variables: {
file: null,
repositoryName: repositoryName || null,
branchName: branchName || null,
},
});
const map = {
'0': ['variables.file'],
};
data.append('operations', operations);
data.append('map', JSON.stringify(map));
data.append('0', createReadStream(filePath));
if (repositoryName) data.append('1', repositoryName);
if (branchName) data.append('2', branchName);
const token = await checkForToken();
const config = {
method: 'post',
url: `${API_BASE_URL}/graphql`,
headers: {
'apollo-require-preflight': 'true',
...(token ? { authorization: `Bearer ${token}` } : {}),
...data.getHeaders(),
},
data: data,
};
try {
const response = await axios.request(config);
const { data } = response;
const { uploadUserCode } = data.data;
return {
deploymentId: uploadUserCode.deploymentId,
filename: uploadUserCode.filename,
status: uploadUserCode.status,
};
} catch (error) {
console.error('Upload failed:', error);
throw new Error('Upload failed');
}
};
const createCLIFrontendDeployment = async (zipFileName: string) => {
await delay(5000);
const client = new ApolloClient({
link: new HttpLink({
uri: `${API_BASE_URL}/graphql`,
fetch,
}),
cache: new InMemoryCache(),
});
const { data } = await client.mutate({
mutation: gql`
mutation CreateCLIFrontendDeployment($zipFileName: String!) {
createCLIFrontendDeployment(zipFileName: $zipFileName) {
id
url
timeToLive
}
}
`,
variables: {
zipFileName,
},
});
return data.createCLIFrontendDeployment;
};
async function pullForDeploymentStatus(deploymentId: string) {
const pollInterval = 4000;
let isPolling = true;
// Handle SIGINT (Ctrl+C)
process.on('SIGINT', () => {
isPolling = false;
outro('Deployment cancelled');
process.exit(0);
});
// Handle process termination
process.on('SIGTERM', () => {
isPolling = false;
outro('Deployment cancelled');
process.exit(0);
});
try {
const token = await checkAuth();
const client = new ApolloClient({
link: new HttpLink({
uri: `${API_BASE_URL}/graphql`,
fetch,
headers: { Authorization: `Bearer ${token}` },
}),
cache: new InMemoryCache(),
});
while (isPolling) {
try {
await delay(pollInterval);
const { data } = await client.query({
query: gql`
query Deployment($deploymentId: ID!) {
deployment(id: $deploymentId) {
url
id
status
}
}
`,
variables: { deploymentId },
fetchPolicy: 'network-only',
});
if (data.deployment.url) {
return { url: data.deployment.url, status: data.deployment.status };
} else if (data.deployment.status === 'FAILED') {
return { url: null, status: data.deployment.status };
}
} catch (error) {
return null;
}
}
} catch (error) {
return null;
} finally {
process.removeAllListeners('SIGINT');
process.removeAllListeners('SIGTERM');
}
}
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
export { listAllRepositories, deployBranch, uploadUserCode, createCLIFrontendDeployment, pullForDeploymentStatus };