feat: done

This commit is contained in:
WH64 2025-03-18 00:04:59 +09:00
parent c90da29b09
commit bcdd1e4a04
12 changed files with 110 additions and 110 deletions

12
app.go
View file

@ -6,6 +6,7 @@ import (
"git.wh64.net/devproje/kuma-archive/config" "git.wh64.net/devproje/kuma-archive/config"
"git.wh64.net/devproje/kuma-archive/internal/routes" "git.wh64.net/devproje/kuma-archive/internal/routes"
"git.wh64.net/devproje/kuma-archive/internal/service"
"github.com/devproje/commando" "github.com/devproje/commando"
"github.com/devproje/commando/option" "github.com/devproje/commando/option"
"github.com/devproje/commando/types" "github.com/devproje/commando/types"
@ -14,15 +15,17 @@ import (
var ( var (
hash = "unknown" hash = "unknown"
branch = "unknown"
version = "unknown" version = "unknown"
) )
func main() { func main() {
fmt.Printf("Kuma Archive %s-%s\n", version, hash)
command := commando.NewCommando(os.Args[1:]) command := commando.NewCommando(os.Args[1:])
cnf := config.Get() cnf := config.Get()
ver := service.NewVersion(version, branch, hash)
command.Root("daemon", "run file server", func(n *commando.Node) error { command.Root("daemon", "run file server", func(n *commando.Node) error {
fmt.Printf("Kuma Archive %s\n", version)
debug, err := option.ParseBool(*n.MustGetOpt("debug"), n) debug, err := option.ParseBool(*n.MustGetOpt("debug"), n)
if err != nil { if err != nil {
return err return err
@ -38,7 +41,7 @@ func main() {
} }
gin := gin.Default() gin := gin.Default()
routes.New(gin, apiOnly) routes.New(gin, ver, apiOnly)
fmt.Fprintf(os.Stdout, "binding server at: http://0.0.0.0:%d\n", cnf.Port) fmt.Fprintf(os.Stdout, "binding server at: http://0.0.0.0:%d\n", cnf.Port)
if err := gin.Run(fmt.Sprintf(":%d", cnf.Port)); err != nil { if err := gin.Run(fmt.Sprintf(":%d", cnf.Port)); err != nil {
@ -57,6 +60,11 @@ func main() {
Type: types.BOOLEAN, Type: types.BOOLEAN,
}) })
command.Root("version", "show system version info", func(n *commando.Node) error {
fmt.Printf("Kuma Archive version %s\n", ver.String())
return nil
})
if err := command.Execute(); err != nil { if err := command.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err) fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1) os.Exit(1)

View file

@ -13,7 +13,7 @@ type ConfigRef struct {
var ( var (
ROOT_DIRECTORY string ROOT_DIRECTORY string
CONFIG_DIR string CONFIG_FILE string
INDEX_DIR string INDEX_DIR string
) )
@ -33,7 +33,7 @@ func init() {
_ = os.WriteFile(filepath.Join(ROOT_DIRECTORY, "config.json"), []byte(buf), 0644) _ = os.WriteFile(filepath.Join(ROOT_DIRECTORY, "config.json"), []byte(buf), 0644)
} }
CONFIG_DIR = filepath.Join(ROOT_DIRECTORY, "config.json") CONFIG_FILE = filepath.Join(ROOT_DIRECTORY, "config.json")
INDEX_DIR = filepath.Join(ROOT_DIRECTORY, "index") INDEX_DIR = filepath.Join(ROOT_DIRECTORY, "index")
if _, err := os.ReadDir(INDEX_DIR); err != nil { if _, err := os.ReadDir(INDEX_DIR); err != nil {
@ -42,7 +42,7 @@ func init() {
} }
func Get() *ConfigRef { func Get() *ConfigRef {
buf, err := os.ReadFile(CONFIG_DIR) buf, err := os.ReadFile(CONFIG_FILE)
if err != nil { if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err) _, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
return nil return nil

2
go.mod
View file

@ -18,12 +18,12 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.25.0 // indirect github.com/go-playground/validator/v10 v10.25.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect

2
go.sum
View file

@ -49,6 +49,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=

View file

@ -11,7 +11,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func New(app *gin.Engine, apiOnly bool) { func New(app *gin.Engine, version *service.Version, apiOnly bool) {
app.Use(middleware.CORS) app.Use(middleware.CORS)
api := app.Group("/api") api := app.Group("/api")
@ -105,6 +105,10 @@ func New(app *gin.Engine, apiOnly bool) {
ctx.FileAttachment(data.Path, data.Name) ctx.FileAttachment(data.Path, data.Name)
}) })
api.GET("/version", func(ctx *gin.Context) {
ctx.String(200, "%s", version.String())
})
} }
if apiOnly { if apiOnly {

View file

@ -0,0 +1,12 @@
package service
import (
"database/sql"
"git.wh64.net/devproje/kuma-archive/config"
_ "github.com/mattn/go-sqlite3"
)
func Open() (*sql.DB, error) {
return sql.Open("sqlite3", (config.ROOT_DIRECTORY))
}

View file

@ -0,0 +1,21 @@
package service
import "fmt"
type Version struct {
Version string `json:"version"`
Branch string `json:"branch"`
Hash string `json:"hash"`
}
func NewVersion(version, branch, hash string) *Version {
return &Version{
Version: version,
Branch: branch,
Hash: hash,
}
}
func (v *Version) String() string {
return fmt.Sprintf("%s-%s (%s)", v.Version, v.Branch, v.Hash)
}

View file

@ -5,11 +5,11 @@
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"build:view": "tsc -b && vite build", "build:view": "tsc -b && vite build",
"build:server": "go build -ldflags \"-X main.version=$(jq -r .version package.json) -X main.hash=$(git rev-parse --short=6 HEAD)\" -o kuma-archive", "build:server": "go build -ldflags \"-X main.version=$(jq -r .version package.json) -X main.hash=$(git rev-parse --short=7 HEAD) -X main.branch=$(git rev-parse --abbrev-ref HEAD)\" -o kuma-archive",
"build": "bun run build:view && bun run build:server", "build": "bun run build:view && bun run build:server",
"dev:view": "vite", "dev:view": "vite",
"dev:server": "go run ./app.go daemon -d --api-only", "dev:server": "go run -ldflags \"-X main.version=$(jq -r .version package.json) -X main.hash=$(git rev-parse --short=7 HEAD) -X main.branch=$(git rev-parse --abbrev-ref HEAD)\" ./app.go daemon -d --api-only",
"dev": "bun run build:view && go run ./app.go daemon -d", "dev": "bun run build:view && go run -ldflags \"-X main.version=$(jq -r .version package.json) -X main.hash=$(git rev-parse --short=7 HEAD) -X main.branch=$(git rev-parse --abbrev-ref HEAD)\" ./app.go daemon -d",
"package": "sh ./scripts/package.sh", "package": "sh ./scripts/package.sh",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview" "preview": "vite preview"

View file

@ -25,59 +25,15 @@
margin: 0 0.5rem; margin: 0 0.5rem;
} }
} }
}
.ka-menu { .action-row {
top: 0;
left: 0;
height: 100%;
display: flex;
position: fixed;
min-width: 300px;
flex-direction: column;
transform: translateX(-100%);
justify-content: space-between;
background-color: var(--nav-color);
transition: transform 0.3s ease-in-out;
&.open {
transform: translateX(0);
}
.ka-menu-group {
width: 100%;
}
.ka-menu-item {
width: 100%;
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);
}
}
.ka-menu-footer {
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: row; flex-direction: row;
padding: 1rem 1.5rem; justify-content: center;
justify-content: space-between; .link {
margin-left: 0.5rem;
} }
@media (max-width: 640px) {
width: 100%;
} }
} }

View file

@ -1,4 +1,6 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useVersion } from "./store/version";
import FileView from "./components/file-view";
import Directory from "./components/directory"; import Directory from "./components/directory";
import { DirEntry, usePath } from "./store/path"; import { DirEntry, usePath } from "./store/path";
import { DynamicIcon } from "lucide-react/dynamic"; import { DynamicIcon } from "lucide-react/dynamic";
@ -6,7 +8,6 @@ import { BrowserRouter, Route, Routes, useLocation } from "react-router";
import "./App.scss"; import "./App.scss";
import kuma from "./assets/kuma.png"; import kuma from "./assets/kuma.png";
import FileView from "./components/file-view";
function App() { function App() {
return ( return (
@ -57,8 +58,6 @@ function Dashboard() {
} }
function Header() { function Header() {
const [open, setOpen] = useState(false);
return ( return (
<nav className="ka-nav"> <nav className="ka-nav">
<a className="title" href="/"> <a className="title" href="/">
@ -66,61 +65,26 @@ function Header() {
<h4 className="title-content">Kuma Archive</h4> <h4 className="title-content">Kuma Archive</h4>
</a> </a>
<a onClick={ev => { <div className="action-row">
ev.preventDefault(); <a className="link" href="https://git.wh64.net/devproje/kuma-archive">
setOpen(!open); <DynamicIcon name="folder-git-2" size={15} />
}}>
<DynamicIcon className="link" name="more-vertical" />
</a> </a>
<MenuView open={open} setOpen={setOpen} /> <a className="link" href="https://projecttl.net">
<DynamicIcon name="globe" size={15} />
</a>
</div>
</nav> </nav>
); );
} }
function MenuView({ open, setOpen }: { open: boolean; setOpen: (value: boolean) => void }) {
return (
<div className={`ka-menu ${open ? "open" : ""}`}>
<div className="ka-menu-group"></div>
<div className="ka-menu-group">
<div className="ka-menu-footer">
<a className="btn link" href="https://git.wh64.net/devproje/kuma-archive">
<DynamicIcon name="git-fork" />
</a>
<a className="btn link" href="https://projecttl.net">
<DynamicIcon name="globe" />
</a>
<a className="btn link" onClick={ev => {
ev.preventDefault();
setOpen(false);
}}>
<DynamicIcon name="panel-left-close" />
</a>
</div>
</div>
</div>
);
}
// function MenuItem({ icon, name, block }: { icon: IconName, name: string, block?: () => void }) {
// return (
// <a className={"ka-menu-item link"} onClick={ev => {
// ev.preventDefault();
// if (typeof block === "undefined")
// return;
// block();
// }}>
// <DynamicIcon name={icon} />
// <span>{name}</span>
// </a>
// );
// }
function Footer() { function Footer() {
const path = usePath(); const path = usePath();
let file = 0; let file = 0;
let dir = 0; let dir = 0;
const version = useVersion();
const [load, setLoad] = useState(false);
if (typeof path.data !== "undefined") { if (typeof path.data !== "undefined") {
if (path.data.is_dir) { if (path.data.is_dir) {
path.data.entries.forEach((entry: DirEntry) => { path.data.entries.forEach((entry: DirEntry) => {
@ -133,6 +97,15 @@ function Footer() {
} }
} }
useEffect(() => {
if (!load) {
version.update().then(() => {
setLoad(true);
});
return;
}
}, [load, version]);
return ( return (
<footer className="ka-footer"> <footer className="ka-footer">
{path.data ? path.data.is_dir ? ( {path.data ? path.data.is_dir ? (
@ -142,6 +115,7 @@ function Footer() {
) : <></> : <></>} ) : <></> : <></>}
<div className="footer"> <div className="footer">
<span><b>Kuma Archive</b> {version.value}</span>
<p> <p>
&copy; 2020-2025 <a href="https://git.wh64.net/devproje">Project_IO</a>. All rights reserved for images. &copy; 2020-2025 <a href="https://git.wh64.net/devproje">Project_IO</a>. All rights reserved for images.
<br /> <br />

View file

@ -45,5 +45,6 @@
} }
.ka-readme { .ka-readme {
padding-top: 1rem; padding: 1rem 10px 0.5rem 10px;
border-bottom: 1px solid var(--foreground);
} }

22
src/store/version.ts Normal file
View file

@ -0,0 +1,22 @@
import { create } from "zustand";
interface VersionState {
value: string | undefined;
update(): Promise<void>;
}
export const useVersion = create<VersionState>((set) => ({
value: undefined,
update: async () => {
const res = await fetch("/api/version", {
cache: "no-cache"
});
if (res.status !== 200 && res.status !== 304) {
set({ value: undefined });
return;
}
const text = await res.text();
set({ value: text });
}
}));