🎉 Basic Web Sideload
This commit is contained in:
		
							
								
								
									
										59
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
<component name="ProjectCodeStyleConfiguration">
 | 
			
		||||
  <code_scheme name="Project" version="173">
 | 
			
		||||
    <HTMLCodeStyleSettings>
 | 
			
		||||
      <option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
 | 
			
		||||
    </HTMLCodeStyleSettings>
 | 
			
		||||
    <JSCodeStyleSettings version="0">
 | 
			
		||||
      <option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
 | 
			
		||||
      <option name="FORCE_SEMICOLON_STYLE" value="true" />
 | 
			
		||||
      <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
 | 
			
		||||
      <option name="FORCE_QUOTE_STYlE" value="true" />
 | 
			
		||||
      <option name="ENFORCE_TRAILING_COMMA" value="Remove" />
 | 
			
		||||
      <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
 | 
			
		||||
      <option name="SPACES_WITHIN_IMPORTS" value="true" />
 | 
			
		||||
    </JSCodeStyleSettings>
 | 
			
		||||
    <TypeScriptCodeStyleSettings version="0">
 | 
			
		||||
      <option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
 | 
			
		||||
      <option name="FORCE_SEMICOLON_STYLE" value="true" />
 | 
			
		||||
      <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
 | 
			
		||||
      <option name="FORCE_QUOTE_STYlE" value="true" />
 | 
			
		||||
      <option name="ENFORCE_TRAILING_COMMA" value="Remove" />
 | 
			
		||||
      <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
 | 
			
		||||
      <option name="SPACES_WITHIN_IMPORTS" value="true" />
 | 
			
		||||
    </TypeScriptCodeStyleSettings>
 | 
			
		||||
    <VueCodeStyleSettings>
 | 
			
		||||
      <option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
 | 
			
		||||
      <option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
 | 
			
		||||
    </VueCodeStyleSettings>
 | 
			
		||||
    <codeStyleSettings language="HTML">
 | 
			
		||||
      <option name="SOFT_MARGINS" value="120" />
 | 
			
		||||
      <indentOptions>
 | 
			
		||||
        <option name="INDENT_SIZE" value="2" />
 | 
			
		||||
        <option name="CONTINUATION_INDENT_SIZE" value="2" />
 | 
			
		||||
        <option name="TAB_SIZE" value="2" />
 | 
			
		||||
      </indentOptions>
 | 
			
		||||
    </codeStyleSettings>
 | 
			
		||||
    <codeStyleSettings language="JavaScript">
 | 
			
		||||
      <option name="SOFT_MARGINS" value="120" />
 | 
			
		||||
      <indentOptions>
 | 
			
		||||
        <option name="INDENT_SIZE" value="2" />
 | 
			
		||||
        <option name="CONTINUATION_INDENT_SIZE" value="2" />
 | 
			
		||||
        <option name="TAB_SIZE" value="2" />
 | 
			
		||||
      </indentOptions>
 | 
			
		||||
    </codeStyleSettings>
 | 
			
		||||
    <codeStyleSettings language="TypeScript">
 | 
			
		||||
      <option name="SOFT_MARGINS" value="120" />
 | 
			
		||||
      <indentOptions>
 | 
			
		||||
        <option name="INDENT_SIZE" value="2" />
 | 
			
		||||
        <option name="CONTINUATION_INDENT_SIZE" value="2" />
 | 
			
		||||
        <option name="TAB_SIZE" value="2" />
 | 
			
		||||
      </indentOptions>
 | 
			
		||||
    </codeStyleSettings>
 | 
			
		||||
    <codeStyleSettings language="Vue">
 | 
			
		||||
      <option name="SOFT_MARGINS" value="120" />
 | 
			
		||||
      <indentOptions>
 | 
			
		||||
        <option name="CONTINUATION_INDENT_SIZE" value="2" />
 | 
			
		||||
      </indentOptions>
 | 
			
		||||
    </codeStyleSettings>
 | 
			
		||||
  </code_scheme>
 | 
			
		||||
</component>
 | 
			
		||||
							
								
								
									
										5
									
								
								.idea/codeStyles/codeStyleConfig.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.idea/codeStyles/codeStyleConfig.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
<component name="ProjectCodeStyleConfiguration">
 | 
			
		||||
  <state>
 | 
			
		||||
    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
 | 
			
		||||
  </state>
 | 
			
		||||
</component>
 | 
			
		||||
@@ -15,8 +15,8 @@ import (
 | 
			
		||||
func doPublish(c *fiber.Ctx) error {
 | 
			
		||||
	var workdir string
 | 
			
		||||
	var site *sign.SiteConfig
 | 
			
		||||
	var upstream *sign.UpstreamConfig
 | 
			
		||||
	var process *sign.ProcessConfig
 | 
			
		||||
	var upstream *sign.UpstreamInstance
 | 
			
		||||
	var process *sign.ProcessInstance
 | 
			
		||||
	for _, item := range sign.App.Sites {
 | 
			
		||||
		if item.ID == c.Params("site") {
 | 
			
		||||
			site = item
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,9 @@ func InitSideload() *fiber.App {
 | 
			
		||||
	cgi := app.Group("/cgi").Name("CGI")
 | 
			
		||||
	{
 | 
			
		||||
		cgi.All("/connectivity", responseConnectivity)
 | 
			
		||||
		cgi.Get("/statistics", getStatistics)
 | 
			
		||||
		cgi.Get("/sites", getSites)
 | 
			
		||||
		cgi.Get("/sites/cfg/:id", getSiteConfig)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	webhooks := app.Group("/webhooks").Name("WebHooks")
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,24 @@ import (
 | 
			
		||||
	"gopkg.in/yaml.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getSites(c *fiber.Ctx) error {
 | 
			
		||||
	return c.JSON(sign.App.Sites)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getSiteConfig(c *fiber.Ctx) error {
 | 
			
		||||
	fp := filepath.Join(viper.GetString("paths.configs"), c.Params("id"))
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	var data []byte
 | 
			
		||||
	if data, err = os.ReadFile(fp + ".yml"); err != nil {
 | 
			
		||||
		if data, err = os.ReadFile(fp + ".yaml"); err != nil {
 | 
			
		||||
			return fiber.NewError(fiber.StatusNotFound, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.Type("yaml").SendString(string(data))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func doSyncSite(c *fiber.Ctx) error {
 | 
			
		||||
	var req sign.SiteConfig
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								pkg/sideload/statistics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								pkg/sideload/statistics.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
package sideload
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"code.smartsheep.studio/goatworks/roadsign/pkg/sign"
 | 
			
		||||
	"github.com/gofiber/fiber/v2"
 | 
			
		||||
	"github.com/samber/lo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getStatistics(c *fiber.Ctx) error {
 | 
			
		||||
	upstreams := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.UpstreamInstance {
 | 
			
		||||
		return item.Upstreams
 | 
			
		||||
	})
 | 
			
		||||
	processes := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance {
 | 
			
		||||
		return item.Processes
 | 
			
		||||
	})
 | 
			
		||||
	unhealthy := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance {
 | 
			
		||||
		return lo.Filter(item.Processes, func(item *sign.ProcessInstance, idx int) bool {
 | 
			
		||||
			return item.Status != sign.ProcessStarted
 | 
			
		||||
		})
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return c.JSON(fiber.Map{
 | 
			
		||||
		"sites":     len(sign.App.Sites),
 | 
			
		||||
		"upstreams": len(upstreams),
 | 
			
		||||
		"processes": len(processes),
 | 
			
		||||
		"status":    len(unhealthy) == 0,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								pkg/sideload/view/.eslintrc.cjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								pkg/sideload/view/.eslintrc.cjs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
/* eslint-env node */
 | 
			
		||||
require('@rushstack/eslint-patch/modern-module-resolution')
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  root: true,
 | 
			
		||||
  'extends': [
 | 
			
		||||
    'plugin:vue/vue3-essential',
 | 
			
		||||
    'eslint:recommended',
 | 
			
		||||
    '@vue/eslint-config-typescript',
 | 
			
		||||
    '@vue/eslint-config-prettier/skip-formatting'
 | 
			
		||||
  ],
 | 
			
		||||
  parserOptions: {
 | 
			
		||||
    ecmaVersion: 'latest'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								pkg/sideload/view/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								pkg/sideload/view/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
# Logs
 | 
			
		||||
logs
 | 
			
		||||
*.log
 | 
			
		||||
npm-debug.log*
 | 
			
		||||
yarn-debug.log*
 | 
			
		||||
yarn-error.log*
 | 
			
		||||
pnpm-debug.log*
 | 
			
		||||
lerna-debug.log*
 | 
			
		||||
 | 
			
		||||
node_modules
 | 
			
		||||
.DS_Store
 | 
			
		||||
dist
 | 
			
		||||
dist-ssr
 | 
			
		||||
coverage
 | 
			
		||||
*.local
 | 
			
		||||
 | 
			
		||||
/cypress/videos/
 | 
			
		||||
/cypress/screenshots/
 | 
			
		||||
 | 
			
		||||
# Editor directories and files
 | 
			
		||||
.vscode/*
 | 
			
		||||
!.vscode/extensions.json
 | 
			
		||||
.idea
 | 
			
		||||
*.suo
 | 
			
		||||
*.ntvs*
 | 
			
		||||
*.njsproj
 | 
			
		||||
*.sln
 | 
			
		||||
*.sw?
 | 
			
		||||
 | 
			
		||||
*.tsbuildinfo
 | 
			
		||||
							
								
								
									
										8
									
								
								pkg/sideload/view/.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pkg/sideload/view/.prettierrc.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "$schema": "https://json.schemastore.org/prettierrc",
 | 
			
		||||
  "semi": false,
 | 
			
		||||
  "tabWidth": 2,
 | 
			
		||||
  "singleQuote": false,
 | 
			
		||||
  "printWidth": 120,
 | 
			
		||||
  "trailingComma": "none"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								pkg/sideload/view/.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pkg/sideload/view/.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "recommendations": [
 | 
			
		||||
    "Vue.volar",
 | 
			
		||||
    "Vue.vscode-typescript-vue-plugin",
 | 
			
		||||
    "dbaeumer.vscode-eslint",
 | 
			
		||||
    "esbenp.prettier-vscode"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								pkg/sideload/view/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								pkg/sideload/view/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
# @roadsign/sideload-ui
 | 
			
		||||
 | 
			
		||||
This template should help get you started developing with Vue 3 in Vite.
 | 
			
		||||
 | 
			
		||||
## Recommended IDE Setup
 | 
			
		||||
 | 
			
		||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
 | 
			
		||||
 | 
			
		||||
## Type Support for `.vue` Imports in TS
 | 
			
		||||
 | 
			
		||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
 | 
			
		||||
 | 
			
		||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
 | 
			
		||||
 | 
			
		||||
1. Disable the built-in TypeScript Extension
 | 
			
		||||
    1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
 | 
			
		||||
    2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
 | 
			
		||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
 | 
			
		||||
 | 
			
		||||
## Customize configuration
 | 
			
		||||
 | 
			
		||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
 | 
			
		||||
 | 
			
		||||
## Project Setup
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
yarn
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Compile and Hot-Reload for Development
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
yarn dev
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Type-Check, Compile and Minify for Production
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
yarn build
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Lint with [ESLint](https://eslint.org/)
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
yarn lint
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										6
									
								
								pkg/sideload/view/embed.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								pkg/sideload/view/embed.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
package view
 | 
			
		||||
 | 
			
		||||
import "embed"
 | 
			
		||||
 | 
			
		||||
//go:embed all:dist
 | 
			
		||||
var FS embed.FS
 | 
			
		||||
							
								
								
									
										1
									
								
								pkg/sideload/view/env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/sideload/view/env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
/// <reference types="vite/client" />
 | 
			
		||||
							
								
								
									
										13
									
								
								pkg/sideload/view/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								pkg/sideload/view/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <link rel="icon" href="/favicon.ico">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>RoadSign</title>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div id="app"></div>
 | 
			
		||||
    <script type="module" src="/src/main.ts"></script>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										44
									
								
								pkg/sideload/view/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								pkg/sideload/view/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@roadsign/sideload-ui",
 | 
			
		||||
  "version": "0.0.0",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "type": "module",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "dev": "vite",
 | 
			
		||||
    "build": "run-p type-check \"build-only {@}\" --",
 | 
			
		||||
    "preview": "vite preview",
 | 
			
		||||
    "build-only": "vite build",
 | 
			
		||||
    "type-check": "vue-tsc --build --force",
 | 
			
		||||
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
 | 
			
		||||
    "format": "prettier --write src/"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@guolao/vue-monaco-editor": "^1.4.1",
 | 
			
		||||
    "highlight.js": "^11.9.0",
 | 
			
		||||
    "js-yaml": "^4.1.0",
 | 
			
		||||
    "pinia": "^2.1.7",
 | 
			
		||||
    "vue": "^3.3.11",
 | 
			
		||||
    "vue-router": "^4.2.5"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@rushstack/eslint-patch": "^1.3.3",
 | 
			
		||||
    "@tsconfig/node18": "^18.2.2",
 | 
			
		||||
    "@types/js-yaml": "^4.0.9",
 | 
			
		||||
    "@types/node": "^18.19.3",
 | 
			
		||||
    "@vicons/carbon": "^0.12.0",
 | 
			
		||||
    "@vitejs/plugin-vue": "^4.5.2",
 | 
			
		||||
    "@vue/eslint-config-prettier": "^8.0.0",
 | 
			
		||||
    "@vue/eslint-config-typescript": "^12.0.0",
 | 
			
		||||
    "@vue/tsconfig": "^0.5.0",
 | 
			
		||||
    "eslint": "^8.49.0",
 | 
			
		||||
    "eslint-plugin-vue": "^9.17.0",
 | 
			
		||||
    "naive-ui": "^2.36.0",
 | 
			
		||||
    "npm-run-all2": "^6.1.1",
 | 
			
		||||
    "prettier": "^3.0.3",
 | 
			
		||||
    "typescript": "~5.3.0",
 | 
			
		||||
    "unocss": "^0.58.2",
 | 
			
		||||
    "vfonts": "^0.0.3",
 | 
			
		||||
    "vite": "^5.0.10",
 | 
			
		||||
    "vue-tsc": "^1.8.25"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								pkg/sideload/view/src/assets/main.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								pkg/sideload/view/src/assets/main.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
@import "vfonts/IBMPlexSans.css";
 | 
			
		||||
@import "vfonts/IBMPlexMono.css";
 | 
			
		||||
 | 
			
		||||
a {
 | 
			
		||||
    color: #3f7ee8;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										133
									
								
								pkg/sideload/view/src/components/data/sites-table-action.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								pkg/sideload/view/src/components/data/sites-table-action.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="flex gap-[4px]">
 | 
			
		||||
    <n-button size="small" @click="publishing = true">
 | 
			
		||||
      <template #icon>
 | 
			
		||||
        <n-icon :component="CloudUpload" />
 | 
			
		||||
      </template>
 | 
			
		||||
    </n-button>
 | 
			
		||||
    <n-button size="small" @click="editConfig()">
 | 
			
		||||
      <template #icon>
 | 
			
		||||
        <n-icon :component="Edit" />
 | 
			
		||||
      </template>
 | 
			
		||||
    </n-button>
 | 
			
		||||
 | 
			
		||||
    <n-modal
 | 
			
		||||
      v-model:show="publishing"
 | 
			
		||||
      class="w-[720px]"
 | 
			
		||||
      preset="card"
 | 
			
		||||
      title="Publish Artifacts"
 | 
			
		||||
      segmented
 | 
			
		||||
      closable
 | 
			
		||||
    >
 | 
			
		||||
      We are sorry about this tool isn't completed yet. <br>
 | 
			
		||||
      For now, you can use our <b>Wonderful Command Line Tool —— RDS</b> <br>
 | 
			
		||||
      Learn more on our <a href="https://wiki.smartsheep.studio/roadsign/index.html" target="_blank">official wiki</a>.
 | 
			
		||||
      <br>
 | 
			
		||||
      <br>
 | 
			
		||||
      Install it by this command below
 | 
			
		||||
      <n-code code="go install code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds@latest" />
 | 
			
		||||
      <br>
 | 
			
		||||
      Then connect your rds client to this server
 | 
			
		||||
      <n-code :code="`rds connect <name> ${host} <credentials>`" />
 | 
			
		||||
      <br>
 | 
			
		||||
      After that you can publish your stuff (You need to compress them to zip archive before publish)
 | 
			
		||||
      <n-code :code="`rds deploy <name> ${props.id} <upstream id or process id>`" />
 | 
			
		||||
    </n-modal>
 | 
			
		||||
 | 
			
		||||
    <n-modal
 | 
			
		||||
      v-model:show="editing"
 | 
			
		||||
      class="w-[720px]"
 | 
			
		||||
      content-style="padding: 0"
 | 
			
		||||
      preset="card"
 | 
			
		||||
      title="Edit Configuration"
 | 
			
		||||
      segmented
 | 
			
		||||
      closable
 | 
			
		||||
    >
 | 
			
		||||
      <div class="relative h-[540px]">
 | 
			
		||||
        <vue-monaco-editor
 | 
			
		||||
          v-model:value="config"
 | 
			
		||||
          :options="{ automaticLayout: true, minimap: { enabled: false } }"
 | 
			
		||||
          language="yaml"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <div class="fab">
 | 
			
		||||
          <n-tooltip placement="left">
 | 
			
		||||
            <template #trigger>
 | 
			
		||||
              <n-button
 | 
			
		||||
                circle
 | 
			
		||||
                type="primary"
 | 
			
		||||
                size="large"
 | 
			
		||||
                class="shadow-lg"
 | 
			
		||||
                :loading="submitting"
 | 
			
		||||
                @click="syncConfig()"
 | 
			
		||||
              >
 | 
			
		||||
                <template #icon>
 | 
			
		||||
                  <n-icon :component="Save" />
 | 
			
		||||
                </template>
 | 
			
		||||
              </n-button>
 | 
			
		||||
            </template>
 | 
			
		||||
            This operation will restart all processes related. Service may interrupted for some while.
 | 
			
		||||
          </n-tooltip>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </n-modal>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { NButton, NCode, NIcon, NModal, NTooltip, useMessage } from "naive-ui"
 | 
			
		||||
import { CloudUpload, Edit, Save } from "@vicons/carbon"
 | 
			
		||||
import { ref } from "vue"
 | 
			
		||||
import { VueMonacoEditor } from "@guolao/vue-monaco-editor"
 | 
			
		||||
import * as yaml from "js-yaml"
 | 
			
		||||
 | 
			
		||||
const message = useMessage()
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{ id: string, rules: any[], upstreams: any[], processes: any[] }>()
 | 
			
		||||
const emits = defineEmits(["reload"])
 | 
			
		||||
const host = location.protocol + "//" + location.host
 | 
			
		||||
 | 
			
		||||
const submitting = ref(false)
 | 
			
		||||
 | 
			
		||||
const publishing = ref(false)
 | 
			
		||||
const editing = ref(false)
 | 
			
		||||
 | 
			
		||||
const config = ref<string | null>(null)
 | 
			
		||||
 | 
			
		||||
async function editConfig() {
 | 
			
		||||
  const resp = await fetch(`/cgi/sites/cfg/${props.id}`)
 | 
			
		||||
  config.value = await resp.text()
 | 
			
		||||
  editing.value = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function syncConfig() {
 | 
			
		||||
  let content
 | 
			
		||||
  try {
 | 
			
		||||
    content = yaml.load(config.value)
 | 
			
		||||
  } catch (e: any) {
 | 
			
		||||
    message.warning(`Your configuration has some issue: ${e.message}`)
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  submitting.value = true
 | 
			
		||||
  const resp = await fetch(`/webhooks/sync/${props.id}`, {
 | 
			
		||||
    method: "PUT",
 | 
			
		||||
    headers: { "Content-Type": "application/json" },
 | 
			
		||||
    body: JSON.stringify(content)
 | 
			
		||||
  })
 | 
			
		||||
  if (resp.status != 200) {
 | 
			
		||||
    message.error(`Something went wrong... ${await resp.text()}`)
 | 
			
		||||
  } else {
 | 
			
		||||
    emits("reload")
 | 
			
		||||
  }
 | 
			
		||||
  submitting.value = false
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.fab {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  bottom: 16px;
 | 
			
		||||
  right: 24px;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										110
									
								
								pkg/sideload/view/src/components/data/sites-table-add.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								pkg/sideload/view/src/components/data/sites-table-add.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <n-button circle size="small" type="primary" @click="creating = true">
 | 
			
		||||
      <template #icon>
 | 
			
		||||
        <n-icon :component="Add" />
 | 
			
		||||
      </template>
 | 
			
		||||
    </n-button>
 | 
			
		||||
 | 
			
		||||
    <n-modal
 | 
			
		||||
      v-model:show="creating"
 | 
			
		||||
      class="w-[720px]"
 | 
			
		||||
      content-style="padding: 0"
 | 
			
		||||
      preset="card"
 | 
			
		||||
      title="Create Site"
 | 
			
		||||
      segmented
 | 
			
		||||
      closable
 | 
			
		||||
    >
 | 
			
		||||
      <div class="py-4 px-5 border border-solid border-b border-[#eee]">
 | 
			
		||||
        <n-input
 | 
			
		||||
          v-model:value="data.id"
 | 
			
		||||
          placeholder="Will be the file name of this file"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="relative mt-[4px] h-[540px]">
 | 
			
		||||
        <vue-monaco-editor
 | 
			
		||||
          v-model:value="data.content"
 | 
			
		||||
          :options="{ automaticLayout: true, minimap: { enabled: false } }"
 | 
			
		||||
          language="yaml"
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <div class="fab">
 | 
			
		||||
          <n-tooltip placement="left">
 | 
			
		||||
            <template #trigger>
 | 
			
		||||
              <n-button
 | 
			
		||||
                circle
 | 
			
		||||
                type="primary"
 | 
			
		||||
                size="large"
 | 
			
		||||
                class="shadow-lg"
 | 
			
		||||
                :loading="submitting"
 | 
			
		||||
                @click="submit()"
 | 
			
		||||
              >
 | 
			
		||||
                <template #icon>
 | 
			
		||||
                  <n-icon :component="Checkmark" />
 | 
			
		||||
                </template>
 | 
			
		||||
              </n-button>
 | 
			
		||||
            </template>
 | 
			
		||||
            This operation will publish this site right away.
 | 
			
		||||
          </n-tooltip>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </n-modal>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { NButton, NIcon, NInput, NModal, NTooltip, useMessage } from "naive-ui"
 | 
			
		||||
import { Add, Checkmark } from "@vicons/carbon"
 | 
			
		||||
import { VueMonacoEditor } from "@guolao/vue-monaco-editor"
 | 
			
		||||
import { ref } from "vue"
 | 
			
		||||
import * as yaml from "js-yaml"
 | 
			
		||||
 | 
			
		||||
const message = useMessage()
 | 
			
		||||
 | 
			
		||||
const emits = defineEmits(["reload"])
 | 
			
		||||
 | 
			
		||||
const submitting = ref(false)
 | 
			
		||||
const creating = ref(false)
 | 
			
		||||
 | 
			
		||||
const data = ref<any>({})
 | 
			
		||||
 | 
			
		||||
async function submit() {
 | 
			
		||||
  let content
 | 
			
		||||
  try {
 | 
			
		||||
    content = yaml.load(data.value.content)
 | 
			
		||||
  } catch (e: any) {
 | 
			
		||||
    message.warning(`Your configuration has some issue: ${e.message}`)
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  submitting.value = true
 | 
			
		||||
  const resp = await fetch(`/webhooks/sync/${data.value.id}`, {
 | 
			
		||||
    method: "PUT",
 | 
			
		||||
    headers: { "Content-Type": "application/json" },
 | 
			
		||||
    body: JSON.stringify(content)
 | 
			
		||||
  })
 | 
			
		||||
  if (resp.status != 200) {
 | 
			
		||||
    message.error(`Something went wrong... ${await resp.text()}`)
 | 
			
		||||
  } else {
 | 
			
		||||
    reset()
 | 
			
		||||
    emits("reload")
 | 
			
		||||
    message.success("Your site has been created! 🎉")
 | 
			
		||||
    creating.value = false
 | 
			
		||||
  }
 | 
			
		||||
  submitting.value = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function reset() {
 | 
			
		||||
  data.value.id = ""
 | 
			
		||||
  data.value.content = ""
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.fab {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  bottom: 16px;
 | 
			
		||||
  right: 24px;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										36
									
								
								pkg/sideload/view/src/components/data/sites-table-expand.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								pkg/sideload/view/src/components/data/sites-table-expand.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="flex flex-col gap-1">
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <div class="font-bold">Rules</div>
 | 
			
		||||
      <n-code :hljs="hljs" :code="parseData(props.rules)" language="json" />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <div class="font-bold">Upstreams</div>
 | 
			
		||||
      <n-code :hljs="hljs" :code="parseData(props.upstreams)" language="json" />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <div class="font-bold">Processes</div>
 | 
			
		||||
      <n-code :hljs="hljs" :code="parseData(props.processes)" language="json" />
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { NCode } from "naive-ui"
 | 
			
		||||
import hljs from "highlight.js/lib/core"
 | 
			
		||||
import json from "highlight.js/lib/languages/json"
 | 
			
		||||
 | 
			
		||||
hljs.registerLanguage("json", json)
 | 
			
		||||
 | 
			
		||||
const props = defineProps<{ rules: any[], upstreams: any[], processes: any[] }>()
 | 
			
		||||
 | 
			
		||||
function parseData(data: any): string {
 | 
			
		||||
  return JSON.stringify(data, null, 1)
 | 
			
		||||
    .replace(/  +/g, " ")
 | 
			
		||||
    .replace(/\n/g, "")
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										76
									
								
								pkg/sideload/view/src/components/data/sites-table.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								pkg/sideload/view/src/components/data/sites-table.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <n-card title="Sites">
 | 
			
		||||
      <template #header-extra>
 | 
			
		||||
        <sites-table-add @reload="readSites()" />
 | 
			
		||||
      </template>
 | 
			
		||||
 | 
			
		||||
      <n-data-table
 | 
			
		||||
        :columns="columns"
 | 
			
		||||
        :data="data"
 | 
			
		||||
        :row-key="(row: any) => row.id"
 | 
			
		||||
      />
 | 
			
		||||
    </n-card>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { NCard, NDataTable, NTag } from "naive-ui"
 | 
			
		||||
import { h, ref } from "vue"
 | 
			
		||||
import SitesTableExpand from "@/components/data/sites-table-expand.vue"
 | 
			
		||||
import SitesTableAction from "@/components/data/sites-table-action.vue"
 | 
			
		||||
import SitesTableAdd from "@/components/data/sites-table-add.vue"
 | 
			
		||||
 | 
			
		||||
const columns = [
 | 
			
		||||
  {
 | 
			
		||||
    type: "expand",
 | 
			
		||||
    renderExpand(row: any) {
 | 
			
		||||
      return h(SitesTableExpand, { ...row, class: "pl-[38px]" })
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    title: "ID",
 | 
			
		||||
    key: "id",
 | 
			
		||||
    render(row: any) {
 | 
			
		||||
      return h(NTag, { type: "info", bordered: false, size: "small" }, row?.id)
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    title: "Rules",
 | 
			
		||||
    key: "rules",
 | 
			
		||||
    render(row: any) {
 | 
			
		||||
      return row?.rules?.length ?? 0
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    title: "Upstreams",
 | 
			
		||||
    key: "upstreams",
 | 
			
		||||
    render(row: any) {
 | 
			
		||||
      return row?.upstreams?.length ?? 0
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    title: "Processes",
 | 
			
		||||
    key: "processes",
 | 
			
		||||
    render(row: any) {
 | 
			
		||||
      return row?.processes?.length ?? 0
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    title: "Actions",
 | 
			
		||||
    key: "actions",
 | 
			
		||||
    render(row: any) {
 | 
			
		||||
      return h(SitesTableAction, { ...row, onReload: () => readSites() })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
const data = ref<any[]>([])
 | 
			
		||||
 | 
			
		||||
async function readSites() {
 | 
			
		||||
  const resp = await fetch("/cgi/sites")
 | 
			
		||||
  data.value = await resp.json()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
readSites()
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										60
									
								
								pkg/sideload/view/src/layouts/main.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								pkg/sideload/view/src/layouts/main.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <n-layout>
 | 
			
		||||
    <n-layout-header class="header py-[8px] px-[36px]" bordered>
 | 
			
		||||
      <div class="flex items-center gap-2">
 | 
			
		||||
        <router-link class="link" to="/">
 | 
			
		||||
          RoadSign<i>!</i>
 | 
			
		||||
        </router-link>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div class="nav-menu">
 | 
			
		||||
        <div class="h-full flex items-center header-nav">
 | 
			
		||||
          <n-menu v-model:value="key" :options="options" mode="horizontal" />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </n-layout-header>
 | 
			
		||||
    <n-layout-content class="h-[calc(100vh-70px)] container mx-auto" content-style="padding: 24px">
 | 
			
		||||
      <router-view />
 | 
			
		||||
    </n-layout-content>
 | 
			
		||||
  </n-layout>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { type MenuOption, NIcon, NLayout, NLayoutContent, NLayoutHeader, NMenu } from "naive-ui"
 | 
			
		||||
import { type Component, h, ref } from "vue"
 | 
			
		||||
import { Dashboard } from "@vicons/carbon"
 | 
			
		||||
import { RouterLink, useRoute, useRouter } from "vue-router"
 | 
			
		||||
 | 
			
		||||
const route = useRoute()
 | 
			
		||||
const router = useRouter()
 | 
			
		||||
const key = ref(route.name?.toString())
 | 
			
		||||
 | 
			
		||||
router.afterEach((to) => {
 | 
			
		||||
  key.value = to.name?.toString() ?? "index"
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const options: MenuOption[] = [
 | 
			
		||||
  {
 | 
			
		||||
    label: () => h(RouterLink, { to: { name: "dashboard" } }, "Dashboard"),
 | 
			
		||||
    icon: renderIcon(Dashboard),
 | 
			
		||||
    key: "dashboard"
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
function renderIcon(icon: Component) {
 | 
			
		||||
  return () => h(NIcon, null, { default: () => h(icon) })
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.header {
 | 
			
		||||
  display: grid;
 | 
			
		||||
  grid-template-columns: 1fr auto 1fr;
 | 
			
		||||
  gap: 40px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.link {
 | 
			
		||||
  all: unset;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										16
									
								
								pkg/sideload/view/src/main.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								pkg/sideload/view/src/main.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import "./assets/main.css"
 | 
			
		||||
 | 
			
		||||
import "virtual:uno.css"
 | 
			
		||||
 | 
			
		||||
import { createApp } from "vue"
 | 
			
		||||
import { createPinia } from "pinia"
 | 
			
		||||
 | 
			
		||||
import root from "./root.vue"
 | 
			
		||||
import router from "./router"
 | 
			
		||||
 | 
			
		||||
const app = createApp(root)
 | 
			
		||||
 | 
			
		||||
app.use(createPinia())
 | 
			
		||||
app.use(router)
 | 
			
		||||
 | 
			
		||||
app.mount("#app")
 | 
			
		||||
							
								
								
									
										9
									
								
								pkg/sideload/view/src/root.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								pkg/sideload/view/src/root.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <n-message-provider>
 | 
			
		||||
    <router-view />
 | 
			
		||||
  </n-message-provider>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { NMessageProvider } from "naive-ui"
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										21
									
								
								pkg/sideload/view/src/router/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								pkg/sideload/view/src/router/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
import { createRouter, createWebHistory } from "vue-router"
 | 
			
		||||
 | 
			
		||||
const router = createRouter({
 | 
			
		||||
  history: createWebHistory(import.meta.env.BASE_URL),
 | 
			
		||||
  routes: [
 | 
			
		||||
    {
 | 
			
		||||
      path: "/",
 | 
			
		||||
      name: "layouts.main",
 | 
			
		||||
      component: () => import("@/layouts/main.vue"),
 | 
			
		||||
      children: [
 | 
			
		||||
        {
 | 
			
		||||
          path: "/",
 | 
			
		||||
          name: "dashboard",
 | 
			
		||||
          component: () => import("@/views/dashboard.vue")
 | 
			
		||||
        },
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
  ]
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
export default router
 | 
			
		||||
							
								
								
									
										35
									
								
								pkg/sideload/view/src/views/dashboard.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								pkg/sideload/view/src/views/dashboard.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="flex flex-col gap-2">
 | 
			
		||||
    <div class="grid gap-2 grid-cols-2 lg:grid-cols-4">
 | 
			
		||||
      <n-card embedded>
 | 
			
		||||
        <n-statistic label="Status">{{ data?.status ? "Operational" : "Incident" }}</n-statistic>
 | 
			
		||||
      </n-card>
 | 
			
		||||
      <n-card embedded>
 | 
			
		||||
        <n-statistic label="Sites">{{ data?.sites }}</n-statistic>
 | 
			
		||||
      </n-card>
 | 
			
		||||
      <n-card embedded>
 | 
			
		||||
        <n-statistic label="Upstreams">{{ data?.upstreams }}</n-statistic>
 | 
			
		||||
      </n-card>
 | 
			
		||||
      <n-card embedded>
 | 
			
		||||
        <n-statistic label="Processes">{{ data?.processes }}</n-statistic>
 | 
			
		||||
      </n-card>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <sites-table />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { NCard, NStatistic } from "naive-ui"
 | 
			
		||||
import { ref } from "vue"
 | 
			
		||||
import SitesTable from "@/components/data/sites-table.vue"
 | 
			
		||||
 | 
			
		||||
const data = ref<any>({})
 | 
			
		||||
 | 
			
		||||
async function readStatistics() {
 | 
			
		||||
  const resp = await fetch("/cgi/statistics")
 | 
			
		||||
  data.value = await resp.json()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
readStatistics()
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										13
									
								
								pkg/sideload/view/tsconfig.app.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								pkg/sideload/view/tsconfig.app.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
{
 | 
			
		||||
  "extends": "@vue/tsconfig/tsconfig.dom.json",
 | 
			
		||||
  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
 | 
			
		||||
  "exclude": ["src/**/__tests__/*"],
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "composite": true,
 | 
			
		||||
    "noEmit": true,
 | 
			
		||||
    "baseUrl": ".",
 | 
			
		||||
    "paths": {
 | 
			
		||||
      "@/*": ["./src/*"]
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								pkg/sideload/view/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								pkg/sideload/view/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
{
 | 
			
		||||
  "files": [],
 | 
			
		||||
  "references": [
 | 
			
		||||
    {
 | 
			
		||||
      "path": "./tsconfig.node.json"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "path": "./tsconfig.app.json"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								pkg/sideload/view/tsconfig.node.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								pkg/sideload/view/tsconfig.node.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
{
 | 
			
		||||
  "extends": "@tsconfig/node18/tsconfig.json",
 | 
			
		||||
  "include": [
 | 
			
		||||
    "vite.config.*",
 | 
			
		||||
    "vitest.config.*",
 | 
			
		||||
    "cypress.config.*",
 | 
			
		||||
    "nightwatch.conf.*",
 | 
			
		||||
    "playwright.config.*"
 | 
			
		||||
  ],
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "composite": true,
 | 
			
		||||
    "noEmit": true,
 | 
			
		||||
    "module": "ESNext",
 | 
			
		||||
    "moduleResolution": "Bundler",
 | 
			
		||||
    "types": ["node"]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								pkg/sideload/view/unocss.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								pkg/sideload/view/unocss.config.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
import { defineConfig, presetUno } from "unocss"
 | 
			
		||||
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
  presets: [presetUno({ preflight: false })]
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										24
									
								
								pkg/sideload/view/vite.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								pkg/sideload/view/vite.config.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
import { fileURLToPath, URL } from "node:url"
 | 
			
		||||
 | 
			
		||||
import { defineConfig } from "vite"
 | 
			
		||||
import vue from "@vitejs/plugin-vue"
 | 
			
		||||
import unocss from "unocss/vite"
 | 
			
		||||
 | 
			
		||||
// https://vitejs.dev/config/
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
  plugins: [
 | 
			
		||||
    vue(),
 | 
			
		||||
    unocss()
 | 
			
		||||
  ],
 | 
			
		||||
  resolve: {
 | 
			
		||||
    alias: {
 | 
			
		||||
      "@": fileURLToPath(new URL("./src", import.meta.url))
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  server: {
 | 
			
		||||
    proxy: {
 | 
			
		||||
      "/webhooks": "http://127.0.0.1:81",
 | 
			
		||||
      "/cgi": "http://127.0.0.1:81"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										2904
									
								
								pkg/sideload/view/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2904
									
								
								pkg/sideload/view/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -2,12 +2,25 @@ package sign
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/samber/lo"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ProcessConfig struct {
 | 
			
		||||
type ProcessStatus = int8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ProcessCreated = ProcessStatus(iota)
 | 
			
		||||
	ProcessStarting
 | 
			
		||||
	ProcessStarted
 | 
			
		||||
	ProcessExited
 | 
			
		||||
	ProcessFailure
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ProcessInstance struct {
 | 
			
		||||
	ID          string     `json:"id" yaml:"id"`
 | 
			
		||||
	Workdir     string     `json:"workdir" yaml:"workdir"`
 | 
			
		||||
	Command     []string   `json:"command" yaml:"command"`
 | 
			
		||||
@@ -16,9 +29,13 @@ type ProcessConfig struct {
 | 
			
		||||
	Preheat     bool       `json:"preheat" yaml:"preheat"`
 | 
			
		||||
 | 
			
		||||
	Cmd    *exec.Cmd       `json:"-"`
 | 
			
		||||
	Logger strings.Builder `json:"-"`
 | 
			
		||||
 | 
			
		||||
	Status ProcessStatus `json:"status"`
 | 
			
		||||
	Logs   string        `json:"logs"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *ProcessConfig) BootProcess() error {
 | 
			
		||||
func (v *ProcessInstance) BootProcess() error {
 | 
			
		||||
	if v.Cmd != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -43,7 +60,7 @@ func (v *ProcessConfig) BootProcess() error {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *ProcessConfig) PrepareProcess() error {
 | 
			
		||||
func (v *ProcessInstance) PrepareProcess() error {
 | 
			
		||||
	for _, script := range v.Prepares {
 | 
			
		||||
		if len(script) <= 0 {
 | 
			
		||||
			continue
 | 
			
		||||
@@ -57,7 +74,7 @@ func (v *ProcessConfig) PrepareProcess() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *ProcessConfig) StartProcess() error {
 | 
			
		||||
func (v *ProcessInstance) StartProcess() error {
 | 
			
		||||
	if len(v.Command) <= 0 {
 | 
			
		||||
		return fmt.Errorf("you need set the command for %s to enable process manager", v.ID)
 | 
			
		||||
	}
 | 
			
		||||
@@ -65,11 +82,28 @@ func (v *ProcessConfig) StartProcess() error {
 | 
			
		||||
	v.Cmd = exec.Command(v.Command[0], v.Command[1:]...)
 | 
			
		||||
	v.Cmd.Dir = filepath.Join(v.Workdir)
 | 
			
		||||
	v.Cmd.Env = append(v.Cmd.Env, v.Environment...)
 | 
			
		||||
	v.Cmd.Stdout = &v.Logger
 | 
			
		||||
	v.Cmd.Stderr = &v.Logger
 | 
			
		||||
 | 
			
		||||
	// Monitor
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			if v.Cmd.Process == nil || v.Cmd.ProcessState == nil {
 | 
			
		||||
				v.Status = ProcessStarting
 | 
			
		||||
			} else if !v.Cmd.ProcessState.Exited() {
 | 
			
		||||
				v.Status = ProcessStarted
 | 
			
		||||
			} else {
 | 
			
		||||
				v.Status = lo.Ternary(v.Cmd.ProcessState.Success(), ProcessExited, ProcessFailure)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			time.Sleep(100 * time.Millisecond)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return v.Cmd.Start()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *ProcessConfig) StopProcess() error {
 | 
			
		||||
func (v *ProcessInstance) StopProcess() error {
 | 
			
		||||
	if v.Cmd != nil && v.Cmd.Process != nil {
 | 
			
		||||
		if err := v.Cmd.Process.Signal(os.Interrupt); err != nil {
 | 
			
		||||
			v.Cmd.Process.Kill()
 | 
			
		||||
@@ -82,8 +116,13 @@ func (v *ProcessConfig) StopProcess() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *ProcessInstance) GetLogs() string {
 | 
			
		||||
	v.Logs = v.Logger.String()
 | 
			
		||||
	return v.Logs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *RoadApp) PreheatProcesses(callbacks ...func(total int, success int)) {
 | 
			
		||||
	var processes []*ProcessConfig
 | 
			
		||||
	var processes []*ProcessInstance
 | 
			
		||||
	for _, site := range v.Sites {
 | 
			
		||||
		for _, process := range site.Processes {
 | 
			
		||||
			if process.Preheat {
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ import (
 | 
			
		||||
	"github.com/valyala/fasthttp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func makeHypertextResponse(c *fiber.Ctx, upstream *UpstreamConfig) error {
 | 
			
		||||
func makeHypertextResponse(c *fiber.Ctx, upstream *UpstreamInstance) error {
 | 
			
		||||
	timeout := time.Duration(viper.GetInt64("performance.network_timeout")) * time.Millisecond
 | 
			
		||||
	return proxy.Do(c, upstream.MakeURI(c), &fasthttp.Client{
 | 
			
		||||
		ReadTimeout:  timeout,
 | 
			
		||||
@@ -26,7 +26,7 @@ func makeHypertextResponse(c *fiber.Ctx, upstream *UpstreamConfig) error {
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeFileResponse(c *fiber.Ctx, upstream *UpstreamConfig) error {
 | 
			
		||||
func makeFileResponse(c *fiber.Ctx, upstream *UpstreamInstance) error {
 | 
			
		||||
	uri, queries := upstream.GetRawURI()
 | 
			
		||||
	root := http.Dir(uri)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -44,13 +44,13 @@ type RequestTransformerConfig = transformers.RequestTransformerConfig
 | 
			
		||||
 | 
			
		||||
type SiteConfig struct {
 | 
			
		||||
	ID           string                      `json:"id"`
 | 
			
		||||
	Rules        []*RouterRuleConfig         `json:"rules" yaml:"rules"`
 | 
			
		||||
	Rules        []*RouterRule               `json:"rules" yaml:"rules"`
 | 
			
		||||
	Transformers []*RequestTransformerConfig `json:"transformers" yaml:"transformers"`
 | 
			
		||||
	Upstreams    []*UpstreamConfig           `json:"upstreams" yaml:"upstreams"`
 | 
			
		||||
	Processes    []*ProcessConfig            `json:"processes" yaml:"processes"`
 | 
			
		||||
	Upstreams    []*UpstreamInstance         `json:"upstreams" yaml:"upstreams"`
 | 
			
		||||
	Processes    []*ProcessInstance          `json:"processes" yaml:"processes"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RouterRuleConfig struct {
 | 
			
		||||
type RouterRule struct {
 | 
			
		||||
	Host    []string            `json:"host" yaml:"host"`
 | 
			
		||||
	Path    []string            `json:"path" yaml:"path"`
 | 
			
		||||
	Queries map[string]string   `json:"queries" yaml:"queries"`
 | 
			
		||||
 
 | 
			
		||||
@@ -15,12 +15,12 @@ const (
 | 
			
		||||
	UpstreamTypeUnknown   = "unknown"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UpstreamConfig struct {
 | 
			
		||||
type UpstreamInstance struct {
 | 
			
		||||
	ID  string `json:"id" yaml:"id"`
 | 
			
		||||
	URI string `json:"uri" yaml:"uri"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *UpstreamConfig) GetType() string {
 | 
			
		||||
func (v *UpstreamInstance) GetType() string {
 | 
			
		||||
	protocol := strings.SplitN(v.URI, "://", 2)[0]
 | 
			
		||||
	switch protocol {
 | 
			
		||||
	case "file", "files":
 | 
			
		||||
@@ -32,7 +32,7 @@ func (v *UpstreamConfig) GetType() string {
 | 
			
		||||
	return UpstreamTypeUnknown
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *UpstreamConfig) GetRawURI() (string, url.Values) {
 | 
			
		||||
func (v *UpstreamInstance) GetRawURI() (string, url.Values) {
 | 
			
		||||
	uri := strings.SplitN(v.URI, "://", 2)[1]
 | 
			
		||||
	data := strings.SplitN(uri, "?", 2)
 | 
			
		||||
	data = append(data, " ") // Make data array least have two element
 | 
			
		||||
@@ -41,7 +41,7 @@ func (v *UpstreamConfig) GetRawURI() (string, url.Values) {
 | 
			
		||||
	return data[0], qs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *UpstreamConfig) MakeURI(ctx *fiber.Ctx) string {
 | 
			
		||||
func (v *UpstreamInstance) MakeURI(ctx *fiber.Ctx) string {
 | 
			
		||||
	var queries []string
 | 
			
		||||
	for k, v := range ctx.Queries() {
 | 
			
		||||
		parsed, _ := url.QueryUnescape(v)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user