Moving server files to ./server

This commit is contained in:
Corteza Monorepo Migrator
2022-11-14 09:26:39 +01:00
parent 204738f7c9
commit 683c7c56e2
5750 changed files with 0 additions and 26430 deletions
+14
View File
@@ -0,0 +1,14 @@
/* 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/recommended",
],
"env": {
"vue/setup-compiler-macros": true
}
}
+30
View 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
!dist/.placeholder
coverage
*.local
public/config.js
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+2
View File
@@ -0,0 +1,2 @@
Placeholder file to avoid broken build when dist is empty (go error: contains no embeddable files)
File is copied from the public to dist when web console app is built.
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/assets/favicon32x32.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Corteza Server Web Console</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+41
View File
@@ -0,0 +1,41 @@
{
"name": "corteza-server-web-console",
"license": "Apache-2.0",
"version": "0.0.0",
"contributors": [
"Denis Arh <denis.arh@crust.tech>"
],
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview --port 5050",
"test:unit": "vitest --environment jsdom",
"typecheck": "vue-tsc --noEmit && vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"axios": "^0.26.0",
"pinia": "^2.0.11",
"sass": "^1.49.7",
"vue": "^3.2.29",
"vue-router": "^4.0.12"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.0",
"@types/jsdom": "^16.2.14",
"@types/node": "^16.11.22",
"@vitejs/plugin-vue": "^2.1.0",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
"@vue/test-utils": "^2.0.0-rc.18",
"@vue/tsconfig": "^0.1.3",
"eslint": "^8.5.0",
"eslint-plugin-vue": "^8.2.0",
"jsdom": "^19.0.0",
"prettier": "^2.5.1",
"typescript": "~4.5.5",
"vite": "^2.9.13",
"vitest": "^0.2.5",
"vue-tsc": "^0.31.1"
}
}
+2
View File
@@ -0,0 +1,2 @@
Placeholder file to avoid broken build when dist is empty (go error: contains no embeddable files)
File is copied from the public to dist when web console app is built.
+27
View File
@@ -0,0 +1,27 @@
package webconsole
import (
"embed"
"io/fs"
"github.com/cortezaproject/corteza-server/pkg/http"
"github.com/go-chi/chi/v5"
)
var (
// we need combination of go:embed dist/* and .placeholder
// file inside. If only dist (w/o) wildcard is used,
// dot-file (.placeholder) will be ignored
//go:embed dist/*
assets embed.FS
)
func Mount(r chi.Router) error {
assets, err := fs.Sub(assets, "dist")
if err != nil {
panic(err)
}
return http.MountSPA(r, "/ui", assets, http.UrlPrefix("/console/ui"))
}
+56
View File
@@ -0,0 +1,56 @@
<template>
<header>
<div class="wrapper">
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/log-viewer">Logs</RouterLink>
</nav>
</div>
</header>
<div
v-if="capturedError"
class="error"
>
<h2>Error</h2>
<p v-html="capturedError"></p>
</div>
<main
v-else
>
<router-view />
</main>
</template>
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import { onBeforeMount, ref } from 'vue'
const capturedError = ref<string | undefined>(undefined)
onBeforeMount(() => {
//
})
</script>
<style lang="scss">
@import '@/assets/base.css';
nav {
background: black;
padding: 15px;
font-size: 140%;
a {
margin-right: 20px;
color: white;
text-decoration: none;
}
}
.error {
background: #ffa9a9;
padding: 40px;
height: 100vh;
}
</style>
+5
View File
@@ -0,0 +1,5 @@
html, body {
margin: 0;
padding: 0;
font-family: monospace;
}
+53
View File
@@ -0,0 +1,53 @@
import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig } from 'axios'
export interface LogEntry {
ts: Date
level: string
logger: string
msg: string
index: number
extra?: Record<string, unknown>
}
interface LogEntryResponse extends Omit<LogEntry, 'ts'> {
ts: string
[_:string]: unknown
}
interface LogFetchParams {
after?: number
limit?: number
}
let baseURL = '/console'
const storedBaseURL = window.localStorage.getItem('console-api-base-url')
if (storedBaseURL !== null) {
baseURL = storedBaseURL
console.warn('using base URL from local store (key: console-api-base-url)', { baseURL })
}
export async function fetchLoggedEvents (params: LogFetchParams = {}): Promise<Array<LogEntry>> {
const config: AxiosRequestConfig = {
params
}
return api()
.get<Array<LogEntryResponse>>('/server-log-feed.json', config)
.then(({ data }) => {
return data.map(({ ts, index, level, logger, msg, ...extra }) => ({
ts: new Date(Date.parse(ts)),
index,
level,
logger,
msg,
extra: Object.getOwnPropertyNames(extra).length > 0 ? extra : undefined
}))
})
}
function api(): AxiosInstance {
return axios.create({ baseURL })
}
+12
View File
@@ -0,0 +1,12 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './WebConsole.vue'
import router from './views'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
+16
View File
@@ -0,0 +1,16 @@
import { defineStore } from 'pinia'
export const useCounterStore = defineStore({
id: 'counter',
state: () => ({
counter: 0
}),
getters: {
doubleCount: (state) => state.counter * 2
},
actions: {
increment() {
this.counter++
}
}
})
+10
View File
@@ -0,0 +1,10 @@
<template>
<section>
<h1>Welcome to Corteza Server Web Console.</h1>
</section>
</template>
<style lang="scss">
section {
padding: 20px;
}
</style>
+133
View File
@@ -0,0 +1,133 @@
<template>
<section>
<h1>Log viewer</h1>
<table>
<thead>
<tr>
<td>one</td>
<td>level</td>
<td>logger</td>
<td>message</td>
</tr>
</thead>
<tfoot v-if="lastRefresh">
<tr>
<td
colspan="4"
class="last-refresh"
>
{{ lastRefresh.toISOString().substring(11) }}
</td>
</tr>
</tfoot>
<tbody>
<tr
v-for="e in events"
:key="e.index"
:class="[`level-${e.level}`]"
>
<td class="ts">{{ e.ts.toISOString().substring(11) }}</td>
<td class="level">{{ e.level }}</td>
<td class="logger">{{ e.logger }}</td>
<td class="msg">
{{ e.msg }}
<pre v-if="e.extra">{{ e.extra }}</pre>
</td>
</tr>
</tbody>
</table>
</section>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { fetchLoggedEvents } from '@/libs/logs'
import type { LogEntry } from '@/libs/logs'
let intervalHandler: number | undefined
const events = ref<Array<LogEntry>>([])
const lastRefresh = ref<Date | undefined>()
onMounted(() => {
events.value = []
fetch().then((interval: boolean) => {
let ih: number
if (!interval) {
return
}
ih = setInterval(async () => {
let after: number | undefined
if (events.value.length > 0) {
after = events.value[events.value.length - 1].index
}
const ok = await fetch(after)
if (!ok) {
clearInterval(ih)
}
}, 2000)
}).catch((err ) => {
alert(err)
})
})
async function fetch (after?: number): Promise<boolean> {
return fetchLoggedEvents({ after, limit: -1 })
.then(ee => {
events.value.push(...ee)
lastRefresh.value = new Date()
return true
})
.catch((err) => {
alert(err)
return false
})
}
onUnmounted(() => {
if (intervalHandler) {
clearInterval(intervalHandler)
}
})
</script>
<style lang="scss" scoped>
table {
width: 100vw;
font-family: monospace;
tbody {
tr {
&.level-warn {
background: gold;
}
&.level-debug .msg {
color: gray;
}
td {
padding: 3px;
margin: 0;
border-top: 1px dotted silver;
vertical-align: top;
&.msg pre {
color: gray;
}
}
}
}
tfoot {
.last-refresh {
padding: 20px;
text-align: center;
}
}
}
</style>
+23
View File
@@ -0,0 +1,23 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from './HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/log-viewer',
name: 'log-viewer',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('./LogViewer.vue')
}
]
})
export default router
+20
View File
@@ -0,0 +1,20 @@
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"references": [
{
"path": "./tsconfig.vite-config.json"
},
{
"path": "./tsconfig.vitest.json"
}
]
}
@@ -0,0 +1,8 @@
{
"extends": "@vue/tsconfig/tsconfig.node.json",
"include": ["vite.config.*"],
"compilerOptions": {
"composite": true,
"types": ["node", "vitest"]
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"extends": "@vue/tsconfig/tsconfig.node.json",
"include": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"types": ["node", "jsdom"]
}
}
+17
View File
@@ -0,0 +1,17 @@
import { fileURLToPath, URL } from "url";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
base: '/console/ui/',
plugins: [vue()],
resolve: {
alias: {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
File diff suppressed because it is too large Load Diff