🎉 Init sideload panel
This commit is contained in:
		| @@ -59,7 +59,7 @@ After that, you can manage your roadsign instance with RoadSign CLI aka. RDS CLI | ||||
| To install it, run this command. (Make sure you have golang toolchain on your computer) | ||||
|  | ||||
| ```shell | ||||
| go install -buildvcs code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds@latest | ||||
| go install -buildvcs code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rdc@latest | ||||
| # Tips: Add `buildvsc` flag to provide more detail compatibility check. | ||||
| ``` | ||||
|  | ||||
|   | ||||
| @@ -8,10 +8,8 @@ import ( | ||||
| 
 | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| 
 | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds/conn" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rdc/conn" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/pelletier/go-toml/v2" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/urfave/cli/v2" | ||||
| ) | ||||
| @@ -59,7 +57,6 @@ var DeployCommands = []*cli.Command{ | ||||
| 	}, | ||||
| 	{ | ||||
| 		Name:      "sync", | ||||
| 		Aliases:   []string{"sc"}, | ||||
| 		ArgsUsage: "<server> <site> <configuration path>", | ||||
| 		Action: func(ctx *cli.Context) error { | ||||
| 			if ctx.Args().Len() < 3 { | ||||
| @@ -73,19 +70,18 @@ var DeployCommands = []*cli.Command{ | ||||
| 				return fmt.Errorf("couldn't connect server: %s", err.Error()) | ||||
| 			} | ||||
| 
 | ||||
| 			var site navi.SiteConfig | ||||
| 			var raw []byte | ||||
| 			if file, err := os.Open(ctx.Args().Get(2)); err != nil { | ||||
| 				return err | ||||
| 			} else { | ||||
| 				raw, _ := io.ReadAll(file) | ||||
| 				toml.Unmarshal(raw, &site) | ||||
| 				raw, _ = io.ReadAll(file) | ||||
| 			} | ||||
| 
 | ||||
| 			url := fmt.Sprintf("/webhooks/sync/%s", ctx.Args().Get(1)) | ||||
| 			client := fiber.Put(server.Url+url). | ||||
| 				JSONEncoder(jsoniter.ConfigCompatibleWithStandardLibrary.Marshal). | ||||
| 				JSONDecoder(jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal). | ||||
| 				JSON(site). | ||||
| 				Body(raw). | ||||
| 				BasicAuth("RoadSign CLI", server.Credential) | ||||
| 
 | ||||
| 			if status, data, err := client.Bytes(); len(err) > 0 { | ||||
| @@ -4,8 +4,8 @@ import ( | ||||
| 	"os" | ||||
| 
 | ||||
| 	roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds/conn" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds/deploy" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rdc/conn" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rdc/deploy" | ||||
| 	"github.com/rs/zerolog" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/spf13/viper" | ||||
| @@ -1,3 +0,0 @@ | ||||
| /dist | ||||
| /node_modules | ||||
| /*.lock | ||||
| @@ -1,15 +0,0 @@ | ||||
| /* 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' | ||||
|   } | ||||
| } | ||||
							
								
								
									
										8
									
								
								pkg/sideload/view/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								pkg/sideload/view/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -8,23 +8,17 @@ 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 | ||||
| .DS_Store | ||||
| *.suo | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| *.sw? | ||||
|  | ||||
| *.tsbuildinfo | ||||
|   | ||||
| @@ -1,8 +0,0 @@ | ||||
| { | ||||
|   "$schema": "https://json.schemastore.org/prettierrc", | ||||
|   "semi": false, | ||||
|   "tabWidth": 2, | ||||
|   "singleQuote": false, | ||||
|   "printWidth": 120, | ||||
|   "trailingComma": "none" | ||||
| } | ||||
							
								
								
									
										8
									
								
								pkg/sideload/view/.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								pkg/sideload/view/.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +0,0 @@ | ||||
| { | ||||
|   "recommendations": [ | ||||
|     "Vue.volar", | ||||
|     "Vue.vscode-typescript-vue-plugin", | ||||
|     "dbaeumer.vscode-eslint", | ||||
|     "esbenp.prettier-vscode" | ||||
|   ] | ||||
| } | ||||
| @@ -1,46 +0,0 @@ | ||||
| # @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 | ||||
| ``` | ||||
| @@ -1,13 +1,13 @@ | ||||
| <!DOCTYPE html> | ||||
| <!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> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <link rel="icon" type="image/svg+xml" href="/vite.svg" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>Vite + Solid + TS</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <div id="app"></div> | ||||
|     <script type="module" src="/src/main.ts"></script> | ||||
|     <div id="root"></div> | ||||
|     <script type="module" src="/src/index.tsx"></script> | ||||
|   </body> | ||||
| </html> | ||||
|   | ||||
| @@ -1,44 +1,24 @@ | ||||
| { | ||||
|   "name": "@roadsign/sideload-ui", | ||||
|   "version": "0.0.0", | ||||
|   "name": "roadsign-sideload", | ||||
|   "private": true, | ||||
|   "version": "0.0.0", | ||||
|   "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/" | ||||
|     "build": "tsc && vite build", | ||||
|     "preview": "vite preview" | ||||
|   }, | ||||
|   "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" | ||||
|     "@solidjs/router": "^0.10.10", | ||||
|     "solid-js": "^1.8.7" | ||||
|   }, | ||||
|   "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" | ||||
|     "autoprefixer": "^10.4.17", | ||||
|     "daisyui": "^4.6.0", | ||||
|     "postcss": "^8.4.33", | ||||
|     "tailwindcss": "^3.4.1", | ||||
|     "typescript": "^5.2.2", | ||||
|     "vite": "^5.0.8", | ||||
|     "vite-plugin-solid": "^2.8.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								pkg/sideload/view/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								pkg/sideload/view/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| export default { | ||||
|   plugins: { | ||||
|     tailwindcss: {}, | ||||
|     autoprefixer: {}, | ||||
|   }, | ||||
| } | ||||
							
								
								
									
										1
									
								
								pkg/sideload/view/public/vite.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/sideload/view/public/vite.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> | ||||
| After Width: | Height: | Size: 1.5 KiB | 
| @@ -1,6 +0,0 @@ | ||||
| @import "vfonts/IBMPlexSans.css"; | ||||
| @import "vfonts/IBMPlexMono.css"; | ||||
|  | ||||
| a { | ||||
|     color: #3f7ee8; | ||||
| } | ||||
| @@ -1,135 +0,0 @@ | ||||
| <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 | undefined>(undefined) | ||||
|  | ||||
| async function editConfig() { | ||||
|   const resp = await fetch(`/cgi/sites/cfg/${props.id}`) | ||||
|   config.value = await resp.text() | ||||
|   editing.value = true | ||||
| } | ||||
|  | ||||
| async function syncConfig() { | ||||
|   if (config.value == null) return | ||||
|  | ||||
|   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> | ||||
| @@ -1,110 +0,0 @@ | ||||
| <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> | ||||
| @@ -1,36 +0,0 @@ | ||||
| <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> | ||||
| @@ -1,76 +0,0 @@ | ||||
| <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: any[] = [ | ||||
|   { | ||||
|     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> | ||||
							
								
								
									
										3
									
								
								pkg/sideload/view/src/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								pkg/sideload/view/src/index.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| @tailwind base; | ||||
| @tailwind components; | ||||
| @tailwind utilities; | ||||
							
								
								
									
										17
									
								
								pkg/sideload/view/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								pkg/sideload/view/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import "./index.css"; | ||||
|  | ||||
| /* @refresh reload */ | ||||
| import { render } from "solid-js/web"; | ||||
|  | ||||
| import { Route, Router } from "@solidjs/router"; | ||||
|  | ||||
| import RootLayout from "./layouts/RootLayout"; | ||||
| import Dashboard from "./pages/dashboard"; | ||||
|  | ||||
| const root = document.getElementById("root"); | ||||
|  | ||||
| render(() => ( | ||||
|     <Router root={RootLayout}> | ||||
|         <Route path="/" component={Dashboard} /> | ||||
|     </Router> | ||||
| ), root!); | ||||
							
								
								
									
										11
									
								
								pkg/sideload/view/src/layouts/RootLayout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								pkg/sideload/view/src/layouts/RootLayout.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| import Navbar from "./shared/Navbar"; | ||||
|  | ||||
| export default function RootLayout(props: any) { | ||||
|   return ( | ||||
|     <div> | ||||
|       <Navbar /> | ||||
|  | ||||
|       <main class="h-[calc(100vh-64px)]">{props.children}</main> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| @@ -1,60 +0,0 @@ | ||||
| <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> | ||||
							
								
								
									
										62
									
								
								pkg/sideload/view/src/layouts/shared/Navbar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								pkg/sideload/view/src/layouts/shared/Navbar.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| import { For } from "solid-js"; | ||||
|  | ||||
| interface MenuItem { | ||||
|   label: string; | ||||
|   href: string; | ||||
| } | ||||
|  | ||||
| export default function Navbar() { | ||||
|   const nav: MenuItem[] = [{ label: "Dashboard", href: "/" }]; | ||||
|  | ||||
|   return ( | ||||
|     <div class="navbar bg-base-100 shadow-md"> | ||||
|       <div class="navbar-start"> | ||||
|         <div class="dropdown"> | ||||
|           <div tabIndex={0} role="button" class="btn btn-ghost lg:hidden"> | ||||
|             <svg | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               class="h-5 w-5" | ||||
|               fill="none" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke="currentColor" | ||||
|             > | ||||
|               <path | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 stroke-width="2" | ||||
|                 d="M4 6h16M4 12h8m-8 6h16" | ||||
|               /> | ||||
|             </svg> | ||||
|           </div> | ||||
|           <ul | ||||
|             tabIndex={0} | ||||
|             class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52" | ||||
|           > | ||||
|             <For each={nav}> | ||||
|               {(item) => ( | ||||
|                 <li> | ||||
|                   <a href={item.href}>{item.label}</a> | ||||
|                 </li> | ||||
|               )} | ||||
|             </For> | ||||
|           </ul> | ||||
|         </div> | ||||
|         <a href="/" class="btn btn-ghost text-xl"> | ||||
|           RoadSign | ||||
|         </a> | ||||
|       </div> | ||||
|       <div class="navbar-center hidden lg:flex"> | ||||
|         <ul class="menu menu-horizontal px-1"> | ||||
|           <For each={nav}> | ||||
|             {(item) => ( | ||||
|               <li> | ||||
|                 <a href={item.href}>{item.label}</a> | ||||
|               </li> | ||||
|             )} | ||||
|           </For> | ||||
|         </ul> | ||||
|       </div> | ||||
|       <div class="navbar-end"></div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| @@ -1,16 +0,0 @@ | ||||
| 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") | ||||
							
								
								
									
										10
									
								
								pkg/sideload/view/src/pages/dashboard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								pkg/sideload/view/src/pages/dashboard.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| export default function Dashboard() { | ||||
|     return ( | ||||
|         <div class="h-full w-full flex justify-center items-center"> | ||||
|             <div class="max-w-96 text-center"> | ||||
|                 <h1 class="text-2xl font-bold">Hold on</h1> | ||||
|                 <p>Our brand new sideload administration panel is still in progress. For now, you can use sideload api and roadsign cli.</p> | ||||
|             </div> | ||||
|         </div> | ||||
|     ) | ||||
| } | ||||
| @@ -1,9 +0,0 @@ | ||||
| <template> | ||||
|   <n-message-provider> | ||||
|     <router-view /> | ||||
|   </n-message-provider> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NMessageProvider } from "naive-ui" | ||||
| </script> | ||||
| @@ -1,21 +0,0 @@ | ||||
| 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 | ||||
| @@ -1,35 +0,0 @@ | ||||
| <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> | ||||
							
								
								
									
										44
									
								
								pkg/sideload/view/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								pkg/sideload/view/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| /** @type {import('tailwindcss').Config} */ | ||||
| export default { | ||||
|   content: ["./src/**/*.{js,jsx,ts,tsx}"], | ||||
|   theme: { | ||||
|     extend: {}, | ||||
|   }, | ||||
|   daisyui: { | ||||
|     themes: [ | ||||
|       { | ||||
|         light: { | ||||
|           ...require("daisyui/src/theming/themes")["light"], | ||||
|           primary: "#4750a3", | ||||
|           secondary: "#93c5fd", | ||||
|           accent: "#0f766e", | ||||
|           info: "#67e8f9", | ||||
|           success: "#15803d", | ||||
|           warning: "#f97316", | ||||
|           error: "#dc2626", | ||||
|           "--rounded-box": "0", | ||||
|           "--rounded-btn": "0", | ||||
|           "--rounded-badge": "0", | ||||
|           "--tab-radius": "0", | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         dark: { | ||||
|           ...require("daisyui/src/theming/themes")["dark"], | ||||
|           primary: "#4750a3", | ||||
|           secondary: "#93c5fd", | ||||
|           accent: "#0f766e", | ||||
|           info: "#67e8f9", | ||||
|           success: "#15803d", | ||||
|           warning: "#f97316", | ||||
|           error: "#dc2626", | ||||
|           "--rounded-box": "0", | ||||
|           "--rounded-btn": "0", | ||||
|           "--rounded-badge": "0", | ||||
|           "--tab-radius": "0", | ||||
|         }, | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   plugins: [require("daisyui")], | ||||
| }; | ||||
| @@ -1,13 +0,0 @@ | ||||
| { | ||||
|   "extends": "@vue/tsconfig/tsconfig.dom.json", | ||||
|   "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], | ||||
|   "exclude": ["src/**/__tests__/*"], | ||||
|   "compilerOptions": { | ||||
|     "composite": true, | ||||
|     "noEmit": true, | ||||
|     "baseUrl": ".", | ||||
|     "paths": { | ||||
|       "@/*": ["./src/*"] | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -1,11 +1,26 @@ | ||||
| { | ||||
|   "files": [], | ||||
|   "references": [ | ||||
|     { | ||||
|       "path": "./tsconfig.node.json" | ||||
|     }, | ||||
|     { | ||||
|       "path": "./tsconfig.app.json" | ||||
|     } | ||||
|   ] | ||||
|   "compilerOptions": { | ||||
|     "target": "ES2020", | ||||
|     "useDefineForClassFields": true, | ||||
|     "module": "ESNext", | ||||
|     "lib": ["ES2020", "DOM", "DOM.Iterable"], | ||||
|     "skipLibCheck": true, | ||||
|  | ||||
|     /* Bundler mode */ | ||||
|     "moduleResolution": "bundler", | ||||
|     "allowImportingTsExtensions": true, | ||||
|     "resolveJsonModule": true, | ||||
|     "isolatedModules": true, | ||||
|     "noEmit": true, | ||||
|     "jsx": "preserve", | ||||
|     "jsxImportSource": "solid-js", | ||||
|  | ||||
|     /* Linting */ | ||||
|     "strict": true, | ||||
|     "noUnusedLocals": true, | ||||
|     "noUnusedParameters": true, | ||||
|     "noFallthroughCasesInSwitch": true | ||||
|   }, | ||||
|   "include": ["src"], | ||||
|   "references": [{ "path": "./tsconfig.node.json" }] | ||||
| } | ||||
|   | ||||
| @@ -1,17 +1,10 @@ | ||||
| { | ||||
|   "extends": "@tsconfig/node18/tsconfig.json", | ||||
|   "include": [ | ||||
|     "vite.config.*", | ||||
|     "vitest.config.*", | ||||
|     "cypress.config.*", | ||||
|     "nightwatch.conf.*", | ||||
|     "playwright.config.*" | ||||
|   ], | ||||
|   "compilerOptions": { | ||||
|     "composite": true, | ||||
|     "noEmit": true, | ||||
|     "skipLibCheck": true, | ||||
|     "module": "ESNext", | ||||
|     "moduleResolution": "Bundler", | ||||
|     "types": ["node"] | ||||
|   } | ||||
|     "moduleResolution": "bundler", | ||||
|     "allowSyntheticDefaultImports": true | ||||
|   }, | ||||
|   "include": ["vite.config.ts"] | ||||
| } | ||||
|   | ||||
| @@ -1,5 +0,0 @@ | ||||
| import { defineConfig, presetUno } from "unocss" | ||||
|  | ||||
| export default defineConfig({ | ||||
|   presets: [presetUno({ preflight: false })] | ||||
| }) | ||||
| @@ -1,24 +1,6 @@ | ||||
| import { fileURLToPath, URL } from "node:url" | ||||
| import { defineConfig } from 'vite' | ||||
| import solid from 'vite-plugin-solid' | ||||
|  | ||||
| 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" | ||||
|     } | ||||
|   } | ||||
|   plugins: [solid()], | ||||
| }) | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user