mirror of
https://github.com/volodymyrsmirnov/MalwareMultiScan.git
synced 2025-08-23 21:12:22 +00:00
finished the first version of UI, refactoring and facelifts are pending
This commit is contained in:
parent
e56f95e3a6
commit
61224d5cb5
@ -1,6 +1,7 @@
|
||||
using MalwareMultiScan.Api.Extensions;
|
||||
using MalwareMultiScan.Api.Services;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@ -18,6 +19,11 @@ namespace MalwareMultiScan.Api
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddDockerForwardedHeadersOptions();
|
||||
|
||||
services.Configure<KestrelServerOptions>(options =>
|
||||
{
|
||||
options.Limits.MaxRequestBodySize = _configuration.GetValue<int>("MaxFileSize");
|
||||
});
|
||||
|
||||
services.AddMongoDb(_configuration);
|
||||
services.AddRabbitMq(_configuration);
|
||||
|
@ -15,6 +15,6 @@
|
||||
|
||||
"DatabaseName": "MalwareMultiScan",
|
||||
"ResultsSubscriptionId": "mms.results",
|
||||
"MaxFileSize": 1048576,
|
||||
"MaxFileSize": 52428800,
|
||||
"BackendsConfiguration": "backends.yaml"
|
||||
}
|
||||
|
@ -25,9 +25,12 @@ namespace MalwareMultiScan.Backends.Backends.Implementations
|
||||
return Scan();
|
||||
}
|
||||
|
||||
private static Task<string[]> Scan()
|
||||
private static async Task<string[]> Scan()
|
||||
{
|
||||
return Task.FromResult(new[] {"Malware.Dummy.Result"});
|
||||
await Task.Delay(
|
||||
TimeSpan.FromSeconds(5));
|
||||
|
||||
return new[] {"Malware.Dummy.Result"};
|
||||
}
|
||||
}
|
||||
}
|
79
MalwareMultiScan.Ui/components/scan-result-component.vue
Normal file
79
MalwareMultiScan.Ui/components/scan-result-component.vue
Normal file
@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="p-2 w-25">
|
||||
<div :class="cardClass" class="card">
|
||||
<h6 class="card-header">{{ id }}</h6>
|
||||
|
||||
<div class="card-body">
|
||||
<b-skeleton-wrapper :loading="!result.completed">
|
||||
<template #loading>
|
||||
<b-skeleton/>
|
||||
</template>
|
||||
|
||||
<div v-if="result.succeeded && !result.threats">No threats have been detected...</div>
|
||||
<div v-if="result.succeeded === false">Scanning failed to complete...</div>
|
||||
|
||||
<ul v-if="result.threats" class="list-inline m-0">
|
||||
<li v-for="threat in result.threats" v-bind:key="threat">{{ threat }}</li>
|
||||
</ul>
|
||||
</b-skeleton-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue, {PropOptions} from 'vue'
|
||||
|
||||
import ScanResultEntry from '~/models/scan-result-entry';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'scan-result-component',
|
||||
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
} as PropOptions<string>,
|
||||
|
||||
result: {
|
||||
type: Object,
|
||||
required: true
|
||||
} as PropOptions<ScanResultEntry>
|
||||
},
|
||||
|
||||
computed: {
|
||||
cardClass() {
|
||||
const result = this.result as ScanResultEntry;
|
||||
|
||||
return {
|
||||
'succeeded': result.succeeded && !result.threats,
|
||||
'detected': result.succeeded && result.threats,
|
||||
'failed': result.succeeded === false,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import 'node_modules/bootstrap/scss/bootstrap.scss';
|
||||
|
||||
.card-header {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.succeeded {
|
||||
@extend .bg-success;
|
||||
@extend .text-white;
|
||||
}
|
||||
|
||||
.detected {
|
||||
@extend .bg-danger;
|
||||
@extend .text-white;
|
||||
}
|
||||
|
||||
.failed {
|
||||
@extend .bg-warning;
|
||||
@extend .text-white;
|
||||
}
|
||||
</style>
|
5
MalwareMultiScan.Ui/models/scan-result-entry.ts
Normal file
5
MalwareMultiScan.Ui/models/scan-result-entry.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default interface ScanResultEntry {
|
||||
readonly completed: boolean,
|
||||
readonly succeeded: boolean | null,
|
||||
readonly threats: string[]
|
||||
}
|
6
MalwareMultiScan.Ui/models/scan-result.ts
Normal file
6
MalwareMultiScan.Ui/models/scan-result.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import ScanResultEntry from "~/models/scan-result-entry";
|
||||
|
||||
export default interface ScanResult {
|
||||
readonly id: string,
|
||||
readonly results: { [id: string]: ScanResultEntry; }
|
||||
}
|
1315
MalwareMultiScan.Ui/package-lock.json
generated
1315
MalwareMultiScan.Ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -19,6 +19,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/types": "^2.14.6",
|
||||
"@nuxt/typescript-build": "^2.0.3"
|
||||
"@nuxt/typescript-build": "^2.0.3",
|
||||
"node-sass": "^4.14.1",
|
||||
"sass-loader": "^10.0.4"
|
||||
}
|
||||
}
|
||||
|
45
MalwareMultiScan.Ui/pages/_id/index.vue
Normal file
45
MalwareMultiScan.Ui/pages/_id/index.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="d-flex results align-content-stretch flex-wrap">
|
||||
<scan-result-component v-for="(result, id) in data.results"
|
||||
v-bind:key="id" v-bind:id="id" v-bind:result="result" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.results {
|
||||
margin: -0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
import ScanResult from '~/models/scan-result';
|
||||
|
||||
export default Vue.extend({
|
||||
async asyncData({params, $axios}) {
|
||||
return { data: await $axios.$get<ScanResult>(`results/${params.id}`) }
|
||||
},
|
||||
|
||||
created() {
|
||||
this.timer = setInterval(this.getData, 1000 * 5);
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async getData() {
|
||||
this.data = await this.$axios.$get<ScanResult>(`results/${this.$route.params.id}`);
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
data: null as ScanResult | null,
|
||||
timer: 0 as any
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,4 +1,91 @@
|
||||
<template>
|
||||
<scan-form />
|
||||
<b-card no-body>
|
||||
<b-tabs card pills>
|
||||
<b-tab title="File Scan" active>
|
||||
<b-input-group>
|
||||
<b-form-file placeholder="Select or drop files here" v-model="file"/>
|
||||
<b-input-group-append>
|
||||
<b-button variant="primary" :disabled="!file || uploading" @click="scanFile">Scan</b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
|
||||
<b-progress class="mt-3" v-if="uploading" :value="progress" animated />
|
||||
</b-tab>
|
||||
|
||||
<b-tab title="URL Scan">
|
||||
<b-input-group>
|
||||
<b-input type="url" placeholder="https://secure.eicar.org/eicar.com.txt" v-model="url"/>
|
||||
<b-input-group-append>
|
||||
<b-button variant="primary" :disabled="!isUrl(url)" @click="scanUrl">Scan</b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</b-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
import ScanResult from '~/models/scan-result';
|
||||
|
||||
export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
uploading: false,
|
||||
progress: 0,
|
||||
|
||||
file: null as File | null,
|
||||
url: '' as string,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
isUrl(url: string) : boolean {
|
||||
return /(http|https):\/\/(\w+:?\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@\-\/]))?/.test(url);
|
||||
},
|
||||
|
||||
async scanFile() {
|
||||
if (!this.file || this.uploading)
|
||||
return
|
||||
|
||||
const data = new FormData();
|
||||
|
||||
data.append('file', this.file, this.file.name);
|
||||
|
||||
try
|
||||
{
|
||||
this.uploading = true;
|
||||
|
||||
const result = await this.$axios.$post<ScanResult>('queue/file', data, {
|
||||
onUploadProgress: (progressEvent) => {
|
||||
if (progressEvent.total == 0)
|
||||
return;
|
||||
|
||||
this.progress = progressEvent.loaded / progressEvent.total * 100;
|
||||
}
|
||||
});
|
||||
|
||||
await this.$router.push({name: 'id', params: {id: result.id}});
|
||||
}
|
||||
finally {
|
||||
this.progress = 0;
|
||||
this.uploading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async scanUrl() {
|
||||
if (!this.isUrl(this.url))
|
||||
return;
|
||||
|
||||
const data = new FormData();
|
||||
|
||||
data.append('url', this.url);
|
||||
|
||||
const result = await this.$axios.$post<ScanResult>('queue/url', data);
|
||||
|
||||
await this.$router.push({name: 'id', params: {id: result.id}});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -25,7 +25,8 @@
|
||||
},
|
||||
"types": [
|
||||
"@types/node",
|
||||
"@nuxt/types"
|
||||
"@nuxt/types",
|
||||
"@nuxtjs/axios"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
|
Loading…
x
Reference in New Issue
Block a user