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/internal/routes"
"git.wh64.net/devproje/kuma-archive/internal/service"
"github.com/devproje/commando"
"github.com/devproje/commando/option"
"github.com/devproje/commando/types"
@ -14,15 +15,17 @@ import (
var (
hash = "unknown"
branch = "unknown"
version = "unknown"
)
func main() {
fmt.Printf("Kuma Archive %s-%s\n", version, hash)
command := commando.NewCommando(os.Args[1:])
cnf := config.Get()
ver := service.NewVersion(version, branch, hash)
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)
if err != nil {
return err
@ -38,7 +41,7 @@ func main() {
}
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)
if err := gin.Run(fmt.Sprintf(":%d", cnf.Port)); err != nil {
@ -57,6 +60,11 @@ func main() {
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 {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)

View file

@ -13,7 +13,7 @@ type ConfigRef struct {
var (
ROOT_DIRECTORY string
CONFIG_DIR string
CONFIG_FILE string
INDEX_DIR string
)
@ -33,7 +33,7 @@ func init() {
_ = 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")
if _, err := os.ReadDir(INDEX_DIR); err != nil {
@ -42,7 +42,7 @@ func init() {
}
func Get() *ConfigRef {
buf, err := os.ReadFile(CONFIG_DIR)
buf, err := os.ReadFile(CONFIG_FILE)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
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/validator/v10 v10.25.0 // 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/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.4.0 // 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/reflect2 v1.0.2 // 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/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-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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
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"
)
func New(app *gin.Engine, apiOnly bool) {
func New(app *gin.Engine, version *service.Version, apiOnly bool) {
app.Use(middleware.CORS)
api := app.Group("/api")
@ -105,6 +105,10 @@ func New(app *gin.Engine, apiOnly bool) {
ctx.FileAttachment(data.Path, data.Name)
})
api.GET("/version", func(ctx *gin.Context) {
ctx.String(200, "%s", version.String())
})
}
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",
"scripts": {
"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",
"dev:view": "vite",
"dev:server": "go run ./app.go daemon -d --api-only",
"dev": "bun run build:view && go run ./app.go daemon -d",
"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 -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",
"lint": "eslint .",
"preview": "vite preview"

View file

@ -25,59 +25,15 @@
margin: 0 0.5rem;
}
}
}
.ka-menu {
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 {
.action-row {
display: flex;
align-items: center;
flex-direction: row;
padding: 1rem 1.5rem;
justify-content: space-between;
justify-content: center;
.link {
margin-left: 0.5rem;
}
@media (max-width: 640px) {
width: 100%;
}
}

View file

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

View file

@ -45,5 +45,6 @@
}
.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 });
}
}));