feat: add private directory
This commit is contained in:
parent
83a5ef9bf6
commit
e4c6332398
9 changed files with 173 additions and 88 deletions
|
@ -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())
|
||||
})
|
||||
|
|
60
internal/routes/privdir.go
Normal file
60
internal/routes/privdir.go
Normal 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,
|
||||
})
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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 <></>;
|
||||
|
|
|
@ -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 <></>;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -50,6 +50,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
min-width: 380px;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue