Compare commits
	
		
			No commits in common. "7598df6018edc978d5a019ced3c95fe97302a95b" and "4a38cad0a40100321b00e65b6bc815b1dc13e973" have entirely different histories.
		
	
	
		
			7598df6018
			...
			4a38cad0a4
		
	
		
					 18 changed files with 155 additions and 187 deletions
				
			
		| 
						 | 
					@ -14,7 +14,6 @@
 | 
				
			||||||
    "@radix-ui/react-icons": "^1.3.0",
 | 
					    "@radix-ui/react-icons": "^1.3.0",
 | 
				
			||||||
    "@radix-ui/react-label": "^2.0.2",
 | 
					    "@radix-ui/react-label": "^2.0.2",
 | 
				
			||||||
    "@radix-ui/react-radio-group": "^1.1.3",
 | 
					    "@radix-ui/react-radio-group": "^1.1.3",
 | 
				
			||||||
    "@radix-ui/react-separator": "^1.0.3",
 | 
					 | 
				
			||||||
    "@radix-ui/react-slot": "^1.0.2",
 | 
					    "@radix-ui/react-slot": "^1.0.2",
 | 
				
			||||||
    "@radix-ui/react-tooltip": "^1.0.7",
 | 
					    "@radix-ui/react-tooltip": "^1.0.7",
 | 
				
			||||||
    "class-variance-authority": "^0.7.0",
 | 
					    "class-variance-authority": "^0.7.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,6 @@ import ProfilePage from "@/page/profilesPage.tsx";
 | 
				
			||||||
import ContentInfoPage from "@/page/contentInfoPage.tsx";
 | 
					import ContentInfoPage from "@/page/contentInfoPage.tsx";
 | 
				
			||||||
import SettingPage from "@/page/settingPage.tsx";
 | 
					import SettingPage from "@/page/settingPage.tsx";
 | 
				
			||||||
import ComicPage from "@/page/reader/comicPage.tsx";
 | 
					import ComicPage from "@/page/reader/comicPage.tsx";
 | 
				
			||||||
import DifferencePage from "./page/differencePage.tsx";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const App = () => {
 | 
					const App = () => {
 | 
				
			||||||
	const { isDarkMode } = useTernaryDarkMode();
 | 
						const { isDarkMode } = useTernaryDarkMode();
 | 
				
			||||||
| 
						 | 
					@ -39,7 +38,9 @@ const App = () => {
 | 
				
			||||||
					<Route path="/doc/:id" component={ContentInfoPage}/>
 | 
										<Route path="/doc/:id" component={ContentInfoPage}/>
 | 
				
			||||||
					<Route path="/setting" component={SettingPage} />
 | 
										<Route path="/setting" component={SettingPage} />
 | 
				
			||||||
					<Route path="/doc/:id/reader" component={ComicPage}/>
 | 
										<Route path="/doc/:id/reader" component={ComicPage}/>
 | 
				
			||||||
					<Route path="/difference" component={DifferencePage}/>
 | 
										{/* 
 | 
				
			||||||
 | 
											<Route path="/difference" component={<DifferencePage />}/>
 | 
				
			||||||
 | 
										*/}
 | 
				
			||||||
					<Route component={NotFoundPage} />
 | 
										<Route component={NotFoundPage} />
 | 
				
			||||||
				</Switch>
 | 
									</Switch>
 | 
				
			||||||
			</Layout>
 | 
								</Layout>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -120,8 +120,7 @@ export default function TagInput({
 | 
				
			||||||
                    setTags(tags.filter(x=>x!==tag));
 | 
					                    setTags(tags.filter(x=>x!==tag));
 | 
				
			||||||
                }}>{tag}</li>)}
 | 
					                }}>{tag}</li>)}
 | 
				
			||||||
            </ul>
 | 
					            </ul>
 | 
				
			||||||
            <input ref={inputRef} type="text" className="flex-1 border-0 ml-2 focus:border-0 focus:outline-none
 | 
					            <input ref={inputRef} type="text" className="flex-1 border-0 ml-2 focus:border-0 focus:outline-none" placeholder="Add tag"
 | 
				
			||||||
            bg-transparent text-sm" placeholder="Add tag"
 | 
					 | 
				
			||||||
                onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)}
 | 
					                onFocus={() => setIsFocused(true)} onBlur={() => setIsFocused(false)}
 | 
				
			||||||
                value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => {
 | 
					                value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => {
 | 
				
			||||||
                    if (e.key === "Enter") {
 | 
					                    if (e.key === "Enter") {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,29 +0,0 @@
 | 
				
			||||||
import * as React from "react"
 | 
					 | 
				
			||||||
import * as SeparatorPrimitive from "@radix-ui/react-separator"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { cn } from "@/lib/utils"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const Separator = React.forwardRef<
 | 
					 | 
				
			||||||
  React.ElementRef<typeof SeparatorPrimitive.Root>,
 | 
					 | 
				
			||||||
  React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
 | 
					 | 
				
			||||||
>(
 | 
					 | 
				
			||||||
  (
 | 
					 | 
				
			||||||
    { className, orientation = "horizontal", decorative = true, ...props },
 | 
					 | 
				
			||||||
    ref
 | 
					 | 
				
			||||||
  ) => (
 | 
					 | 
				
			||||||
    <SeparatorPrimitive.Root
 | 
					 | 
				
			||||||
      ref={ref}
 | 
					 | 
				
			||||||
      decorative={decorative}
 | 
					 | 
				
			||||||
      orientation={orientation}
 | 
					 | 
				
			||||||
      className={cn(
 | 
					 | 
				
			||||||
        "shrink-0 bg-border",
 | 
					 | 
				
			||||||
        orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
 | 
					 | 
				
			||||||
        className
 | 
					 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
      {...props}
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
Separator.displayName = SeparatorPrimitive.Root.displayName
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export { Separator }
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
export const BASE_API_URL = 'http://localhost:5173/';
 | 
					export const BASE_API_URL = 'http://localhost:8080/';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function fetcher(url: string) {
 | 
					export async function fetcher(url: string) {
 | 
				
			||||||
    const u = new URL(url, BASE_API_URL);
 | 
					    const u = new URL(url, BASE_API_URL);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,38 +0,0 @@
 | 
				
			||||||
import useSWR, { mutate } from "swr";
 | 
					 | 
				
			||||||
import { fetcher } from "./fetcher";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type FileDifference = {
 | 
					 | 
				
			||||||
    type: string;
 | 
					 | 
				
			||||||
    value: {
 | 
					 | 
				
			||||||
        type: string;
 | 
					 | 
				
			||||||
        path: string;
 | 
					 | 
				
			||||||
    }[];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function useDifferenceDoc() {
 | 
					 | 
				
			||||||
    return useSWR<FileDifference[]>("/api/diff/list", fetcher);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function commit(path: string, type: string) {
 | 
					 | 
				
			||||||
    const res = await fetch("/api/diff/commit", {
 | 
					 | 
				
			||||||
        method: "POST",
 | 
					 | 
				
			||||||
        body: JSON.stringify([{ path, type }]),
 | 
					 | 
				
			||||||
        headers: {
 | 
					 | 
				
			||||||
            "Content-Type": "application/json",
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    mutate("/api/diff/list");
 | 
					 | 
				
			||||||
    return res.ok;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function commitAll(type: string) {
 | 
					 | 
				
			||||||
    const res = await fetch("/api/diff/commitall", {
 | 
					 | 
				
			||||||
        method: "POST",
 | 
					 | 
				
			||||||
        body: JSON.stringify({ type }),
 | 
					 | 
				
			||||||
        headers: {
 | 
					 | 
				
			||||||
            "Content-Type": "application/json",
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    mutate("/api/diff/list");
 | 
					 | 
				
			||||||
    return res.ok;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										126
									
								
								packages/client/src/page/difference.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								packages/client/src/page/difference.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,126 @@
 | 
				
			||||||
 | 
					// import { Box, Button, Paper, Typography } from "@mui/material";
 | 
				
			||||||
 | 
					// import React, { useContext, useEffect, useState } from "react";
 | 
				
			||||||
 | 
					// import { CommonMenuList, Headline } from "../component/mod";
 | 
				
			||||||
 | 
					// import { UserContext } from "../state";
 | 
				
			||||||
 | 
					// import { PagePad } from "../component/pagepad";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// type FileDifference = {
 | 
				
			||||||
 | 
					// 	type: string;
 | 
				
			||||||
 | 
					// 	value: {
 | 
				
			||||||
 | 
					// 		type: string;
 | 
				
			||||||
 | 
					// 		path: string;
 | 
				
			||||||
 | 
					// 	}[];
 | 
				
			||||||
 | 
					// };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// function TypeDifference(prop: {
 | 
				
			||||||
 | 
					// 	content: FileDifference;
 | 
				
			||||||
 | 
					// 	onCommit: (v: { type: string; path: string }) => void;
 | 
				
			||||||
 | 
					// 	onCommitAll: (type: string) => void;
 | 
				
			||||||
 | 
					// }) {
 | 
				
			||||||
 | 
					// 	// const classes = useStyles();
 | 
				
			||||||
 | 
					// 	const x = prop.content;
 | 
				
			||||||
 | 
					// 	const [button_disable, set_disable] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 	return (
 | 
				
			||||||
 | 
					// 		<Paper /*className={classes.paper}*/>
 | 
				
			||||||
 | 
					// 			<Box /*className={classes.contentTitle}*/>
 | 
				
			||||||
 | 
					// 				<Typography variant="h3">{x.type}</Typography>
 | 
				
			||||||
 | 
					// 				<Button
 | 
				
			||||||
 | 
					// 					variant="contained"
 | 
				
			||||||
 | 
					// 					key={x.type}
 | 
				
			||||||
 | 
					// 					onClick={() => {
 | 
				
			||||||
 | 
					// 						set_disable(true);
 | 
				
			||||||
 | 
					// 						prop.onCommitAll(x.type);
 | 
				
			||||||
 | 
					// 						set_disable(false);
 | 
				
			||||||
 | 
					// 					}}
 | 
				
			||||||
 | 
					// 				>
 | 
				
			||||||
 | 
					// 					Commit all
 | 
				
			||||||
 | 
					// 				</Button>
 | 
				
			||||||
 | 
					// 			</Box>
 | 
				
			||||||
 | 
					// 			{x.value.map((y) => (
 | 
				
			||||||
 | 
					// 				<Box sx={{ display: "flex" }} key={y.path}>
 | 
				
			||||||
 | 
					// 					<Button
 | 
				
			||||||
 | 
					// 						variant="contained"
 | 
				
			||||||
 | 
					// 						onClick={() => {
 | 
				
			||||||
 | 
					// 							set_disable(true);
 | 
				
			||||||
 | 
					// 							prop.onCommit(y);
 | 
				
			||||||
 | 
					// 							set_disable(false);
 | 
				
			||||||
 | 
					// 						}}
 | 
				
			||||||
 | 
					// 						disabled={button_disable}
 | 
				
			||||||
 | 
					// 					>
 | 
				
			||||||
 | 
					// 						Commit
 | 
				
			||||||
 | 
					// 					</Button>
 | 
				
			||||||
 | 
					// 					<Typography variant="h5">{y.path}</Typography>
 | 
				
			||||||
 | 
					// 				</Box>
 | 
				
			||||||
 | 
					// 			))}
 | 
				
			||||||
 | 
					// 		</Paper>
 | 
				
			||||||
 | 
					// 	);
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// export function DifferencePage() {
 | 
				
			||||||
 | 
					// 	const ctx = useContext(UserContext);
 | 
				
			||||||
 | 
					// 	// const classes = useStyles();
 | 
				
			||||||
 | 
					// 	const [diffList, setDiffList] = useState<FileDifference[]>([]);
 | 
				
			||||||
 | 
					// 	const doLoad = async () => {
 | 
				
			||||||
 | 
					// 		const list = await fetch("/api/diff/list");
 | 
				
			||||||
 | 
					// 		if (list.ok) {
 | 
				
			||||||
 | 
					// 			const inner = await list.json();
 | 
				
			||||||
 | 
					// 			setDiffList(inner);
 | 
				
			||||||
 | 
					// 		} else {
 | 
				
			||||||
 | 
					// 			// setDiffList([]);
 | 
				
			||||||
 | 
					// 		}
 | 
				
			||||||
 | 
					// 	};
 | 
				
			||||||
 | 
					// 	const Commit = async (x: { type: string; path: string }) => {
 | 
				
			||||||
 | 
					// 		const res = await fetch("/api/diff/commit", {
 | 
				
			||||||
 | 
					// 			method: "POST",
 | 
				
			||||||
 | 
					// 			body: JSON.stringify([{ ...x }]),
 | 
				
			||||||
 | 
					// 			headers: {
 | 
				
			||||||
 | 
					// 				"content-type": "application/json",
 | 
				
			||||||
 | 
					// 			},
 | 
				
			||||||
 | 
					// 		});
 | 
				
			||||||
 | 
					// 		const bb = await res.json();
 | 
				
			||||||
 | 
					// 		if (bb.ok) {
 | 
				
			||||||
 | 
					// 			doLoad();
 | 
				
			||||||
 | 
					// 		} else {
 | 
				
			||||||
 | 
					// 			console.error("fail to add document");
 | 
				
			||||||
 | 
					// 		}
 | 
				
			||||||
 | 
					// 	};
 | 
				
			||||||
 | 
					// 	const CommitAll = async (type: string) => {
 | 
				
			||||||
 | 
					// 		const res = await fetch("/api/diff/commitall", {
 | 
				
			||||||
 | 
					// 			method: "POST",
 | 
				
			||||||
 | 
					// 			body: JSON.stringify({ type: type }),
 | 
				
			||||||
 | 
					// 			headers: {
 | 
				
			||||||
 | 
					// 				"content-type": "application/json",
 | 
				
			||||||
 | 
					// 			},
 | 
				
			||||||
 | 
					// 		});
 | 
				
			||||||
 | 
					// 		const bb = await res.json();
 | 
				
			||||||
 | 
					// 		if (bb.ok) {
 | 
				
			||||||
 | 
					// 			doLoad();
 | 
				
			||||||
 | 
					// 		} else {
 | 
				
			||||||
 | 
					// 			console.error("fail to add document");
 | 
				
			||||||
 | 
					// 		}
 | 
				
			||||||
 | 
					// 	};
 | 
				
			||||||
 | 
					// 	useEffect(() => {
 | 
				
			||||||
 | 
					// 		doLoad();
 | 
				
			||||||
 | 
					// 		const i = setInterval(doLoad, 5000);
 | 
				
			||||||
 | 
					// 		return () => {
 | 
				
			||||||
 | 
					// 			clearInterval(i);
 | 
				
			||||||
 | 
					// 		};
 | 
				
			||||||
 | 
					// 	}, []);
 | 
				
			||||||
 | 
					// 	const menu = CommonMenuList();
 | 
				
			||||||
 | 
					// 	return (
 | 
				
			||||||
 | 
					// 		<Headline menu={menu}>
 | 
				
			||||||
 | 
					// 			<PagePad>
 | 
				
			||||||
 | 
					// 				{ctx.username == "admin" ? (
 | 
				
			||||||
 | 
					// 					<div>
 | 
				
			||||||
 | 
					// 						{diffList.map((x) => (
 | 
				
			||||||
 | 
					// 							<TypeDifference key={x.type} content={x} onCommit={Commit} onCommitAll={CommitAll} />
 | 
				
			||||||
 | 
					// 						))}
 | 
				
			||||||
 | 
					// 					</div>
 | 
				
			||||||
 | 
					// 				) : (
 | 
				
			||||||
 | 
					// 					<Typography variant="h2">Not Allowed : please login as an admin</Typography>
 | 
				
			||||||
 | 
					// 				)}
 | 
				
			||||||
 | 
					// 			</PagePad>
 | 
				
			||||||
 | 
					// 		</Headline>
 | 
				
			||||||
 | 
					// 	);
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
| 
						 | 
					@ -1,63 +0,0 @@
 | 
				
			||||||
import { Button } from "@/components/ui/button";
 | 
					 | 
				
			||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
 | 
					 | 
				
			||||||
import { Separator } from "@/components/ui/separator";
 | 
					 | 
				
			||||||
import { useDifferenceDoc, commit, commitAll } from "@/hook/useDifference";
 | 
					 | 
				
			||||||
import { useLogin } from "@/state/user";
 | 
					 | 
				
			||||||
import { Fragment } from "react/jsx-runtime";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function DifferencePage() {
 | 
					 | 
				
			||||||
    const { data, isLoading, error } = useDifferenceDoc();
 | 
					 | 
				
			||||||
    const userInfo = useLogin();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!userInfo) {
 | 
					 | 
				
			||||||
        return <div className="p-4">
 | 
					 | 
				
			||||||
            <h2 className="text-3xl">
 | 
					 | 
				
			||||||
                Not logged in
 | 
					 | 
				
			||||||
            </h2>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (error) {
 | 
					 | 
				
			||||||
        return <div>Error: {String(error)}</div>
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
        <div className="p-4">
 | 
					 | 
				
			||||||
            <Card>
 | 
					 | 
				
			||||||
                <CardHeader className="relative">
 | 
					 | 
				
			||||||
                    <Button className="absolute right-2 top-8" variant="ghost"
 | 
					 | 
				
			||||||
                        onClick={() => commitAll("comic")}
 | 
					 | 
				
			||||||
                    >Commit All</Button>
 | 
					 | 
				
			||||||
                    <CardTitle className="text-2xl">Difference</CardTitle>
 | 
					 | 
				
			||||||
                    <CardDescription>Scanned Files List</CardDescription>
 | 
					 | 
				
			||||||
                </CardHeader>
 | 
					 | 
				
			||||||
                <CardContent>
 | 
					 | 
				
			||||||
                    <Separator decorative />
 | 
					 | 
				
			||||||
                    {isLoading && <div>Loading...</div>}
 | 
					 | 
				
			||||||
                    {data?.map((c) => {
 | 
					 | 
				
			||||||
                        const x = c.value;
 | 
					 | 
				
			||||||
                        return (
 | 
					 | 
				
			||||||
                            <Fragment key={c.type}>
 | 
					 | 
				
			||||||
                                {x.map((y) => (
 | 
					 | 
				
			||||||
                                    <div key={y.path} className="flex items-center mt-2">
 | 
					 | 
				
			||||||
                                        <p
 | 
					 | 
				
			||||||
                                        className="flex-1 text-sm text-wrap">{y.path}</p>
 | 
					 | 
				
			||||||
                                        <Button
 | 
					 | 
				
			||||||
                                            className="flex-none ml-2"
 | 
					 | 
				
			||||||
                                            variant="outline"
 | 
					 | 
				
			||||||
                                            onClick={() => commit(y.path, y.type)}
 | 
					 | 
				
			||||||
                                        >
 | 
					 | 
				
			||||||
                                            Commit
 | 
					 | 
				
			||||||
                                        </Button>
 | 
					 | 
				
			||||||
                                    </div>
 | 
					 | 
				
			||||||
                                ))}
 | 
					 | 
				
			||||||
                            </Fragment>
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                    })}
 | 
					 | 
				
			||||||
                </CardContent>
 | 
					 | 
				
			||||||
            </Card>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default DifferencePage;
 | 
					 | 
				
			||||||
| 
						 | 
					@ -24,10 +24,9 @@ function getUserSessions() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function refresh() {
 | 
					export async function refresh() {
 | 
				
			||||||
	const u = new URL("/api/user/refresh", BASE_API_URL);
 | 
						const u = new URL("/user/refresh", BASE_API_URL);
 | 
				
			||||||
    const res = await fetch(u, {
 | 
					    const res = await fetch(u, {
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
		credentials: "include",
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    if (res.status !== 200) throw new Error("Maybe Network Error");
 | 
					    if (res.status !== 200) throw new Error("Maybe Network Error");
 | 
				
			||||||
    const r = (await res.json()) as LoginLocalStorage & { refresh: boolean };
 | 
					    const r = (await res.json()) as LoginLocalStorage & { refresh: boolean };
 | 
				
			||||||
| 
						 | 
					@ -50,10 +49,9 @@ export async function refresh() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const doLogout = async () => {
 | 
					export const doLogout = async () => {
 | 
				
			||||||
	const u = new URL("/api/user/logout", BASE_API_URL);
 | 
						const u = new URL("/user/refresh", BASE_API_URL);
 | 
				
			||||||
	const req = await fetch(u, {
 | 
						const req = await fetch(u, {
 | 
				
			||||||
		method: "POST",
 | 
							method: "POST",
 | 
				
			||||||
		credentials: "include",
 | 
					 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
    const setVal = setAtomValue(userLoginStateAtom);
 | 
					    const setVal = setAtomValue(userLoginStateAtom);
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
| 
						 | 
					@ -81,12 +79,11 @@ export const doLogin = async (userLoginInfo: {
 | 
				
			||||||
	username: string;
 | 
						username: string;
 | 
				
			||||||
	password: string;
 | 
						password: string;
 | 
				
			||||||
}): Promise<string | LoginLocalStorage> => {
 | 
					}): Promise<string | LoginLocalStorage> => {
 | 
				
			||||||
	const u = new URL("/api/user/login", BASE_API_URL);
 | 
						const u = new URL("/user/refresh", BASE_API_URL);
 | 
				
			||||||
	const res = await fetch(u, {
 | 
						const res = await fetch(u, {
 | 
				
			||||||
		method: "POST",
 | 
							method: "POST",
 | 
				
			||||||
		body: JSON.stringify(userLoginInfo),
 | 
							body: JSON.stringify(userLoginInfo),
 | 
				
			||||||
		headers: { "content-type": "application/json" },
 | 
							headers: { "content-type": "application/json" },
 | 
				
			||||||
		credentials: "include",
 | 
					 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	const b = await res.json();
 | 
						const b = await res.json();
 | 
				
			||||||
	if (res.status !== 200) {
 | 
						if (res.status !== 200) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,3 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    "watch": ["testdata"]
 | 
					    "watch": []
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import { extname } from "node:path";
 | 
					import { extname } from "node:path";
 | 
				
			||||||
import type { DocumentBody } from "dbtype/api";
 | 
					import type { DocumentBody } from "../model/doc";
 | 
				
			||||||
import { readZip } from "../util/zipwrap";
 | 
					import { readZip } from "../util/zipwrap";
 | 
				
			||||||
import { type ContentConstructOption, createDefaultClass, registerContentReferrer } from "./file";
 | 
					import { type ContentConstructOption, createDefaultClass, registerContentReferrer } from "./file";
 | 
				
			||||||
import { TextWriter } from "@zip.js/zip.js";
 | 
					import { TextWriter } from "@zip.js/zip.js";
 | 
				
			||||||
| 
						 | 
					@ -29,18 +29,19 @@ export class ComicReferrer extends createDefaultClass("comic") {
 | 
				
			||||||
		const zip = await readZip(this.path);
 | 
							const zip = await readZip(this.path);
 | 
				
			||||||
		const entries = await zip.reader.getEntries();
 | 
							const entries = await zip.reader.getEntries();
 | 
				
			||||||
		this.pagenum = entries.filter((x) => ImageExt.includes(extname(x.filename))).length;
 | 
							this.pagenum = entries.filter((x) => ImageExt.includes(extname(x.filename))).length;
 | 
				
			||||||
		const descEntry = entries.find(x=> x.filename === "desc.json");
 | 
							const entry = entries.find(x=> x.filename === "desc.json");
 | 
				
			||||||
		if (descEntry === undefined) {
 | 
							if (entry === undefined) {
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (descEntry.getData === undefined) {
 | 
							if (entry.getData === undefined) {
 | 
				
			||||||
			throw new Error("entry.getData is undefined");
 | 
								throw new Error("entry.getData is undefined");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		const textWriter = new TextWriter();
 | 
							const textWriter = new TextWriter();
 | 
				
			||||||
		const data = (await descEntry.getData(textWriter));
 | 
							const data = (await entry.getData(textWriter));
 | 
				
			||||||
		this.desc = JSON.parse(data);
 | 
							this.desc = JSON.parse(data);
 | 
				
			||||||
		zip.reader.close()
 | 
							if (this.desc === undefined) {
 | 
				
			||||||
			.then(() => zip.handle.close());
 | 
								throw new Error(`JSON.parse is returning undefined. ${this.path} desc.json format error`);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async createDocumentBody(): Promise<DocumentBody> {
 | 
						async createDocumentBody(): Promise<DocumentBody> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
import { createHash } from "node:crypto";
 | 
					import { createHash } from "node:crypto";
 | 
				
			||||||
import { promises, type Stats } from "node:fs";
 | 
					import { promises, type Stats } from "node:fs";
 | 
				
			||||||
 | 
					import { Context, DefaultContext, DefaultState, Middleware, Next } from "koa";
 | 
				
			||||||
 | 
					import Router from "koa-router";
 | 
				
			||||||
import path, { extname } from "node:path";
 | 
					import path, { extname } from "node:path";
 | 
				
			||||||
import type { DocumentBody } from "dbtype/api";
 | 
					import type { DocumentBody } from "../model/mod";
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * content file or directory referrer
 | 
					 * content file or directory referrer
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -38,9 +40,8 @@ export const createDefaultClass = (type: string): ContentFileConstructor => {
 | 
				
			||||||
			this.stat = undefined;
 | 
								this.stat = undefined;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		async createDocumentBody(): Promise<DocumentBody> {
 | 
							async createDocumentBody(): Promise<DocumentBody> {
 | 
				
			||||||
			console.log(`createDocumentBody: ${this.path}`);
 | 
					 | 
				
			||||||
			const { base, dir, name } = path.parse(this.path);
 | 
								const { base, dir, name } = path.parse(this.path);
 | 
				
			||||||
			
 | 
					
 | 
				
			||||||
			const ret = {
 | 
								const ret = {
 | 
				
			||||||
				title: name,
 | 
									title: name,
 | 
				
			||||||
				basepath: dir,
 | 
									basepath: dir,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,13 +73,11 @@ class SqliteDocumentAccessor implements DocumentAccessor {
 | 
				
			||||||
			await trx.insertInto("tags")
 | 
								await trx.insertInto("tags")
 | 
				
			||||||
				.values(tags.map((x) => ({ name: x })))
 | 
									.values(tags.map((x) => ({ name: x })))
 | 
				
			||||||
				// on conflict is supported in sqlite and postgresql.
 | 
									// on conflict is supported in sqlite and postgresql.
 | 
				
			||||||
				.onConflict((oc) => oc.doNothing())
 | 
									.onConflict((oc) => oc.doNothing());
 | 
				
			||||||
				.execute();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (tags.length > 0) {
 | 
								if (tags.length > 0) {
 | 
				
			||||||
				await trx.insertInto("doc_tag_relation")
 | 
									await trx.insertInto("doc_tag_relation")
 | 
				
			||||||
					.values(tags.map((x) => ({ doc_id: id, tag_name: x })))
 | 
										.values(tags.map((x) => ({ doc_id: id, tag_name: x })));
 | 
				
			||||||
					.execute();
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return id;
 | 
								return id;
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,6 @@ export class DiffManager {
 | 
				
			||||||
			throw new Error("path is not exist");
 | 
								throw new Error("path is not exist");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		await list.delete(c);
 | 
							await list.delete(c);
 | 
				
			||||||
		console.log(`commit: ${c.path} ${c.type}`);
 | 
					 | 
				
			||||||
		const body = await c.createDocumentBody();
 | 
							const body = await c.createDocumentBody();
 | 
				
			||||||
		const id = await this.doc_cntr.add(body);
 | 
							const id = await this.doc_cntr.add(body);
 | 
				
			||||||
		return id;
 | 
							return id;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ type PostAddedBody = {
 | 
				
			||||||
	path: string;
 | 
						path: string;
 | 
				
			||||||
}[];
 | 
					}[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function checkPostAddedBody(body: unknown): body is PostAddedBody {
 | 
					function checkPostAddedBody(body: any): body is PostAddedBody {
 | 
				
			||||||
	if (Array.isArray(body)) {
 | 
						if (Array.isArray(body)) {
 | 
				
			||||||
		return body.map((x) => "type" in x && "path" in x).every((x) => x);
 | 
							return body.map((x) => "type" in x && "path" in x).every((x) => x);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -43,7 +43,6 @@ export const postAdded = (diffmgr: DiffManager) => async (ctx: Router.IRouterCon
 | 
				
			||||||
		docs: results,
 | 
							docs: results,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	ctx.type = "json";
 | 
						ctx.type = "json";
 | 
				
			||||||
	await next();
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export const postAddedAll = (diffmgr: DiffManager) => async (ctx: Router.IRouterContext, next: Koa.Next) => {
 | 
					export const postAddedAll = (diffmgr: DiffManager) => async (ctx: Router.IRouterContext, next: Koa.Next) => {
 | 
				
			||||||
	if (!ctx.is("json")) {
 | 
						if (!ctx.is("json")) {
 | 
				
			||||||
| 
						 | 
					@ -65,7 +64,6 @@ export const postAddedAll = (diffmgr: DiffManager) => async (ctx: Router.IRouter
 | 
				
			||||||
		ok: true,
 | 
							ok: true,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	ctx.type = "json";
 | 
						ctx.type = "json";
 | 
				
			||||||
	await next();
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
export const getNotWatched = (diffmgr : DiffManager)=> (ctx:Router.IRouterContext,next:Koa.Next)=>{
 | 
					export const getNotWatched = (diffmgr : DiffManager)=> (ctx:Router.IRouterContext,next:Koa.Next)=>{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,9 @@
 | 
				
			||||||
import { sign, TokenExpiredError, verify } from "jsonwebtoken";
 | 
					import { request } from "node:http";
 | 
				
			||||||
 | 
					import { decode, sign, TokenExpiredError, verify } from "jsonwebtoken";
 | 
				
			||||||
 | 
					import Knex from "knex";
 | 
				
			||||||
import type Koa from "koa";
 | 
					import type Koa from "koa";
 | 
				
			||||||
import Router from "koa-router";
 | 
					import Router from "koa-router";
 | 
				
			||||||
 | 
					import { createSqliteUserController } from "./db/mod";
 | 
				
			||||||
import type { IUser, UserAccessor } from "./model/mod";
 | 
					import type { IUser, UserAccessor } from "./model/mod";
 | 
				
			||||||
import { sendError } from "./route/error_handler";
 | 
					import { sendError } from "./route/error_handler";
 | 
				
			||||||
import { get_setting } from "./SettingConfig";
 | 
					import { get_setting } from "./SettingConfig";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@ export async function readZip(path: string): Promise<{
 | 
				
			||||||
	const fd = await open(path, "r");
 | 
						const fd = await open(path, "r");
 | 
				
			||||||
	const reader = new ZipReader(new FileReader(fd), {
 | 
						const reader = new ZipReader(new FileReader(fd), {
 | 
				
			||||||
		useCompressionStream: true,
 | 
							useCompressionStream: true,
 | 
				
			||||||
		preventClose: false,
 | 
							preventClose: true,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	return { reader, handle: fd };
 | 
						return { reader, handle: fd };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										24
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -23,9 +23,6 @@ importers:
 | 
				
			||||||
      '@radix-ui/react-radio-group':
 | 
					      '@radix-ui/react-radio-group':
 | 
				
			||||||
        specifier: ^1.1.3
 | 
					        specifier: ^1.1.3
 | 
				
			||||||
        version: 1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
					        version: 1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
				
			||||||
      '@radix-ui/react-separator':
 | 
					 | 
				
			||||||
        specifier: ^1.0.3
 | 
					 | 
				
			||||||
        version: 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
					 | 
				
			||||||
      '@radix-ui/react-slot':
 | 
					      '@radix-ui/react-slot':
 | 
				
			||||||
        specifier: ^1.0.2
 | 
					        specifier: ^1.0.2
 | 
				
			||||||
        version: 1.0.2(@types/react@18.2.71)(react@18.2.0)
 | 
					        version: 1.0.2(@types/react@18.2.71)(react@18.2.0)
 | 
				
			||||||
| 
						 | 
					@ -1287,27 +1284,6 @@ packages:
 | 
				
			||||||
      react-dom: 18.2.0(react@18.2.0)
 | 
					      react-dom: 18.2.0(react@18.2.0)
 | 
				
			||||||
    dev: false
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0):
 | 
					 | 
				
			||||||
    resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==}
 | 
					 | 
				
			||||||
    peerDependencies:
 | 
					 | 
				
			||||||
      '@types/react': '*'
 | 
					 | 
				
			||||||
      '@types/react-dom': '*'
 | 
					 | 
				
			||||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
					 | 
				
			||||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
					 | 
				
			||||||
    peerDependenciesMeta:
 | 
					 | 
				
			||||||
      '@types/react':
 | 
					 | 
				
			||||||
        optional: true
 | 
					 | 
				
			||||||
      '@types/react-dom':
 | 
					 | 
				
			||||||
        optional: true
 | 
					 | 
				
			||||||
    dependencies:
 | 
					 | 
				
			||||||
      '@babel/runtime': 7.24.1
 | 
					 | 
				
			||||||
      '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
					 | 
				
			||||||
      '@types/react': 18.2.71
 | 
					 | 
				
			||||||
      '@types/react-dom': 18.2.22
 | 
					 | 
				
			||||||
      react: 18.2.0
 | 
					 | 
				
			||||||
      react-dom: 18.2.0(react@18.2.0)
 | 
					 | 
				
			||||||
    dev: false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /@radix-ui/react-slot@1.0.2(@types/react@18.2.71)(react@18.2.0):
 | 
					  /@radix-ui/react-slot@1.0.2(@types/react@18.2.71)(react@18.2.0):
 | 
				
			||||||
    resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
 | 
					    resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
 | 
				
			||||||
    peerDependencies:
 | 
					    peerDependencies:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue