diff --git a/Dockerfile b/Dockerfile index 71cee3c..ad20bfa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,15 @@ # Building Backend FROM golang:alpine as roadsign-server -RUN apk add nodejs npm - WORKDIR /source COPY . . -WORKDIR /source/pkg/sideload/view -RUN npm install -RUN npm run build -WORKDIR /source RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -buildvcs -o /dist ./pkg/cmd/server/main.go # Runtime FROM golang:alpine +RUN apk add zip + COPY --from=roadsign-server /dist /roadsign/server EXPOSE 81 diff --git a/cli/.gitignore b/cli/.gitignore index 9b1ee42..a42ec33 100644 --- a/cli/.gitignore +++ b/cli/.gitignore @@ -173,3 +173,5 @@ dist # Finder (MacOS) folder config .DS_Store + +/test/static-files \ No newline at end of file diff --git a/cli/bun.lockb b/cli/bun.lockb index 2587130..8d69663 100755 Binary files a/cli/bun.lockb and b/cli/bun.lockb differ diff --git a/cli/index.ts b/cli/index.ts index 0ba722e..39fc1aa 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -8,6 +8,7 @@ import { ListServerCommand } from "./src/cmd/list.ts" import { StatusCommand } from "./src/cmd/status.ts" import { InfoCommand } from "./src/cmd/info.ts" import { ProcessCommand } from "./src/cmd/process-info.ts" +import { DeployCommand } from "./src/cmd/deploy.ts" const [node, app, ...args] = process.argv @@ -31,4 +32,5 @@ cli.register(ListServerCommand) cli.register(StatusCommand) cli.register(InfoCommand) cli.register(ProcessCommand) +cli.register(DeployCommand) cli.runExit(args) \ No newline at end of file diff --git a/cli/src/cmd/deploy.ts b/cli/src/cmd/deploy.ts new file mode 100644 index 0000000..1ef0049 --- /dev/null +++ b/cli/src/cmd/deploy.ts @@ -0,0 +1,86 @@ +import { RsConfig } from "../utils/config.ts" +import { Command, Option, type Usage } from "clipanion" +import chalk from "chalk" +import ora from "ora" +import * as fs from "node:fs" +import * as child_process from "node:child_process" +import * as path from "node:path" +import { createAuthHeader } from "../utils/auth.ts" + +export class DeployCommand extends Command { + static paths = [[`deploy`]] + static usage: Usage = { + category: `Building`, + description: `Deploying App / Static Site onto RoadSign`, + details: `Deploying an application or hosting a static site via RoadSign, you need preconfigured the RoadSign, or sync the configurations via sync command.`, + examples: [["Deploying to RoadSign", `deploy `]] + } + + server = Option.String({ required: true }) + site = Option.String({ required: true }) + upstream = Option.String({ required: true }) + input = Option.String({ required: true }) + + async execute() { + const config = await RsConfig.getInstance() + + const server = config.config.servers.find(item => item.label === this.server) + if (server == null) { + this.context.stdout.write(chalk.red(`Server with label ${chalk.bold(this.server)} was not found.\n`)) + return + } + + if (!fs.existsSync(this.input)) { + this.context.stdout.write(chalk.red(`Input file ${chalk.bold(this.input)} was not found.\n`)) + return + } + + let isDirectory = false + if (fs.statSync(this.input).isDirectory()) { + if (this.input.endsWith("/")) { + this.input = this.input.slice(0, -1) + } + this.input += "/*" + + const compressPrefStart = performance.now() + const compressSpinner = ora(`Compressing ${chalk.bold(this.input)}...`).start() + const destName = `${Date.now()}-roadsign-archive.zip` + child_process.execSync(`zip -rj ${destName} ${this.input}`) + const compressPrefTook = performance.now() - compressPrefStart + compressSpinner.succeed(`Compressing completed in ${(compressPrefTook / 1000).toFixed(2)}s 🎉`) + this.input = destName + isDirectory = true + } + + const destBreadcrumb = [this.site, this.upstream].join(" ➜ ") + const spinner = ora(`Deploying ${chalk.bold(destBreadcrumb)} to ${chalk.bold(this.server)}...`).start() + + const prefStart = performance.now() + + try { + const payload = new FormData() + payload.set("attachments", await fs.openAsBlob(this.input), isDirectory ? "dist.zip" : path.basename(this.input)) + const res = await fetch(`${server.url}/webhooks/publish/${this.site}/${this.upstream}?mimetype=application/zip`, { + method: "PUT", + body: payload, + headers: { + Authorization: createAuthHeader(server.credential) + } + }) + if (res.status !== 200) { + throw new Error(await res.text()) + } + const prefTook = performance.now() - prefStart + spinner.succeed(`Deploying completed in ${(prefTook / 1000).toFixed(2)}s 🎉`) + } catch (e) { + this.context.stdout.write(`Failed to deploy to remote: ${e}\n`) + spinner.fail(`Server with label ${chalk.bold(this.server)} is not running! 😢`) + } finally { + if (isDirectory && this.input.endsWith(".zip")) { + fs.unlinkSync(this.input) + } + } + + process.exit(0) + } +} \ No newline at end of file diff --git a/cli/test/static-files/index.html b/cli/test/static-files/index.html new file mode 100644 index 0000000..de321d9 --- /dev/null +++ b/cli/test/static-files/index.html @@ -0,0 +1,12 @@ + + + + + + Hello, World! + + +

Hello, there!

+

Here's the newer version of static files hosted by roadsign!

+ + \ No newline at end of file diff --git a/pkg/sideload/publish.go b/pkg/sideload/publish.go index 711a8bb..59bcae1 100644 --- a/pkg/sideload/publish.go +++ b/pkg/sideload/publish.go @@ -46,7 +46,7 @@ func doPublish(c *fiber.Ctx) error { } } else if destination != nil && destination.GetType() != navi.DestinationStaticFile { return fiber.ErrUnprocessableEntity - } else { + } else if destination == nil { return fiber.ErrNotFound } @@ -84,7 +84,7 @@ func doPublish(c *fiber.Ctx) error { } if instance != nil { - instance.Wake() + _ = instance.Wake() } return c.SendStatus(fiber.StatusOK) diff --git a/pkg/sideload/regions.go b/pkg/sideload/regions.go index 9ce0f9c..33e753d 100644 --- a/pkg/sideload/regions.go +++ b/pkg/sideload/regions.go @@ -43,24 +43,38 @@ func doSync(c *fiber.Ctx) error { defer file.Close() } - var rebootQueue []*warden.AppInstance + var stopQueue, startQueue []*warden.AppInstance + // Getting things need to stop if region, ok := lo.Find(navi.R.Regions, func(item *navi.Region) bool { return item.ID == id }); ok { for _, application := range region.Applications { if instance := warden.GetFromPool(application.ID); instance != nil { - instance.Stop() - rebootQueue = append(rebootQueue, instance) + stopQueue = append(stopQueue, instance) } } } // Reload - navi.ReadInConfig(viper.GetString("paths.configs")) + _ = navi.ReadInConfig(viper.GetString("paths.configs")) + + // Getting things need to start + if region, ok := lo.Find(navi.R.Regions, func(item *navi.Region) bool { + return item.ID == id + }); ok { + for _, application := range region.Applications { + if instance := warden.GetFromPool(application.ID); instance != nil { + startQueue = append(startQueue, instance) + } + } + } // Reboot - for _, instance := range rebootQueue { - instance.Wake() + for _, instance := range stopQueue { + _ = instance.Stop() + } + for _, instance := range startQueue { + _ = instance.Start() } return c.SendStatus(fiber.StatusOK) diff --git a/test/data/.gitignore b/test/data/.gitignore index 4cffec0..babd63c 100644 --- a/test/data/.gitignore +++ b/test/data/.gitignore @@ -1 +1,2 @@ -/capital \ No newline at end of file +/capital +/static-files \ No newline at end of file diff --git a/test/roadsign/config/example.toml b/test/roadsign/config/example.toml new file mode 100644 index 0000000..20fcc6a --- /dev/null +++ b/test/roadsign/config/example.toml @@ -0,0 +1,9 @@ +id = "static-files" + +[[locations]] +id = "static-files-loc" +hosts = ["localhost:8000"] +paths = ["/"] +[[locations.destinations]] +id = "static-files-des" +uri = "files://../data/static-files" diff --git a/test/roadsign/config/example.yaml b/test/roadsign/config/example.yaml deleted file mode 100644 index 2a46d7d..0000000 --- a/test/roadsign/config/example.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Example Site -rules: - - host: ["localhost:8000"] - path: ["/"] -upstreams: - - id: example - name: Benchmarking Data - uri: files://../data diff --git a/test/roadsign/settings.toml b/test/roadsign/settings.toml index c6ccfb3..ddcc90b 100644 --- a/test/roadsign/settings.toml +++ b/test/roadsign/settings.toml @@ -1,18 +1,21 @@ +id = "central-dc" + [debug] -print_routes = true +print_routes = false + +[sideload] +ports = [":81"] +secured_ports = [] +trusted_proxies = ["localhost"] [hypertext] -sideload_ports = [":81"] -sideload_secured_ports = [] ports = [":8000"] secured_ports = [] +force_https = false -[hypertext.certificate] -redirect = false -sideload_key = "./cert.key" -sideload_pem = "./cert.pem" -key = "./cert.key" -pem = "./cert.pem" +# [[hypertext.certificate]] +# key = "./certs/privkey.pem" +# pem = "./certs/fullchain.pem" [hypertext.limitation] max_body_size = 549_755_813_888 # 512 GiB @@ -21,11 +24,13 @@ max_qps = -1 [paths] configs = "./config" -[performance] +[telemetry] request_logging = true -network_timeout = 3_000 +capture_traces = true + +[performance] +traces_limit = 256 prefork = false [security] -sideload_trusted_proxies = ["localhost"] credential = "e81f43f32d934271af6322e5376f5f59"