mirror of
https://github.com/DirectoryLister/DirectoryLister.git
synced 2025-09-02 02:12:37 +02:00
Implemented file info modal
This commit is contained in:
@@ -2,12 +2,25 @@
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use RuntimeException;
|
||||
use PHLAK\Config\Config;
|
||||
use Slim\Psr7\Response;
|
||||
use SplFileInfo;
|
||||
|
||||
class FileInfoController
|
||||
{
|
||||
/** @var Config App configuration component */
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Create a new FileInfoController object.
|
||||
*
|
||||
* @param \PHLAK\Config\Config $config
|
||||
*/
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the FileInfoController.
|
||||
*
|
||||
@@ -17,10 +30,13 @@ class FileInfoController
|
||||
public function __invoke(Response $response, string $path = '.')
|
||||
{
|
||||
if (! is_file($path)) {
|
||||
throw new RuntimeException('Invalid file path', $path);
|
||||
return $response->withStatus(404, 'File not found');
|
||||
}
|
||||
|
||||
$file = new SplFileInfo($path);
|
||||
if ($file->getSize() >= $this->config->get('max_hash_size', 1000000000)) {
|
||||
return $response->withStatus(500, 'File size too large');
|
||||
}
|
||||
|
||||
$response->getBody()->write(json_encode([
|
||||
'hashes' => [
|
||||
|
@@ -53,4 +53,12 @@ return [
|
||||
* Default value: true
|
||||
*/
|
||||
'ignore_vcs_files' => true,
|
||||
|
||||
/**
|
||||
* The maximum file size (in bytes) that can be hashed. This helps to
|
||||
* prevent timeouts for excessively large files.
|
||||
*
|
||||
* Defualt value: 1000000000
|
||||
*/
|
||||
'max_hash_size' => 1000000000
|
||||
];
|
||||
|
@@ -1,36 +1,45 @@
|
||||
<a
|
||||
href="{% if parentDir %}..{% else %}{{ file.getRelativePathname }}{% endif %}"
|
||||
class="flex justify-between items-center rounded-lg p-4 hover:bg-gray-200 hover:p-4 hover:shadow"
|
||||
href="{{ parentDir ? '..' : file.getRelativePathname }}"
|
||||
class="flex flex-col items-center rounded-lg group hover:bg-gray-200 hover:shadow"
|
||||
>
|
||||
<div class="flex-shrink pr-2">
|
||||
{% if parentDir %}
|
||||
<i class="fas fa-level-up-alt fa-fw fa-lg"></i>
|
||||
{% else %}
|
||||
{{ icon(file) | raw }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="flex justify-between items-center p-4 w-full">
|
||||
<div class="flex-shrink pr-2">
|
||||
{% if parentDir %}
|
||||
<i class="fas fa-level-up-alt fa-fw fa-lg"></i>
|
||||
{% else %}
|
||||
{{ icon(file) | raw }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="flex-grow truncate mr-2">
|
||||
{% if parentDir %}
|
||||
..
|
||||
{% else %}
|
||||
{{ file.getBasename }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="flex-grow sm:mr-2">
|
||||
<div class="flex justify-between">
|
||||
<div class="truncate">
|
||||
{{ parentDir ? '..' : file.getBasename }}
|
||||
</div>
|
||||
|
||||
<div class="hidden whitespace-no-wrap text-right mx-2 w-1/6 sm:block">
|
||||
{% if parentDir or file.isDir %}
|
||||
—
|
||||
{% else %}
|
||||
{{ sizeForHumans(file.getSize) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if file.isFile %}
|
||||
<div class="ml-2">
|
||||
<button
|
||||
class="flex justify-center items-center rounded-full text-sm p-2 -m-1 invisible hover:bg-gray-400 hover:shadow group-hover:visible"
|
||||
v-on:click.prevent="showFileInfo('{{ file.getPathname }}')"
|
||||
>
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden whitespace-no-wrap text-right truncate ml-2 w-1/4 sm:block">
|
||||
{% if parentDir %}
|
||||
—
|
||||
{% else %}
|
||||
{{ file.getMTime | date }}
|
||||
{% endif %}
|
||||
<div class="hidden whitespace-no-wrap text-right mx-2 w-1/6 sm:block">
|
||||
{% if parentDir or file.isDir %}
|
||||
—
|
||||
{% else %}
|
||||
{{ sizeForHumans(file.getSize) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="hidden whitespace-no-wrap text-right truncate ml-2 w-1/4 sm:block">
|
||||
{{ parentDir ? '—' : file.getMTime | date }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
@@ -2,11 +2,13 @@
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="{{ asset('dist/app.js') }}" defer></script>
|
||||
<link rel="stylesheet" href="{{ asset('dist/app.css') }}">
|
||||
|
||||
<title>Directory Lister</title>
|
||||
|
||||
<div id="app" class="font-mono min-h-screen {{ config('dark_mode') ? 'dark-mode' : '' }}">
|
||||
<div id="app" class="font-mono min-h-screen z-0 {{ config('dark_mode') ? 'dark-mode' : '' }}">
|
||||
|
||||
<header id="header" class="block bg-blue-600 shadow sticky top-0 text-white text-sm tracking-tight">
|
||||
<div class="container mx-auto p-4">
|
||||
<a href="/" class="hover:underline">Home</a>
|
||||
@@ -57,16 +59,6 @@
|
||||
>
|
||||
<i class="fas fa-arrow-up fa-lg"></i>
|
||||
</a>
|
||||
|
||||
<file-info-modal ref="fileInfoModal"></file-info-modal>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let link = document.getElementById('scroll-to-top');
|
||||
|
||||
window.addEventListener('scroll', function() {
|
||||
if (window.scrollY > 10) {
|
||||
link.classList.remove('hidden');
|
||||
} else {
|
||||
link.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
4635
app/themes/default/package-lock.json
generated
4635
app/themes/default/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,16 +9,19 @@
|
||||
"prod": "npm run production",
|
||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.0",
|
||||
"vue": "^2.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.12.0",
|
||||
"cross-env": "^6.0.3",
|
||||
"laravel-mix": "^5.0.0",
|
||||
"laravel-mix": "^5.0.1",
|
||||
"laravel-mix-purgecss": "^4.2.0",
|
||||
"resolve-url-loader": "^3.1.1",
|
||||
"sass": "^1.23.7",
|
||||
"sass": "^1.24.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"tailwindcss": "^1.1.4",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,21 @@
|
||||
window.Vue = require('vue');
|
||||
|
||||
Vue.component('file-info-modal', require('./components/file-info-modal.vue').default);
|
||||
|
||||
const app = new Vue({
|
||||
el: "#app",
|
||||
methods: {
|
||||
showFileInfo(filePath) {
|
||||
this.$refs.fileInfoModal.show(filePath);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let link = document.getElementById('scroll-to-top');
|
||||
window.addEventListener('scroll', function() {
|
||||
if (window.scrollY > 10) {
|
||||
link.classList.remove('hidden');
|
||||
} else {
|
||||
link.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
86
app/themes/default/src/js/components/file-info-modal.vue
Normal file
86
app/themes/default/src/js/components/file-info-modal.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div
|
||||
class="fixed top-0 flex justify-center items-center w-screen h-screen p-4 z-50"
|
||||
style="background-color: hsla(218, 23%, 23%, .5)"
|
||||
v-bind:class="this.styles"
|
||||
>
|
||||
<div class="bg-white rounded-lg shadow-lg overflow-hidden" v-show="! loading">
|
||||
<div class="flex justify-between items-center bg-blue-600 p-4">
|
||||
<i class="fas fa-info-circle fa-lg text-white"></i>
|
||||
|
||||
<div class="items-center text-xl text-white font-mono mx-4">
|
||||
{{ title }}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="flex justify-center items-center rounded-full w-6 h-6 text-blue-900 text-sm hover:bg-red-700 hover:text-white hover:shadow"
|
||||
v-on:click="hide()"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center items-center p-4">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-auto">
|
||||
<tbody>
|
||||
<tr v-for="(hash, title) in this.hashes" v-bind:key="hash">
|
||||
<td class="border font-bold px-4 py-2">{{ title }}</td>
|
||||
<td class="border px-4 py-2">{{ hash }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<i class="fas fa-spinner fa-pulse fa-5x text-white" v-show="loading"></i>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const axios = require('axios').default;
|
||||
|
||||
export default {
|
||||
data: function () {
|
||||
return {
|
||||
filePath: 'file-info.txt',
|
||||
hashes: {
|
||||
'md5': '••••••••••••••••••••••••••••••••',
|
||||
'sha1': '••••••••••••••••••••••••••••••••••••••••',
|
||||
'sha256': '••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••'
|
||||
},
|
||||
loading: true,
|
||||
visible: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
styles() {
|
||||
return { 'hidden': ! this.visible };
|
||||
},
|
||||
title() {
|
||||
return this.filePath.split('/').pop();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async show(filePath) {
|
||||
this.filePath = filePath;
|
||||
this.visible = true;
|
||||
|
||||
await axios.get('/file-info/' + filePath).then(function (response) {
|
||||
this.hashes = response.data.hashes;
|
||||
this.loading = false;
|
||||
}.bind(this)).catch(
|
||||
response => this.hide() && console.error(response)
|
||||
);
|
||||
},
|
||||
hide() {
|
||||
this.visible = false;
|
||||
this.loading = true;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('keyup', e => e.keyCode == 27 && this.hide());
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -1,7 +1,9 @@
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
variants: {},
|
||||
plugins: []
|
||||
}
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
variants: {
|
||||
visibility: ['responsive', 'hover', 'group-hover']
|
||||
},
|
||||
plugins: []
|
||||
};
|
||||
|
Reference in New Issue
Block a user