Skip to content

Commit a84ddd9

Browse files
authored
Feature/eng 866 integrate sites apis with cli sites commands (#94)
1 parent 18885e7 commit a84ddd9

11 files changed

Lines changed: 374 additions & 17 deletions

File tree

src/commands/function/delete.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const deleteFunction = async (data: any) => {
4242
console.log(Chalk.yellow(`Deleting ${functionName} ...`))
4343
console.log('')
4444

45-
const { data } = await consoleClient.get(`/api/modules/mine`, {})
45+
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
4646
const functions = data.docs ? data.docs : []
4747

4848
// Sort all matching functions by name and select the last matching function

src/commands/function/deploy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const deployFunction = async (functionName: string, functionData: any, options:
6565

6666
// Find all matching functions, warn users if they are overwriting a deployed function
6767
try {
68-
const { data } = await consoleClient.get(`/api/modules/mine`, {})
68+
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
6969
const functions = data.docs ? data.docs : []
7070

7171
// Sort all matching functions by name and select the last matching function

src/commands/function/list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { logger } from "../../lib/logger"
44

55
export const run = async () => {
66
try {
7-
const { data } = await consoleClient.get(`/api/modules/mine`, {})
7+
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
88
const functions = data.docs ? data.docs : []
99

1010
logger.log('List of Functions:')

src/commands/function/stop.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const stopFunction = async (data: any) => {
4242
console.log(Chalk.yellow(`Stopping ${functionName} ...`))
4343
console.log('')
4444

45-
const { data } = await consoleClient.get(`/api/modules/mine`, {})
45+
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
4646
const functions = data.docs ? data.docs : []
4747

4848
// Sort all matching functions by name and select the last matching function

src/commands/function/update.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const updateFunction = async (data: any) => {
6464

6565
// Find all matching functions, warn users if they are updating a function that is not deployed
6666
try {
67-
const { data } = await consoleClient.get(`/api/modules/mine`, {})
67+
const { data } = await consoleClient.get(`/api/modules/mine?limit=999`, {})
6868
const functions = data.docs ? data.docs : []
6969

7070
// Sort all matching functions by name and select the last matching function

src/commands/sites/build.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,4 @@ export const run = async (options: {
6969
logger.error('Failed to build site.', error.message)
7070
return
7171
}
72-
}
73-
74-
const dynamicImport = async (path: string) => {
75-
console.log('dynamic import')
76-
77-
try {
78-
const module = await import(path)
79-
return module.default
80-
} catch (err) {
81-
return console.error(err)
82-
}
83-
};
72+
}

src/commands/sites/delete.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import Chalk from "chalk"
2+
import { parseBlsConfig } from "../../lib/blsConfig"
3+
import { consoleClient } from "../../lib/http"
4+
import { logger } from "../../lib/logger"
5+
import { normalizeFunctionName } from "../../lib/strings"
6+
7+
interface DeleteCommandOptions {
8+
target: string
9+
}
10+
11+
/**
12+
* Entry function for bls site delete
13+
*
14+
* @param options
15+
*/
16+
export const run = async (options: DeleteCommandOptions) => {
17+
try {
18+
if (options.target) {
19+
await deleteSite({ name: options.target })
20+
} else {
21+
const { name: configName } = parseBlsConfig()
22+
await deleteSite({ name: configName })
23+
}
24+
} catch (error: any) {
25+
logger.error('Failed to delete site.', error.message)
26+
}
27+
}
28+
29+
/**
30+
*
31+
* @param data
32+
* @returns
33+
*/
34+
const deleteSite = async (data: any) => {
35+
const { name: functionName } = data
36+
37+
let matchingFunction = null
38+
let internalFunctionId = null
39+
40+
// Find all matching functions, warn users if they are updating a function that is not deployed
41+
try {
42+
console.log(Chalk.yellow(`Deleting ${functionName} ...`))
43+
console.log('')
44+
45+
const { data } = await consoleClient.get(`/api/sites?limit=999`, {})
46+
const functions = data.docs ? data.docs : []
47+
48+
// Sort all matching functions by name and select the last matching function
49+
// TODO: Ensure all functions have unique names under a user's scope
50+
const matchingFunctions = functions.filter((f: any) =>
51+
normalizeFunctionName(f.functionName) === normalizeFunctionName(functionName))
52+
53+
if (matchingFunctions && matchingFunctions.length > 0) {
54+
matchingFunction = matchingFunctions[matchingFunctions.length - 1]
55+
internalFunctionId = matchingFunction._id
56+
}
57+
58+
// If a function does not exist, request the user to deploy that function first
59+
if (!matchingFunction) {
60+
throw new Error('Site not found.')
61+
}
62+
} catch (error: any) {
63+
logger.error('Failed to retrive deployed sites.', error.message)
64+
return
65+
}
66+
67+
// Delete the site
68+
try {
69+
if (!internalFunctionId || !matchingFunction)
70+
throw new Error('Unable to retrive site ID.')
71+
72+
const { data } = await consoleClient.delete(`/api/sites/${internalFunctionId}`)
73+
74+
if (!data) throw new Error("")
75+
76+
console.log(
77+
Chalk.green(
78+
`Successfully deleted site ${functionName}!`
79+
)
80+
)
81+
} catch (error: any) {
82+
logger.error('Failed to delete site.', error.message)
83+
return
84+
}
85+
}

src/commands/sites/deploy.ts

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import Chalk from "chalk"
2+
import { run as runPublish } from "./publish"
3+
import { basename, resolve } from "path"
4+
import { consoleClient } from "../../lib/http"
5+
import promptFnDeploy from "../../prompts/function/deploy"
6+
import { parseBlsConfig } from "../../lib/blsConfig"
7+
import { logger } from "../../lib/logger"
8+
import { normalizeFunctionName, slugify } from "../../lib/strings"
9+
10+
interface DeployCommandOptions {
11+
name?: string
12+
path?: string
13+
yes?: boolean
14+
}
15+
16+
/**
17+
* Entry function for bls function deploy
18+
*
19+
* @param options
20+
*/
21+
export const run = (options: DeployCommandOptions) => {
22+
try {
23+
const {
24+
name: configName
25+
} = parseBlsConfig()
26+
27+
const {
28+
name = configName || basename(resolve(process.cwd())),
29+
path = process.cwd()
30+
} = options
31+
32+
runPublish({
33+
debug: false,
34+
name,
35+
path,
36+
publishCallback: (data: any) => deployFunction(slugify(name), data, options),
37+
rebuild: true,
38+
})
39+
} catch (error: any) {
40+
logger.error('Failed to deploy site.', error.message)
41+
}
42+
}
43+
44+
/**
45+
* Helper to deploy a bls site via CLI
46+
*
47+
* 1. Publish package and retrive ipfs site id
48+
* 2. Get list of user sites
49+
* 3. Decide whether to update or create a new site (based on site name), bail if deploying same data
50+
* 4. Call new or update API with site config parameters
51+
* 5. Run deploy
52+
*
53+
* @param data
54+
* @returns
55+
*/
56+
const deployFunction = async (functionName: string, functionData: any, options: DeployCommandOptions) => {
57+
const { cid: functionId } = functionData
58+
let matchingFunction = null
59+
let internalFunctionId = null
60+
61+
// Find all matching functions, warn users if they are overwriting a deployed function
62+
try {
63+
const { data } = await consoleClient.get(`/api/sites?limit=999`, {})
64+
const functions = data.docs ? data.docs : []
65+
66+
// Sort all matching functions by name and select the last matching function
67+
// TODO: Ensure all functions have unique names under a user's scope
68+
const matchingFunctions = functions.filter((f: any) =>
69+
normalizeFunctionName(f.functionName) === normalizeFunctionName(functionName))
70+
71+
if (matchingFunctions && matchingFunctions.length > 0) {
72+
matchingFunction = matchingFunctions[matchingFunctions.length - 1]
73+
internalFunctionId = matchingFunction._id
74+
}
75+
76+
// If a function exists and has been deployed, request a user's confirmation
77+
if (matchingFunction && matchingFunction.status === 'deployed' && !options.yes) {
78+
const { confirm } = await promptFnDeploy({ name: matchingFunction.functionName })
79+
80+
if (!confirm) {
81+
throw new Error("Cancelled by user, aborting deployment.")
82+
}
83+
}
84+
} catch (error: any) {
85+
logger.error('Failed to retrive deployed sites.', error.message)
86+
return
87+
}
88+
89+
// Create or update site
90+
try {
91+
let response
92+
93+
if (!internalFunctionId) {
94+
response = await consoleClient.post(`/api/sites`, { functionId, functionName })
95+
} else {
96+
response = await consoleClient.patch(
97+
`/api/sites/${internalFunctionId}`,
98+
{ functionId, functionName, status: 'deploying' }
99+
)
100+
}
101+
102+
if (!internalFunctionId && response.data && response.data._id) internalFunctionId = response.data._id
103+
} catch (error: any) {
104+
logger.error('Failed to update site metadata.', error.message)
105+
return
106+
}
107+
108+
// Deploy Site
109+
try {
110+
if (!internalFunctionId) throw new Error('Unable to retrive site ID')
111+
console.log(Chalk.yellow(`Deploying ${functionName} ...`))
112+
113+
const { data } = await consoleClient.put(`/api/sites/${internalFunctionId}/deploy`, {
114+
functionId: functionId
115+
})
116+
117+
if (!!data.err) {
118+
console.log(Chalk.red(`Deployment unsuccessful, ${data.message}`))
119+
} else {
120+
console.log(
121+
Chalk.green(
122+
`Successfully deployed ${functionName} with id ${functionId}`
123+
)
124+
)
125+
}
126+
} catch (error: any) {
127+
logger.error('Failed to deploy site.', error.message)
128+
return
129+
}
130+
131+
return
132+
}

src/commands/sites/index.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
import type { Argv } from "yargs"
22
import { run as runInit } from "./init"
3+
import { run as runList } from "./list"
34
import { run as runBuild } from "./build"
45
import { run as runPreview } from "./preview"
6+
import { run as runDelete } from "./delete"
7+
import { run as runDeploy } from "./deploy"
58

69
export function sitesCli(yargs: Argv) {
710
yargs
811
.usage('bls sites [subcommand]')
912
.demandOption('experimental')
1013

14+
yargs.command(
15+
'list',
16+
'Lists your deployed blockless sites',
17+
() => { },
18+
() => {
19+
runList()
20+
}
21+
)
22+
1123
yargs.command(
1224
'init [name]',
1325
'Initializes a blockless site project with a given name and template',
@@ -29,6 +41,22 @@ export function sitesCli(yargs: Argv) {
2941
}
3042
)
3143

44+
yargs.command(
45+
'delete [target]',
46+
'Undeploys and deletes a site from the network',
47+
(yargs) => {
48+
return yargs
49+
.positional('target', {
50+
description: 'The name of the site to delete (Defaults to the working directory)',
51+
type: 'string',
52+
default: undefined
53+
})
54+
},
55+
(argv) => {
56+
runDelete(argv as any)
57+
}
58+
)
59+
3260
yargs.command(
3361
'build [path]',
3462
'Builds and creates a wasm archive of a static site',
@@ -67,4 +95,20 @@ export function sitesCli(yargs: Argv) {
6795
runPreview(argv)
6896
}
6997
)
98+
99+
yargs.command(
100+
'deploy [path]',
101+
'Deploys a static site on Blockless',
102+
(yargs) => {
103+
return yargs
104+
.positional('path', {
105+
description: 'Set the path to the static site project',
106+
type: 'string',
107+
default: undefined
108+
})
109+
},
110+
(argv) => {
111+
runDeploy(argv as any)
112+
}
113+
)
70114
}

src/commands/sites/list.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Chalk from "chalk"
2+
import { consoleClient } from "../../lib/http"
3+
import { logger } from "../../lib/logger"
4+
5+
export const run = async () => {
6+
try {
7+
const { data } = await consoleClient.get(`/api/sites?limit=999`, {})
8+
const sites = data.docs ? data.docs : []
9+
10+
logger.log('List of Sites:')
11+
logger.log('-----------------------------------')
12+
13+
if (sites && sites.length > 0) {
14+
sites.forEach && sites.forEach((f: any) => {
15+
logger.log('')
16+
logger.log(`${Chalk.blue('Name:')} ${f.functionName}`)
17+
logger.log(`${Chalk.blue('CID:')} ${f.functionId}`)
18+
logger.log(`${Chalk.blue('Status:')} ${f.status === 'stopped' ? Chalk.red(f.status) : f.status === 'deployed' ? Chalk.green(f.status) : f.status}`)
19+
})
20+
21+
logger.log('')
22+
logger.log(`Total Sites: ${sites.length}`)
23+
} else {
24+
logger.log('')
25+
logger.log('You have no sites.')
26+
}
27+
} catch (error: any) {
28+
logger.error('Failed to retrieve site list.', error.message)
29+
return
30+
}
31+
}

0 commit comments

Comments
 (0)