diff --git a/.env b/.env deleted file mode 100644 index a6f2882..0000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -VUE_APP_ROOT_WEBDAV="http://127.0.0.1:4918" diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4cbf28d --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +VUE_APP_ROOT_WEBDAV="http://127.0.0.1:8080" diff --git a/package.json b/package.json index ec00f36..94254d9 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,15 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "bootstrap": "^5.2.0", + "bootstrap-darkmode": "^5.0.1", + "bootstrap-icons": "^1.9.1", "core-js": "^3.6.5", + "pinia": "^2.0.13", "register-service-worker": "^1.7.1", + "typescript-is": "^0.19.0", "vue": "^3.0.0", "vue-router": "^4.0.0-0", - "pinia": "^2.0.13", "webdav": "^4.9.0" }, "devDependencies": { diff --git a/src/App.vue b/src/App.vue index 113b6b8..7a74cba 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,30 +1,24 @@ - + diff --git a/src/components/DarkmodeToggle.vue b/src/components/DarkmodeToggle.vue new file mode 100644 index 0000000..4587653 --- /dev/null +++ b/src/components/DarkmodeToggle.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/components/SideMenu.vue b/src/components/SideMenu.vue new file mode 100644 index 0000000..40e38ab --- /dev/null +++ b/src/components/SideMenu.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/components/files/FileList.vue b/src/components/files/FileList.vue new file mode 100644 index 0000000..78fcfda --- /dev/null +++ b/src/components/files/FileList.vue @@ -0,0 +1,100 @@ + + + diff --git a/src/components/files/FileListElement.vue b/src/components/files/FileListElement.vue new file mode 100644 index 0000000..4c92d1a --- /dev/null +++ b/src/components/files/FileListElement.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/components/files/FolderListElement.vue b/src/components/files/FolderListElement.vue new file mode 100644 index 0000000..ccbe366 --- /dev/null +++ b/src/components/files/FolderListElement.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/src/components/files/PathSegments.vue b/src/components/files/PathSegments.vue new file mode 100644 index 0000000..9fcacf7 --- /dev/null +++ b/src/components/files/PathSegments.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/src/components/helpers/ByteCalc.vue b/src/components/helpers/ByteCalc.vue new file mode 100644 index 0000000..67f38e4 --- /dev/null +++ b/src/components/helpers/ByteCalc.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/components/helpers/FileUpload.vue b/src/components/helpers/FileUpload.vue new file mode 100644 index 0000000..4dfb23a --- /dev/null +++ b/src/components/helpers/FileUpload.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/src/lib/fileTypeToIconMappings.ts b/src/lib/fileTypeToIconMappings.ts new file mode 100644 index 0000000..f5806ae --- /dev/null +++ b/src/lib/fileTypeToIconMappings.ts @@ -0,0 +1,41 @@ +export const defaultIcon = 'file-earmark'; + +export const fileExtensions = new Map([ + // Images + ['png', 'file-earmark-image'], + ['jpg', 'file-earmark-image'], + ['tiff', 'file-earmark-image'], + // Music + ['mp3', 'file-earmark-music'], + ['m4a', 'file-earmark-music'], + ['aac', 'file-earmark-music'], + ['aiff', 'file-earmark-music'], + ['wav', 'file-earmark-music'], + ['wma', 'file-earmark-music'], + // Code + ['html', 'file-earmark-code'], + ['htm', 'file-earmark-code'], + ['xml', 'file-earmark-code'], + ['js', 'file-earmark-code'], + ['mjs', 'file-earmark-code'], + ['py', 'file-earmark-code'], + ['sh', 'file-earmark-code'], + ['ts', 'file-earmark-code'], + ['go', 'file-earmark-code'], + ['rs', 'file-earmark-code'], + ['java', 'file-earmark-code'], + // Binaries + ['jar', 'file-earmark-binary'], + ['exe', 'file-earmark-binary'], + ['iso', 'file-earmark-binary'], + // etc... + ['pdf', 'file-earmark-pdf'], + ['txt', 'filetype-txt'], + ['zip', 'file-earmark-zip'], + ['gz', 'file-earmark-zip'], + ['xz', 'file-earmark-zip'], +]); + +export const mimeTypes = new Map([ + ['application/pdf', 'file-earmark-pdf'], +]); diff --git a/src/lib/readFileBlob.ts b/src/lib/readFileBlob.ts new file mode 100644 index 0000000..1133b80 --- /dev/null +++ b/src/lib/readFileBlob.ts @@ -0,0 +1,41 @@ +import { is } from 'typescript-is'; + +export const readFileAs = ( + file: File, + getReaderMethod = (reader: FileReader) => reader.readAsDataURL +): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onerror = reject; + reader.onload = async (event) => { + const buffer = event.target?.result; + if (!buffer) reject('failed to read file'); + if (!is(buffer)) reject('wrong type'); + else resolve(buffer as T); + }; + getReaderMethod(reader as FileReader)(file); + }); +}; + +export const readFileBuffer = async (file: File): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onerror = reject; + reader.onload = async (event) => { + const buffer = event.target?.result; + if (buffer === null || buffer === undefined || typeof buffer === 'string') + reject('failed to read file'); + else resolve(buffer); + }; + reader.readAsArrayBuffer(file); + }); + /*return readFileAs( + file, + (reader: FileReader) => reader.readAsArrayBuffer + );*/ +}; + +export const readFileBlob = async (file: File): Promise => { + const data = await readFileAs(file); + return await (await fetch(data)).blob(); +}; diff --git a/src/lib/runMiddleware.ts b/src/lib/runMiddleware.ts index 8d7e935..d6c0b68 100644 --- a/src/lib/runMiddleware.ts +++ b/src/lib/runMiddleware.ts @@ -1,29 +1,29 @@ import { Context } from '@/middleware/Context'; -type RunMiddleware = (context: Context) => void; +type Middleware = (context: Context) => void; function nextFactory( context: Context, - middlewares: Array, + middlewares: Array, index: number ) { const subsequentMiddleware = middlewares[index]; if (!subsequentMiddleware) return context.next; - return (...args: any[]) => { + return (...args: Array) => { // @ts-ignore - context.next(...args); + context.next(...args as Array); subsequentMiddleware({ ...context, next: nextFactory(context, middlewares, index++), }); }; } -export function runMiddleware(context: Context) { +export function runMiddleware(context: Context): void { const { to } = context; const middlewares = [ ...((Array.isArray(to.meta.middleware) ? to.meta.middleware - : [to.meta.middleware]) as Array), + : [to.meta.middleware]) as Array), ]; middlewares[0]({ ...context, next: nextFactory(context, middlewares, 1) }); diff --git a/src/main.scss b/src/main.scss new file mode 100644 index 0000000..924874c --- /dev/null +++ b/src/main.scss @@ -0,0 +1,16 @@ +// Your variable overrides +//$body-bg: #222; +//$body-color: #222; +$dark-body-bg: #222; + +// Bootstrap and its default variables +@import "~bootstrap/scss/bootstrap"; +@import "~bootstrap-darkmode/css/darktheme"; + +html, body { +} + +#app{ + font-family: monospace; + //color: #fff; +} diff --git a/src/main.ts b/src/main.ts index 7284570..172b195 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,13 @@ -import { createApp } from "vue"; -import App from "./App.vue"; -import "./registerServiceWorker"; -import router from "./router"; -import store from "./store"; +import { createApp } from 'vue'; +import App from './App.vue'; +import './registerServiceWorker'; +import router from './router'; +import store from './store'; +import 'bootstrap-icons/font/bootstrap-icons.scss'; +import './main.scss'; +import { ThemeConfig, writeDarkSwitch } from 'bootstrap-darkmode'; -createApp(App).use(store).use(router).mount("#app"); +export const themeConfig = new ThemeConfig(); +themeConfig.initTheme(); + +createApp(App).use(store).use(router).mount('#app'); diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts index dfeb051..4c00cce 100644 --- a/src/middleware/auth.ts +++ b/src/middleware/auth.ts @@ -1,8 +1,8 @@ -import { useWebdavStorage } from '@/store/webdav'; +import { useWebdavStore } from '@/store/webdav'; import { Context } from '@/middleware/Context'; -export default function auth({ next, router }: Context) { - console.log('auth'); - if (!useWebdavStorage().currentSession?.isActive) return router.push({ name: 'Login' }); +export const auth = ({ next }: Context) => { + if (!useWebdavStore().currentSession?.isActive) + return next({ name: 'Login' }); return next(); -} +}; diff --git a/src/models.ts b/src/models.ts new file mode 100644 index 0000000..91fd934 --- /dev/null +++ b/src/models.ts @@ -0,0 +1,9 @@ +export type File = { + basename: string; + etag: string; + filename: string; + lastmod: string; + mime: string; + size: number; + type: string; +}; diff --git a/src/router/index.ts b/src/router/index.ts index a80105a..2b3e19d 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,7 +1,6 @@ import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'; import Home from '../views/Home.vue'; -import auth from '@/middleware/auth'; -import log from '@/middleware/log'; +import { auth } from '@/middleware/auth'; import { runMiddleware } from '@/lib/runMiddleware'; const routes: Array = [ @@ -22,9 +21,6 @@ const routes: Array = [ path: '/login', name: 'Login', component: () => import('../views/Login.vue'), - meta: { - middleware: [log], - }, }, ]; diff --git a/src/store/auth.ts b/src/store/auth.ts deleted file mode 100644 index 0f1aee4..0000000 --- a/src/store/auth.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {defineStore} from "pinia"; - -export const useAuthStorage = defineStore('auth', { - state: () => ({ - user: '', - password: '' - }), - actions: { - login(){ - - } - } -}); diff --git a/src/store/webdav.ts b/src/store/webdav.ts index 0d01b14..84fc0db 100644 --- a/src/store/webdav.ts +++ b/src/store/webdav.ts @@ -11,31 +11,29 @@ export type Session = { isActive: boolean; }; -export const useWebdavStorage = defineStore('auth', { +export const useWebdavStore = defineStore('auth', { state: () => ({ sessions: [] as Array, currentSession: null as null | Session, }), actions: { - login({ user, pass }: Auth): Promise { - return new Promise((resolve, reject) => { - try { - const client = createClient( - process.env.VUE_APP_ROOT_WEBDAV as string, - { - authType: AuthType.Digest, - username: user as string, - password: pass as string, - } - ) as WebDAVClient; - const session = { client, isActive: true } as Session; - this.sessions.push(session); - this.currentSession = session; - resolve(session); - } catch (e) { - reject(e); - } - }); + async login({ user, pass }: Auth): Promise { + try { + const client = createClient( + `${process.env.VUE_APP_ROOT_WEBDAV ?? ''}/api/dav/files/` as string, + { + authType: AuthType.Password, + username: user as string, + password: pass as string, + } + ) as WebDAVClient; + const session = { client, isActive: true } as Session; + this.sessions.push(session); + this.currentSession = session; + return session; + } catch (e) { + throw 'login failed'; + } }, }, }); diff --git a/src/views/Files.vue b/src/views/Files.vue index 230f3b9..5360a58 100644 --- a/src/views/Files.vue +++ b/src/views/Files.vue @@ -1,11 +1,20 @@ - + diff --git a/src/views/Login.vue b/src/views/Login.vue index 8ac5615..4a0bdd6 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -2,22 +2,32 @@

Login

user:
pass:
- +
+ {{ error }}