From 34880f4abc276cfde6eda28f16e667c65e9771ed Mon Sep 17 00:00:00 2001 From: Project_IO Date: Sun, 16 Mar 2025 02:00:54 +0900 Subject: [PATCH] feat: file view --- internal/routes/mod.go | 54 ++++++++++++++++-- internal/service/worker.go | 3 + src/App.scss | 27 ++++++++- src/App.tsx | 73 +++++++++++++++++++++---- src/components/directory/directory.scss | 29 ++++++++++ src/components/directory/index.tsx | 64 +++++++++++++++++++++- src/components/file-view/fview.scss | 51 +++++++++++++++++ src/components/file-view/index.tsx | 59 +++++++++++++++++++- src/index.scss | 6 ++ src/store/path.ts | 13 +++-- src/store/raw.ts | 22 ++++++++ src/util/unit.ts | 18 ++++++ 12 files changed, 394 insertions(+), 25 deletions(-) create mode 100644 src/store/raw.ts create mode 100644 src/util/unit.ts diff --git a/internal/routes/mod.go b/internal/routes/mod.go index 714c1e4..29334b5 100644 --- a/internal/routes/mod.go +++ b/internal/routes/mod.go @@ -3,6 +3,7 @@ package routes import ( "fmt" "os" + "path/filepath" "git.wh64.net/devproje/kuma-archive/internal/service" "github.com/gin-contrib/static" @@ -14,22 +15,28 @@ func New(app *gin.Engine, apiOnly bool) { { api.GET("/path/*path", func(ctx *gin.Context) { worker := service.NewWorkerService() - path := ctx.Param("path") data, err := worker.Read(path) if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) ctx.Status(404) return } if !data.IsDir { - ctx.FileAttachment(data.Path, data.Name) + ctx.JSON(200, gin.H{ + "ok": 1, + "path": path, + "total": data.FileSize, + "is_dir": false, + "entries": nil, + }) return } raw, err := os.ReadDir(data.Path) if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) ctx.Status(500) return } @@ -44,7 +51,8 @@ func New(app *gin.Engine, apiOnly bool) { entries = append(entries, service.DirEntry{ Name: entry.Name(), - Path: path, + Path: filepath.Join(path, entry.Name()), + Date: finfo.ModTime().Unix(), FileSize: uint64(finfo.Size()), IsDir: finfo.IsDir(), }) @@ -53,9 +61,47 @@ func New(app *gin.Engine, apiOnly bool) { ctx.JSON(200, gin.H{ "ok": 1, "path": path, + "total": data.FileSize, + "is_dir": true, "entries": entries, }) }) + + api.GET("/raw/*path", func(ctx *gin.Context) { + worker := service.NewWorkerService() + path := ctx.Param("path") + data, err := worker.Read(path) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) + ctx.Status(404) + return + } + + if data.IsDir { + ctx.String(400, "current path is not file") + return + } + + ctx.File(data.Path) + }) + + api.GET("/download/*path", func(ctx *gin.Context) { + worker := service.NewWorkerService() + path := ctx.Param("path") + data, err := worker.Read(path) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) + ctx.Status(404) + return + } + + if data.IsDir { + ctx.String(400, "current path is not file") + return + } + + ctx.FileAttachment(data.Path, data.Name) + }) } if apiOnly { diff --git a/internal/service/worker.go b/internal/service/worker.go index 3859d54..dfc8c80 100644 --- a/internal/service/worker.go +++ b/internal/service/worker.go @@ -12,6 +12,7 @@ type WorkerService struct{} type DirEntry struct { Name string `json:"name"` Path string `json:"path"` + Date int64 `json:"date"` FileSize uint64 `json:"file_size"` IsDir bool `json:"is_dir"` } @@ -29,8 +30,10 @@ func (sv *WorkerService) Read(path string) (*DirEntry, error) { ret := DirEntry{ Name: info.Name(), + Date: info.ModTime().Unix(), Path: fullpath, FileSize: uint64(info.Size()), + IsDir: info.IsDir(), } return &ret, nil } diff --git a/src/App.scss b/src/App.scss index c665890..3510121 100644 --- a/src/App.scss +++ b/src/App.scss @@ -43,17 +43,40 @@ .ka-menu-item { width: 100%; - height: 55px; - padding: 5px 0.5rem; + height: 45px; display: flex; + padding: 0 0.5rem; align-items: center; span { margin: 0 5px; } + + &:hover { + background-color: var(--nav-hover); + } + + &:focus { + color: var(--focus); + } } @media (max-width: 640px) { width: 100%; } } + +.ka-footer { + width: 100%; + display: flex; + align-items: center; + flex-direction: column; + justify-content: center; + + div { + display: flex; + align-items: center; + flex-direction: row; + justify-content: center; + } +} diff --git a/src/App.tsx b/src/App.tsx index 1008747..97bf2b0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,12 @@ -import { BrowserRouter, Route, Routes, useLocation } from "react-router"; -import { usePath } from "./store/path"; import { useEffect, useState } from "react"; +import Directory from "./components/directory"; +import { DirEntry, usePath } from "./store/path"; +import { DynamicIcon, IconName } from "lucide-react/dynamic"; +import { BrowserRouter, Route, Routes, useLocation } from "react-router"; import "./App.scss"; import kuma from "./assets/kuma.png"; -import { DynamicIcon, IconName } from "lucide-react/dynamic"; +import FileView from "./components/file-view"; function App() { return ( @@ -24,19 +26,37 @@ function Dashboard() { useEffect(() => { if (!load) { path.update(location.pathname.substring(1, location.pathname.length)); - setLoad(true); - } + setLoad(true); - const id = setInterval(() => { - path.update(location.pathname.substring(1, location.pathname.length)); - }, 5000); + return; + } + + const id = setInterval(() => { + path.update(location.pathname.substring(1, location.pathname.length)); + }, 5000); return () => clearInterval(id); }, [load, path, location]); + if (!load) { + return <>; + } + return (
+ {typeof path.data !== "undefined" ? path.data.is_dir ? : : ( + <> +

404 Not Found

+ + + + )} + +
); } @@ -46,7 +66,7 @@ function Header() { return (