feat: add private directory

This commit is contained in:
Project_IO 2025-03-23 18:00:42 +09:00
parent 83a5ef9bf6
commit e4c6332398
9 changed files with 173 additions and 88 deletions

View file

@ -31,6 +31,11 @@ func New(app *gin.Engine, version *service.Version, apiOnly bool) {
auth.DELETE("/delete", deleteAcc)
}
privdir := api.Group("/privdir")
{
privdir.POST("/create", createDir)
}
api.GET("/version", func(ctx *gin.Context) {
ctx.String(200, "%s", version.String())
})

View file

@ -0,0 +1,60 @@
package routes
import (
"fmt"
"git.wh64.net/devproje/kuma-archive/internal/service"
"github.com/gin-gonic/gin"
"os"
)
func createDir(ctx *gin.Context) {
var err error
auth := service.NewAuthService()
username, password, ok := ctx.Request.BasicAuth()
if !ok {
ctx.JSON(401, gin.H{
"ok": 0,
"errno": "Unauthorized",
})
return
}
if ok, err = auth.VerifyToken(username, password); !ok {
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
}
ctx.JSON(401, gin.H{
"ok": 0,
"errno": "Unauthorized",
})
return
}
var acc *service.Account
acc, err = auth.Read(username)
if err != nil {
ctx.JSON(500, gin.H{
"ok": 0,
"errno": "Interval Server Error",
})
return
}
path := ctx.PostForm("path")
privdir := service.NewPrivDirService(acc)
var id string
if id, err = privdir.Create(path); err != nil {
ctx.JSON(500, gin.H{
"ok": 0,
"errno": fmt.Sprintf("'%s' directory is already registered", path),
})
return
}
ctx.JSON(200, gin.H{
"ok": 1,
"dir_id": id,
})
}

View file

@ -60,22 +60,22 @@ func NewPrivDirService(acc *Account) *PrivDirService {
}
}
func (sv *PrivDirService) Create(dirname string) error {
func (sv *PrivDirService) Create(dirname string) (string, error) {
db, err := Open()
if err != nil {
return err
return "", err
}
defer db.Close()
id := uuid.NewString()
stmt, err := db.Prepare("insert into PrivDir(id, dirname, owner) values (?, ?, ?);")
if err != nil {
return err
return "", err
}
defer stmt.Close()
_, err = stmt.Exec(id, dirname, sv.acc.Username)
return nil
return id, nil
}
func (sv *PrivDirService) Read(dirname string) (*PrivDir, error) {

View file

@ -1,6 +1,6 @@
import Login from "./components/login";
import Logout from "./components/logout";
import { useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
import Settings from "./components/settings";
import { useVersion } from "./store/version";
import NotFound from "./components/notfound";
@ -39,18 +39,20 @@ function Dashboard({ children }: { children: React.ReactNode }) {
function View() {
const path = usePath();
const auth = useAuthStore();
const location = useLocation();
const [load, setLoad] = useState(false);
useEffect(() => {
if (!load) {
path.update(location.pathname.substring(1, location.pathname.length)).then(() => {
path.update(location.pathname.substring(1, location.pathname.length), auth.token)
.then(() => {
setLoad(true);
});
return;
}
}, [load, path, location]);
}, [auth, load, path, location]);
if (!load) {
return <></>;

View file

@ -6,16 +6,19 @@ import { DynamicIcon, IconName } from "lucide-react/dynamic";
import "./fview.scss";
import { FileNavigator } from "../navigation";
import {useAuthStore} from "../../store/auth.ts";
function FileView() {
const path = usePath();
const auth = useAuthStore();
const location = useLocation();
const [load, setLoad] = useState(false);
const [type, setType] = useState<IconName>("file");
useEffect(() => {
if (!load) {
path.update(location.pathname.substring(1, location.pathname.length)).then(() => {
path.update(location.pathname.substring(1, location.pathname.length), auth.token)
.then(() => {
setLoad(true);
switch (true) {
@ -87,7 +90,7 @@ function FileView() {
});
return;
}
}, [path, location, load]);
}, [auth, path, location, load]);
if (typeof path.data === "undefined")
return <></>;

View file

@ -10,13 +10,13 @@ function Settings() {
useEffect(() => {
if (auth.token === null) {
// document.location.href = "/";
document.location.href = "/login";
return;
}
auth.checkToken(auth.token).then((ok) => {
if (!ok) {
// document.location.href = "/";
document.location.href = "/login";
return;
}
@ -69,7 +69,7 @@ function AccountSetting({ auth }: { auth: AuthState }) {
<span>If you change your password, you will need to log in again.</span>
</div>
<form className="box-col" id="pw-change">
<form className="box-col form" id="pw-change">
<PasswordInput placeholder="Password" ref={orRef} />
<PasswordInput placeholder="New Password" ref={pwRef} />
<PasswordInput placeholder="Check Password" ref={ckRef} />
@ -122,7 +122,7 @@ function AccountSetting({ auth }: { auth: AuthState }) {
<span>You can delete account. This action is irreversible. Please proceed with caution.</span>
</div>
<form className="box-col">
<form className="box-col form">
<label className="checkbox">
<input type="checkbox" onChange={ev => {
setRemove(ev.target.checked);

View file

@ -50,6 +50,11 @@
}
}
.form {
display: flex;
min-width: 380px;
}
.line {
margin-bottom: 15px;
}

View file

@ -32,12 +32,13 @@
--btn-success-focus: #1d7c33;
--btn-danger-focus: #a72532;
--font-family: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif;
--font-family: "Pretendard Variable", Pretendard, -apple-system, BlinkMacSystemFont, system-ui, Roboto, "Helvetica Neue", "Segoe UI", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--font-mono: "JetBrains Mono", monospace;
}
html, body {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
@ -60,7 +61,8 @@ html, body {
padding: 0;
box-sizing: border-box;
font-family: var(--font-family);
font-family: var(--font-family), sans-serif;
overflow-x: hidden;
}
h1 {

View file

@ -2,7 +2,7 @@ import { create } from "zustand";
interface PathState {
data: PathResponse | undefined;
update(path: string): Promise<void>;
update(path: string, token: string | null): Promise<void>;
}
interface PathResponse {
@ -23,8 +23,16 @@ export interface DirEntry {
export const usePath = create<PathState>((set) => ({
data: undefined,
update: async (path: string) => {
const res = await fetch(`/api/worker/discover/${path}`);
update: async (path: string, token: string | null) => {
const res = await fetch(`/api/worker/discover/${path}`, {
headers: {
"Authorization": token === null ? "" : `Basic ${token}`
}
});
if (res.status === 401) {
document.location.href = "/login";
}
if (res.status !== 200 && res.status !== 304) {
set({ data: undefined });
return;