Skip to content

Commit

Permalink
V 1.1.4
Browse files Browse the repository at this point in the history
- Add shutdown denoman
- Introducing Terminal
- Minor fix to search box
- Minor fix to expanded Tr (v-if instead of v-show)
- Styling scrollbar
  • Loading branch information
fakoua committed Feb 19, 2024
1 parent 91c64bd commit 54aec4a
Show file tree
Hide file tree
Showing 11 changed files with 5,212 additions and 4,907 deletions.
30 changes: 24 additions & 6 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { oakCors } from "https://deno.land/x/cors@v1.2.2/mod.ts";

import { actions, getDependsServices, getServices } from "./src/services.ts";
import { getPerfmon, getSystem } from "./src/system.ts";
import { executeCommand } from "./src/command.ts";
import * as cache from "./src/memcache.ts";
import {
DependenciesModel,
Expand All @@ -20,6 +21,14 @@ await initServer(await getWwwRoot());

const router = new Router();

router.get("/api/exit", (ctx) => {
ctx.response.body = true;
console.log("Exiting...");
setTimeout(() => {
Deno.exit(0);
}, 200);
});

router.get("/api/:apiName", async (ctx) => {
const payload: WinRMPayload = {
username: ctx.request.url.searchParams.get("username")!,
Expand Down Expand Up @@ -101,12 +110,21 @@ router.post("/api/service", async (ctx) => {
ctx.response.body = service;
});

router.put("/api/service", (ctx) => {
ctx.response.body = "Received a PUT HTTP method";
});

router.delete("/api/service", (ctx) => {
ctx.response.body = "Received a DELETE HTTP method";
router.post("/api/command", async (ctx) => {
const payload: WinRMPayload = {
username: ctx.request.url.searchParams.get("username")!,
password: ctx.request.url.searchParams.get("password")!,
protocol: (ctx.request.url.searchParams.get("protocol")!),
hostname: ctx.request.url.searchParams.get("hostname")!,
port: Number(ctx.request.url.searchParams.get("port")!),
};
const req = await ctx.request.body.json();
const res = await executeCommand(
payload,
req.command,
req.isPowerShell,
);
ctx.response.body = res;
});

const app = new Application();
Expand Down
6 changes: 3 additions & 3 deletions q-manui/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "q-manui",
"version": "1.1.3",
"description": "DenoMan 1.1.3",
"version": "1.1.4",
"description": "DenoMan 1.1.4",
"productName": "DenoMan",
"author": "Sameh Fakoua <s.fakoua@gmail.com>",
"private": true,
Expand Down Expand Up @@ -50,4 +50,4 @@
"npm": ">= 6.13.4",
"yarn": ">= 1.21.1"
}
}
}
6 changes: 6 additions & 0 deletions q-manui/src/components/ServerComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<q-tab name="dashboard" icon="dashboard" />
<q-tab name="performance" icon="insights" />
<q-tab name="services" icon="miscellaneous_services" />
<q-tab name="terminal" icon="terminal" />
</q-tabs>
</div>
<div class="s-right col-grow">
Expand Down Expand Up @@ -54,6 +55,9 @@
</template>
</q-splitter>
</q-tab-panel>
<q-tab-panel name="terminal">
<terminal-component :host="host" />
</q-tab-panel>
</q-tab-panels>
</div>
</div>
Expand Down Expand Up @@ -167,6 +171,7 @@ import ServiceDetailsComponent from '../components/service-window/ServiceDetails
import ServiceWindowComponent from '../components/service-window/ServiceWindowComponent.vue';
import PerfmonComponent from '../components/performance/PerfmonComponent.vue';
import DashboardComponent from '../components/dashboard/DashboardComponent.vue';
import TerminalComponent from '../components/terminal/TerminalComponent.vue';

import { ServiceModel, WinRMPayload } from './models';

Expand All @@ -178,6 +183,7 @@ export default defineComponent({
ServiceWindowComponent,
DashboardComponent,
PerfmonComponent,
TerminalComponent,
},
props: {
host: {
Expand Down
11 changes: 11 additions & 0 deletions q-manui/src/components/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,14 @@ export type PerfmonModel = {
disks: DiskModel[];
networks: NetworkModel;
};

export type ShellResponse = {
stdout: string,
stderr: string,
exitCode: number,
state?: string,
error?: {
reason?: string,
message?: string
}
};
11 changes: 11 additions & 0 deletions q-manui/src/components/service-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
DependenciesModel,
PerfmonModel,
ServiceModel,
ShellResponse,
SystemModel,
WinRMPayload,
} from './models';
Expand Down Expand Up @@ -68,3 +69,13 @@ export async function getPerfmon(params: WinRMPayload): Promise<PerfmonModel> {
const res = await api.get('http://localhost:8001/api/perfmon', { params });
return res.data as PerfmonModel;
}

export async function execCommand(params: WinRMPayload, command: string, isPowerShell: boolean): Promise<ShellResponse> {
const res = await api.post('http://localhost:8001/api/command', { command, isPowerShell }, { params });
return res.data as ShellResponse;
}

export function exitApp() {
api.get('http://localhost:8001/api/exit').catch(() => {
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@
color="grey-14"
text-color="white"
label="Ctrl+K"
v-if="!filterFocused"
v-if="!filterFocused && !filter"
/>
<q-icon name="search" v-if="filterFocused" />
<q-icon name="search" v-if="filterFocused && !filter" />
</template>
</q-input>
</template>
Expand Down Expand Up @@ -263,7 +263,7 @@
{{ col.value }}
</q-td>
</q-tr>
<q-tr v-show="props.expand" :props="props">
<q-tr v-if="props.expand" v-show="props.expand" :props="props">
<q-td colspan="100%" style="padding-left: 6px; background-color: white">
<div class="text-left">
<dependencies-row-component
Expand Down
134 changes: 134 additions & 0 deletions q-manui/src/components/terminal/TerminalComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<template>
<div class="fit row wrap justify-start items-start content-start">
<div class="col-grow">
<div>Terminal</div>
<div>
<q-input
ref="commandInput"
outlined
v-model="command"
type="textarea"
:disable="executing"
:loading="executing"
:error="errorIndicator"
>
<q-inner-loading :showing="executing">
<q-spinner-gears size="50px" color="primary" />
</q-inner-loading>
</q-input>
</div>
<div style="margin-top: 4px">
<q-btn
label="Execute as PowerShell"
color="black"
unelevated
:disable="executing"
@click="
() => {
executeCommand(true);
}
"
style="margin-right: 4px"
/>
<q-btn
color="primary"
label="Execute as Command Line"
unelevated
:disable="executing"
@click="
() => {
executeCommand(false);
}
"
/>
</div>
<div>
<div class="term">
{{ terminalResult }}
</div>
</div>
</div>
</div>
</template>

<style lang="scss" scoped>
.term {
background-color: #000;
color: #fff;
height: 460px;
overflow: auto;
padding: 10px;
font-family: monospace;
font-size: 12px;
white-space: pre-wrap;
word-wrap: break-word;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
margin-top: 20px;
margin-bottom: 10px;
}
</style>

<script lang="ts">
import { PropType, computed, defineComponent, ref } from 'vue';
import * as serviceApi from '../service-api';
import { ShellResponse, WinRMPayload } from '../models';
import { QInput } from 'quasar';
export default defineComponent({
name: 'TerminalComponent',
props: {
host: {
type: Object as PropType<WinRMPayload>,
required: true,
},
},
methods: {
async executeCommand(isPowerShell: boolean) {
this.executing = true;
this.response = await serviceApi.execCommand(
this.host,
this.command,
isPowerShell,
);
this.errorIndicator = this.response.exitCode !== 0;
window.setTimeout(() => {
this.errorIndicator = false;
}, 5000);
if (this.response.exitCode === 0) {
this.command = '';
}
this.executing = false;
window.setTimeout(() => {
(this.commandInput! as QInput).focus();
}, 100);
},
},
setup() {
const command = ref('');
const response = ref<ShellResponse>({} as ShellResponse);
const executing = ref(false);
const errorIndicator = ref(false);
const commandInput = ref(null);
const terminalResult = computed(() => {
if (response.value.exitCode === 0) {
return response.value.stdout;
} else {
return response.value.stderr;
}
});
return {
command,
commandInput,
response,
executing,
errorIndicator,
terminalResult,
};
},
});
</script>
27 changes: 26 additions & 1 deletion q-manui/src/css/app.css
Original file line number Diff line number Diff line change
@@ -1 +1,26 @@
/* app global css */
/* Works on Chrome, Edge, and Safari */
*::-webkit-scrollbar {
height: 12px;
width: 14px;
background: transparent;
z-index: 12;
overflow: visible;
}

*::-webkit-scrollbar-thumb {
width: 10px;
background-color: #00b4ff;
border-radius: 10px;
z-index: 12;
border: 4px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
-webkit-transition: background-color 0.28s ease-in-out;
transition: background-color 0.28s ease-in-out;
margin: 4px;
min-height: 32px;
min-width: 32px;
}

::-webkit-scrollbar-thumb:hover {
background: #00b4ff;
}
45 changes: 42 additions & 3 deletions q-manui/src/layouts/MainLayout.vue
Original file line number Diff line number Diff line change
@@ -1,31 +1,70 @@
<template>
<q-layout view="lHh Lpr lFf">
<q-layout v-if="!shutdown" view="lHh Lpr lFf">
<q-header elevated>
<q-toolbar>
<q-avatar>
<img src="/icons/denoman.svg" alt="DenoMan" />
</q-avatar>
<q-toolbar-title> DenoMan </q-toolbar-title>

<div>v {{ version }}</div>
<div>
v {{ version }}
<q-btn
flat
round
dense
icon="power_settings_new"
@click="confirm = true"
/>
</div>
</q-toolbar>
</q-header>
<q-page-container>
<router-view />
</q-page-container>
</q-layout>
<q-dialog v-model="confirm" persistent>
<q-card>
<q-card-section class="row items-center">
<q-avatar
icon="power_settings_new"
color="deep-orange"
text-color="white"
/>
<span class="q-ml-sm">Are you sure you want to exit DenoMan?</span>
</q-card-section>

<q-card-actions align="right">
<q-btn flat label="Cancel" color="primary" v-close-popup />
<q-btn flat label="Exit DenoMan" color="deep-orange" @click="exitApp" />
</q-card-actions>
</q-card>
</q-dialog>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, ref } from 'vue';
import * as serviceApi from '../components/service-api';
import { version } from '../../package.json';
export default defineComponent({
name: 'MainLayout',
methods: {
async exitApp() {
this.confirm = false;
serviceApi.exitApp();
this.shutdown = true;
},
},
setup() {
const confirm = ref(false);
const shutdown = ref(false);
return {
shutdown,
version,
confirm,
};
},
});
Expand Down
Loading

0 comments on commit 54aec4a

Please sign in to comment.