diff --git a/app.ts b/app.ts
index c769f3f..c1701d7 100644
--- a/app.ts
+++ b/app.ts
@@ -3,7 +3,7 @@ import { get_setting } from "./src/setting";
import { create_server, start_server } from "./src/server";
import { getAdminAccessTokenValue,getAdminRefreshTokenValue, accessTokenName, refreshTokenName } from "./src/login";
-const get_loading_html = (content?:string)=> `
+const get_loading_html = (message?:string)=> `
react-sample
@@ -31,8 +31,8 @@ h1 {
}
- ${content || "Loading..."}
- ${content === undefined ? '' : ""}
+ ${message || "Loading..."}
+ ${message === undefined ? '' : ""}
`;
diff --git a/migrations/initial.ts b/migrations/initial.ts
index 6a8b075..86a16d3 100644
--- a/migrations/initial.ts
+++ b/migrations/initial.ts
@@ -6,7 +6,7 @@ export async function up(knex:Knex) {
b.string("password_hash",64).notNullable();
b.string("password_salt",64).notNullable();
});
- await knex.schema.createTable("contents",(b)=>{
+ await knex.schema.createTable("document",(b)=>{
b.increments("id").primary();
b.string("title").notNullable();
b.string("content_type",16).notNullable();
@@ -21,12 +21,12 @@ export async function up(knex:Knex) {
b.string("name").primary();
b.text("description");
});
- await knex.schema.createTable("content_tag_relation",(b)=>{
- b.integer("content_id").unsigned().notNullable();
+ await knex.schema.createTable("doc_tag_relation",(b)=>{
+ b.integer("doc_id").unsigned().notNullable();
b.string("tag_name").notNullable();
- b.foreign("content_id").references("contents.id");
+ b.foreign("doc_id").references("document.id");
b.foreign("tag_name").references("tags.name");
- b.primary(["content_id","tag_name"]);
+ b.primary(["doc_id","tag_name"]);
});
await knex.schema.createTable("permissions",b=>{
b.string('username').notNullable();
@@ -45,8 +45,8 @@ export async function up(knex:Knex) {
export async function down(knex:Knex) {
//throw new Error('Downward migrations are not supported. Restore from backup.');
await knex.schema.dropTable("users");
- await knex.schema.dropTable("contents");
+ await knex.schema.dropTable("document");
await knex.schema.dropTable("tags");
- await knex.schema.dropTable("content_tag_relation");
+ await knex.schema.dropTable("document_tag_relation");
await knex.schema.dropTable("permissions");
};
diff --git a/package.json b/package.json
index 9ca8a54..52e83ab 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
},
"build": {
"asar": false,
- "files":[
+ "files": [
"build/**/*",
"node_modules/**/*",
"package.json"
@@ -79,6 +79,7 @@
"css-loader": "^5.0.1",
"electron": "^11.1.1",
"electron-builder": "^22.9.1",
+ "eslint-plugin-node": "^11.1.0",
"mini-css-extract-plugin": "^1.3.3",
"style-loader": "^2.0.0",
"ts-node": "^9.1.1",
diff --git a/plan.md b/plan.md
index 6c91847..8427437 100644
--- a/plan.md
+++ b/plan.md
@@ -5,8 +5,9 @@
### server routing
- content
- \d+
- - image
- - (?P\d+)
+ - manga
+ - (?P<page>\d+)
+ - video
- diff
- itemNum
- diff
@@ -14,6 +15,8 @@
- commit
- user
- login
+ - logout
+ - refresh
### client routing
@@ -23,6 +26,9 @@
- login
- profile
- search ? querystring...;
+- setting
+- difference
+- profile
## TODO
- server push
diff --git a/src/client/accessor/contents.ts b/src/client/accessor/document.ts
similarity index 72%
rename from src/client/accessor/contents.ts
rename to src/client/accessor/document.ts
index d35353c..98d442f 100644
--- a/src/client/accessor/contents.ts
+++ b/src/client/accessor/document.ts
@@ -1,8 +1,8 @@
-import {Content, ContentAccessor, ContentContent, QueryListOption} from "../../model/contents";
+import {Document, DocumentAccessor, DocumentBody, QueryListOption} from "../../model/doc";
import {toQueryString} from './util';
-const baseurl = "/content";
+const baseurl = "/api/doc";
-export * from "../../model/contents";
+export * from "../../model/doc";
export class FetchFailError implements Error{
name: string;
@@ -14,14 +14,14 @@ export class FetchFailError implements Error{
this.stack = stack;
}
}
-export class ClientContentAccessor implements ContentAccessor{
- async findList(option?: QueryListOption | undefined): Promise{
+export class ClientDocumentAccessor implements DocumentAccessor{
+ async findList(option?: QueryListOption | undefined): Promise{
let res = await fetch(`${baseurl}/search?${option !== undefined ? toQueryString(option) : ""}`);
if(res.status !== 200) throw new FetchFailError("findList Failed");
let ret = await res.json();
return ret;
}
- async findById(id: number, tagload?: boolean | undefined): Promise{
+ async findById(id: number, tagload?: boolean | undefined): Promise{
let res = await fetch(`${baseurl}/${id}`);
if(res.status !== 200) throw new FetchFailError("findById Failed");;
let ret = await res.json();
@@ -30,11 +30,11 @@ export class ClientContentAccessor implements ContentAccessor{
/**
* not implement
*/
- async findListByBasePath(basepath: string): Promise{
+ async findListByBasePath(basepath: string): Promise{
throw new Error("not implement");
return [];
}
- async update(c: Partial & { id: number; }): Promise{
+ async update(c: Partial & { id: number; }): Promise{
const {id,...rest} = c;
const res = await fetch(`${baseurl}/${id}`,{
method: "POST",
@@ -43,7 +43,7 @@ export class ClientContentAccessor implements ContentAccessor{
const ret = await res.json();
return ret;
}
- async add(c: ContentContent): Promise{
+ async add(c: DocumentBody): Promise{
const res = await fetch(`${baseurl}`,{
method: "POST",
body: JSON.stringify(c)
@@ -58,7 +58,7 @@ export class ClientContentAccessor implements ContentAccessor{
const ret = await res.json();
return ret;
}
- async addTag(c: Content, tag_name: string): Promise{
+ async addTag(c: Document, tag_name: string): Promise{
const {id,...rest} = c;
const res = await fetch(`${baseurl}/${id}/tags/${tag_name}`,{
method: "POST",
@@ -67,7 +67,7 @@ export class ClientContentAccessor implements ContentAccessor{
const ret = await res.json();
return ret;
}
- async delTag(c: Content, tag_name: string): Promise{
+ async delTag(c: Document, tag_name: string): Promise{
const {id,...rest} = c;
const res = await fetch(`${baseurl}/${id}/tags/${tag_name}`,{
method: "DELETE",
@@ -77,9 +77,9 @@ export class ClientContentAccessor implements ContentAccessor{
return ret;
}
}
-export const CContentAccessor = new ClientContentAccessor;
-export const makeThumbnailUrl = (x: Content)=>{
- return `/content/${x.id}/${x.content_type}/thumbnail`;
+export const CDocumentAccessor = new ClientDocumentAccessor;
+export const makeThumbnailUrl = (x: Document)=>{
+ return `${baseurl}/${x.id}/${x.content_type}/thumbnail`;
}
-export default CContentAccessor;
\ No newline at end of file
+export default CDocumentAccessor;
\ No newline at end of file
diff --git a/src/client/app.tsx b/src/client/app.tsx
index a469b01..baaf2f4 100644
--- a/src/client/app.tsx
+++ b/src/client/app.tsx
@@ -1,7 +1,7 @@
import React, { createContext, useEffect, useRef, useState } from 'react';
import ReactDom from 'react-dom';
import {BrowserRouter, Redirect, Route, Switch as RouterSwitch} from 'react-router-dom';
-import { Gallery, ContentAbout, LoginPage, NotFoundPage, ProfilePage,DifferencePage} from './page/mod';
+import { Gallery, DocumentAbout, LoginPage, NotFoundPage, ProfilePage,DifferencePage} from './page/mod';
import {getInitialValue, UserContext} from './state';
import './css/style.css';
@@ -27,7 +27,7 @@ const App = () => {
}>
}>
- }>
+ }>
}/>
diff --git a/src/client/component/contentinfo.tsx b/src/client/component/contentinfo.tsx
index 6e58843..5d2d484 100644
--- a/src/client/component/contentinfo.tsx
+++ b/src/client/component/contentinfo.tsx
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { Redirect, Route, Switch, useHistory, useRouteMatch, match as MatchType, Link as RouterLink } from 'react-router-dom';
-import ContentAccessor, { Content } from '../accessor/contents';
+import { Document } from '../accessor/document';
import { LoadingCircle } from '../component/loading';
import { Link, Paper, makeStyles, Theme, Box, useTheme, Typography } from '@material-ui/core';
import { ThumbnailContainer } from '../page/reader/reader';
@@ -74,7 +74,7 @@ const useStyles = makeStyles((theme: Theme) => ({
}))
export const ContentInfo = (props: {
- content: Content, children?: React.ReactNode, classes?: {
+ document: Document, children?: React.ReactNode, classes?: {
root?: string,
thumbnail_anchor?: string,
thumbnail_content?: string,
@@ -88,31 +88,30 @@ export const ContentInfo = (props: {
}) => {
const classes = useStyles();
const theme = useTheme();
- const content = props.content;
+ const document = props.document;
const propclasses = props.classes || {};
-
const rootName = props.short ? classes.short_root : classes.root;
const thumbnail_anchor = props.short ? classes.short_thumbnail_anchor : "";
const thumbnail_content = props.short ? classes.short_thumbnail_content :
classes.thumbnail_content;
const subinfoContainer = props.short ? classes.short_subinfoContainer :
classes.subinfoContainer;
- let allTag = content.tags;
+ let allTag = document.tags;
const artists = allTag.filter(x => x.startsWith("artist:")).map(x => x.substr(7));
allTag = allTag.filter(x => !x.startsWith("artist:"));
return (
-
- {content.title}
+ {document.title}
Artist
diff --git a/src/client/component/galleryinfo.tsx b/src/client/component/galleryinfo.tsx
index e9f3fe4..886e2e4 100644
--- a/src/client/component/galleryinfo.tsx
+++ b/src/client/component/galleryinfo.tsx
@@ -1,7 +1,7 @@
import { Box, Paper, Link, useMediaQuery, Portal, List, ListItem, ListItemIcon, Tooltip, ListItemText } from '@material-ui/core';
import {useTheme, makeStyles, Theme} from '@material-ui/core/styles';
import React, { useEffect, useState } from 'react';
-import ContentAccessor,{QueryListOption, Content} from '../accessor/contents';
+import ContentAccessor,{QueryListOption, Document} from '../accessor/document';
import {Link as RouterLink} from 'react-router-dom';
import { LoadingCircle, ContentInfo, NavList, NavItem } from './mod';
import {toQueryString} from '../accessor/util';
@@ -42,29 +42,29 @@ export type GalleryProp = {
option?:QueryListOption;
};
type GalleryState = {
- content:Content[]|undefined;
+ documents:Document[]|undefined;
}
export const GalleryInfo = (props: GalleryProp)=>{
- const [state,setState]= useState({content:undefined});
+ const [state,setState]= useState({documents:undefined});
useEffect(()=>{
const load = (async ()=>{
const c = await ContentAccessor.findList(props.option);
//todo : if c is undefined, retry to fetch 3 times. and show error message.
- setState({content:c});
+ setState({documents:c});
})
load();
},[props.option]);
const classes = useStyles();
const queryString = toQueryString(props.option||{});
- if(state.content === undefined){
+ if(state.documents === undefined){
return ();
}
else{
return ({
- state.content.map(x=>{
- return ({
+ return ();
})
}
diff --git a/src/client/page/contentinfo.tsx b/src/client/page/contentinfo.tsx
index 7e38221..755a85f 100644
--- a/src/client/page/contentinfo.tsx
+++ b/src/client/page/contentinfo.tsx
@@ -1,6 +1,6 @@
import React, { useState, useEffect, useContext } from 'react';
import { Redirect, Route, Switch, useHistory, useRouteMatch, match as MatchType, Link as RouterLink, useParams, useLocation } from 'react-router-dom';
-import ContentAccessor, { Content } from '../accessor/contents';
+import DocumentAccessor, { Document } from '../accessor/document';
import { LoadingCircle } from '../component/loading';
import { Link, Paper, makeStyles, Theme, Box, useTheme, Typography } from '@material-ui/core';
import {ArrowBack as ArrowBackIcon } from '@material-ui/icons';
@@ -10,8 +10,8 @@ import { BackItem, CommonMenuList, ContentInfo, Headline, NavItem, NavList } fro
export const makeContentInfoUrl = (id: number) => `/doc/${id}`;
export const makeMangaReaderUrl = (id: number) => `/doc/${id}/reader`;
-type ContentState = {
- content: Content | undefined,
+type DocumentState = {
+ doc: Document | undefined,
notfound: boolean,
}
@@ -27,25 +27,25 @@ const useStyles = makeStyles((theme:Theme)=>({
}
}));
-export const ContentAbout = (prop: { match: MatchType }) => {
+export const DocumentAbout = (prop: { match: MatchType }) => {
const match = useRouteMatch<{id:string}>("/doc/:id");
if (match == null) {
throw new Error("unreachable");
}
const id = Number.parseInt(match.params['id']);
- const [info, setInfo] = useState({ content: undefined, notfound:false });
- const menu_list = () => ;
+ const [info, setInfo] = useState({ doc: undefined, notfound:false });
+ const menu_list = (link?:string) => ;
useEffect(() => {
(async () => {
if (!isNaN(id)) {
- const c = await ContentAccessor.findById(id);
- setInfo({ content: c, notfound: c === undefined });
+ const c = await DocumentAccessor.findById(id);
+ setInfo({ doc: c, notfound: c === undefined });
}
- })()
+ })();
}, []);
const classes = useStyles();
-
+ console.log(info.doc);
if (isNaN(id)) {
return (
@@ -60,18 +60,18 @@ export const ContentAbout = (prop: { match: MatchType }) => {
)
}
- else if (info.content === undefined) {
+ else if (info.doc === undefined) {
return (
);
}
else{
- const ReaderPage = getPresenter(info.content);
+ const ReaderPage = getPresenter(info.doc);
return (
-
+
@@ -79,7 +79,7 @@ export const ContentAbout = (prop: { match: MatchType }) => {
content:classes.noPaddingContent,
toolbar:classes.noPaddingToolbar
}}>
-
+
diff --git a/src/client/page/gallery.tsx b/src/client/page/gallery.tsx
index 33fd96d..74d710f 100644
--- a/src/client/page/gallery.tsx
+++ b/src/client/page/gallery.tsx
@@ -8,9 +8,8 @@ import { QueryStringToMap } from '../accessor/util';
export const Gallery = ()=>{
const location = useLocation();
const query = QueryStringToMap(location.search);
- location;
const menu_list = CommonMenuList({url:location.search});
-
+
return (
)
diff --git a/src/client/page/reader/manga.tsx b/src/client/page/reader/manga.tsx
index c1c857d..631fd83 100644
--- a/src/client/page/reader/manga.tsx
+++ b/src/client/page/reader/manga.tsx
@@ -1,6 +1,6 @@
import React, {useState, useEffect} from 'react';
import { Typography, useTheme } from '@material-ui/core';
-import { Content } from '../../accessor/contents';
+import { Document } from '../../accessor/document';
type MangaType = "manga"|"artist cg"|"donjinshi"|"western"
@@ -13,9 +13,9 @@ export type PresentableTag = {
tags: string[],
}
-export const MangaReader = (props:{content:Content})=>{
+export const MangaReader = (props:{doc:Document})=>{
const theme = useTheme();
- const additional = props.content.additional;
+ const additional = props.doc.additional;
const [curPage,setCurPage] = useState(0);
if(!('page' in additional)){
console.error("invalid content : page read fail : "+ JSON.stringify(additional));
@@ -40,7 +40,7 @@ export const MangaReader = (props:{content:Content})=>{
});
//theme.mixins.toolbar.minHeight;
return (
-
);
}
diff --git a/src/client/page/reader/reader.tsx b/src/client/page/reader/reader.tsx
index 8ec4e41..71601b1 100644
--- a/src/client/page/reader/reader.tsx
+++ b/src/client/page/reader/reader.tsx
@@ -1,18 +1,18 @@
import { Typography } from '@material-ui/core';
import React from 'react';
-import { Content, makeThumbnailUrl } from '../../accessor/contents';
+import { Document, makeThumbnailUrl } from '../../accessor/document';
import {MangaReader} from './manga';
import {VideoReader} from './video'
export interface PagePresenterProp{
- content:Content,
+ doc:Document,
className?:string
}
interface PagePresenter{
(prop:PagePresenterProp):JSX.Element
}
-export const getPresenter = (content:Content):PagePresenter => {
+export const getPresenter = (content:Document):PagePresenter => {
switch (content.content_type) {
case "manga":
return MangaReader;
@@ -22,7 +22,7 @@ export const getPresenter = (content:Content):PagePresenter => {
return ()=>Not implemented reader;
}
-export const ThumbnailContainer = (props:{content:Content, className?:string})=>{
+export const ThumbnailContainer = (props:{content:Document, className?:string})=>{
const thumbnailurl = makeThumbnailUrl(props.content);
if(props.content.content_type === "video"){
return ()
diff --git a/src/client/page/reader/video.tsx b/src/client/page/reader/video.tsx
index caa546e..fccd2d6 100644
--- a/src/client/page/reader/video.tsx
+++ b/src/client/page/reader/video.tsx
@@ -1,7 +1,7 @@
import React from 'react';
-import { Content } from '../../accessor/contents';
+import { Document } from '../../accessor/document';
-export const VideoReader = (props:{content:Content})=>{
- const id = props.content.id;
- return ;
+export const VideoReader = (props:{doc:Document})=>{
+ const id = props.doc.id;
+ return ;
}
\ No newline at end of file
diff --git a/src/content/manga.ts b/src/content/manga.ts
index 68535ab..66f54f3 100644
--- a/src/content/manga.ts
+++ b/src/content/manga.ts
@@ -1,8 +1,14 @@
-import {ContentReferrer} from './referrer';
+import {ContentFile} from './referrer';
import {createDefaultClass,registerContentReferrer} from './referrer';
+import {readZip} from '../util/zipwrap';
export class MangaReferrer extends createDefaultClass("manga"){
constructor(path:string){
super(path);
+ /*(async ()=>{
+ const zip = await readZip(path);
+ const entry = zip.entries();
+
+ })*/
}
};
registerContentReferrer(MangaReferrer);
\ No newline at end of file
diff --git a/src/content/mod.ts b/src/content/mod.ts
index 6bebf16..9bef609 100644
--- a/src/content/mod.ts
+++ b/src/content/mod.ts
@@ -1,3 +1,3 @@
import './manga';
import './video';
-export {ContentReferrer,createContentReferrer} from './referrer';
\ No newline at end of file
+export {ContentFile as ContentReferrer,createContentReferrer} from './referrer';
\ No newline at end of file
diff --git a/src/content/referrer.ts b/src/content/referrer.ts
index f4b0395..4284a0d 100644
--- a/src/content/referrer.ts
+++ b/src/content/referrer.ts
@@ -6,15 +6,15 @@ import {extname} from 'path';
/**
* content file or directory referrer
*/
-export interface ContentReferrer{
+export interface ContentFile{
getHash():Promise;
readonly path: string;
readonly type: string;
desc: object|undefined;
}
-type ContentReferrerConstructor = (new (path:string,desc?:object) => ContentReferrer)&{content_type:string};
-export const createDefaultClass = (type:string):ContentReferrerConstructor=>{
- let cons = class implements ContentReferrer{
+type ContentFileConstructor = (new (path:string,desc?:object) => ContentFile)&{content_type:string};
+export const createDefaultClass = (type:string):ContentFileConstructor=>{
+ let cons = class implements ContentFile{
readonly path: string;
type = type;
static content_type = type;
@@ -37,8 +37,8 @@ export const createDefaultClass = (type:string):ContentReferrerConstructor=>{
};
return cons;
}
-let ContstructorTable:{[k:string]:ContentReferrerConstructor} = {};
-export function registerContentReferrer(s: ContentReferrerConstructor){
+let ContstructorTable:{[k:string]:ContentFileConstructor} = {};
+export function registerContentReferrer(s: ContentFileConstructor){
console.log(`registered content type: ${s.content_type}`)
ContstructorTable[s.content_type] = s;
}
@@ -50,7 +50,7 @@ export function createContentReferrer(type:string,path:string,desc?:object){
}
return new constructorMethod(path,desc);
}
-export function getContentRefererConstructor(type:string): ContentReferrerConstructor|undefined{
+export function getContentRefererConstructor(type:string): ContentFileConstructor|undefined{
const ret = ContstructorTable[type];
return ret;
}
\ No newline at end of file
diff --git a/src/content/video.ts b/src/content/video.ts
index 8a16429..06a206c 100644
--- a/src/content/video.ts
+++ b/src/content/video.ts
@@ -1,4 +1,4 @@
-import {ContentReferrer, registerContentReferrer} from './referrer';
+import {ContentFile, registerContentReferrer} from './referrer';
import {createDefaultClass} from './referrer';
export class VideoReferrer extends createDefaultClass("video"){
diff --git a/src/db/contents.ts b/src/db/doc.ts
similarity index 69%
rename from src/db/contents.ts
rename to src/db/doc.ts
index bd310d7..8230072 100644
--- a/src/db/contents.ts
+++ b/src/db/doc.ts
@@ -1,53 +1,53 @@
-import { Content, ContentContent, ContentAccessor, QueryListOption } from '../model/contents';
+import { Document, DocumentBody, DocumentAccessor, QueryListOption } from '../model/doc';
import Knex from 'knex';
import {createKnexTagController} from './tag';
import { TagAccessor } from '../model/tag';
type DBTagContentRelation = {
- content_id:number,
+ doc_id:number,
tag_name:string
}
-class KnexContentsAccessor implements ContentAccessor{
+class KnexContentsAccessor implements DocumentAccessor{
knex : Knex;
tagController: TagAccessor;
constructor(knex : Knex){
this.knex = knex;
this.tagController = createKnexTagController(knex);
}
- async add(c: ContentContent){
+ async add(c: DocumentBody){
const {tags,additional, ...rest} = c;
const id_lst = await this.knex.insert({
additional:JSON.stringify(additional),
...rest
- }).into('contents');
+ }).into('document');
const id = id_lst[0];
for (const it of tags) {
this.tagController.addTag({name:it});
}
if(tags.length > 0){
await this.knex.insert(
- tags.map(x=>({content_id:id,tag_name:x}))
- ).into("content_tag_relation");
+ tags.map(x=>({doc_id:id,tag_name:x}))
+ ).into("doc_tag_relation");
}
return id;
};
async del(id:number) {
if (await this.findById(id) !== undefined){
- await this.knex.delete().from("content_tag_relation").where({content_id:id});
- await this.knex.delete().from("contents").where({id:id});
+ await this.knex.delete().from("doc_tag_relation").where({doc_id:id});
+ await this.knex.delete().from("document").where({id:id});
return true;
}
return false;
};
- async findById(id:number,tagload?:boolean): Promise{
- const s = await this.knex.select("*").from("contents").where({id:id});
+ async findById(id:number,tagload?:boolean): Promise{
+ const s = await this.knex.select("*").from("document").where({id:id});
if(s.length === 0) return undefined;
const first = s[0];
let ret_tags:string[] = []
if(tagload === true){
const tags : DBTagContentRelation[] = await this.knex.select("*")
- .from("content_tag_relation").where({content_id:first.id});
+ .from("doc_tag_relation").where({doc_id:first.id});
ret_tags = tags.map(x=>x.tag_name);
}
return {
@@ -70,13 +70,13 @@ class KnexContentsAccessor implements ContentAccessor{
const buildquery = ()=>{
let query = this.knex.select("*");
if(allow_tag.length > 0){
- query = query.from("content_tag_relation").innerJoin("contents","content_tag_relation.content_id","contents.id");
+ query = query.from("doc_tag_relation").innerJoin("document","doc_tag_relation.doc_id","document.id");
for(const tag of allow_tag){
query = query.where({tag_name:tag});
}
}
else{
- query = query.from("contents");
+ query = query.from("document");
}
if(word !== undefined){
//don't worry about sql injection.
@@ -98,56 +98,58 @@ class KnexContentsAccessor implements ContentAccessor{
}
let query = buildquery();
//console.log(query.toSQL());
- let result:Content[] = await query;
+ let result:Document[] = await query;
for(let i of result){
i.additional = JSON.parse((i.additional as unknown) as string);
}
if(eager_loading){
- let idmap: {[index:number]:Content} = {};
+ let idmap: {[index:number]:Document} = {};
for(const r of result){
idmap[r.id] = r;
r.tags = [];
}
let subquery = buildquery();
- let tagresult:{id:number,tag_name:string}[] = await this.knex.select("id","content_tag_relation.tag_name").from(subquery)
- .innerJoin("content_tag_relation","content_tag_relation.content_id","id");
+ let tagquery= this.knex.select("id","doc_tag_relation.tag_name").from(subquery)
+ .innerJoin("doc_tag_relation","doc_tag_relation.doc_id","id");
+ //console.log(tagquery.toSQL());
+ let tagresult:{id:number,tag_name:string}[] = await tagquery;
for(const {id,tag_name} of tagresult){
idmap[id].tags.push(tag_name);
}
}
return result;
};
- async findListByBasePath(path:string):Promise{
- let results = await this.knex.select("*").from("contents").where({basepath:path});
+ async findListByBasePath(path:string):Promise{
+ let results = await this.knex.select("*").from("document").where({basepath:path});
return results.map(x=>({
...x,
tags:[],
additional:JSON.parse(x.additional || "{}"),
}));
}
- async update(c:Partial & { id:number }){
+ async update(c:Partial & { id:number }){
const {id,tags,...rest} = c;
if (await this.findById(id) !== undefined){
- await this.knex.update(rest).where({id: id}).from("contents");
+ await this.knex.update(rest).where({id: id}).from("document");
return true;
}
return false;
}
- async addTag(c: Content,tag_name:string){
+ async addTag(c: Document,tag_name:string){
if (c.tags.includes(tag_name)) return false;
this.tagController.addTag({name:tag_name});
- await this.knex.insert({tag_name: tag_name, content_id: c.id})
- .into("content_tag_relation");
+ await this.knex.insert({tag_name: tag_name, doc_id: c.id})
+ .into("doc_tag_relation");
c.tags.push(tag_name);
return true;
}
- async delTag(c: Content,tag_name:string){
+ async delTag(c: Document,tag_name:string){
if (c.tags.includes(tag_name)) return false;
- await this.knex.delete().where({tag_name: tag_name,content_id: c.id}).from("content_tag_relation");
+ await this.knex.delete().where({tag_name: tag_name,doc_id: c.id}).from("doc_tag_relation");
c.tags.push(tag_name);
return true;
}
}
-export const createKnexContentsAccessor = (knex:Knex): ContentAccessor=>{
+export const createKnexContentsAccessor = (knex:Knex): DocumentAccessor=>{
return new KnexContentsAccessor(knex);
}
\ No newline at end of file
diff --git a/src/db/mod.ts b/src/db/mod.ts
index c36784f..747d980 100644
--- a/src/db/mod.ts
+++ b/src/db/mod.ts
@@ -1,3 +1,3 @@
-export * from './contents';
+export * from './doc';
export * from './tag';
export * from './user';
\ No newline at end of file
diff --git a/src/diff/diff.ts b/src/diff/diff.ts
index 96b3d66..d9578ea 100644
--- a/src/diff/diff.ts
+++ b/src/diff/diff.ts
@@ -1,6 +1,6 @@
import { watch } from 'fs';
import { promises } from 'fs';
-import { ContentReferrer, createContentReferrer, getContentRefererConstructor } from '../content/referrer'
+import { ContentFile, createContentReferrer, getContentRefererConstructor } from '../content/referrer'
import path from 'path';
const readdir = promises.readdir;
@@ -12,21 +12,21 @@ export class Watcher{
/**
* @todo : alter type Map
*/
- private _added: ContentReferrer[];
- private _deleted: ContentReferrer[];
+ private _added: ContentFile[];
+ private _deleted: ContentFile[];
constructor(path:string,type:string){
this._path = path;
this._added =[];
this._deleted =[];
this._type = type;
}
- public get added() : ContentReferrer[] {
+ public get added() : ContentFile[] {
return this._added;
}
/*public set added(diff : FileDiff[]) {
this._added = diff;
}*/
- public get deleted(): ContentReferrer[]{
+ public get deleted(): ContentFile[]{
return this._deleted;
}
/*public set deleted(diff : FileDiff[]){
diff --git a/src/model/contents.ts b/src/model/doc.ts
similarity index 61%
rename from src/model/contents.ts
rename to src/model/doc.ts
index dd380ed..3d2b7c5 100644
--- a/src/model/contents.ts
+++ b/src/model/doc.ts
@@ -1,12 +1,12 @@
import {TagAccessor} from './tag';
-import {check_type} from './../util/type_check'
+import {check_type} from '../util/type_check'
type JSONPrimitive = null|boolean|number|string;
interface JSONMap extends Record{}
interface JSONArray extends Array{};
type JSONType = JSONMap|JSONPrimitive|JSONArray;
-export interface ContentContent{
+export interface DocumentBody{
title : string,
content_type : string,
basepath : string,
@@ -16,7 +16,7 @@ export interface ContentContent{
tags : string[],//eager loading
}
-export const MetaContentContent = {
+export const MetaContentBody = {
title : "string",
content_type : "string",
basepath : "string",
@@ -26,18 +26,18 @@ export const MetaContentContent = {
tags : "string[]",
}
-export const isContentContent = (c : any):c is ContentContent =>{
- return check_type(c,MetaContentContent);
+export const isDocBody = (c : any):c is DocumentBody =>{
+ return check_type(c,MetaContentBody);
}
-export interface Content extends ContentContent{
+export interface Document extends DocumentBody{
readonly id: number;
};
-export const isContent = (c: any):c is Content =>{
+export const isDoc = (c: any):c is Document =>{
if('id' in c && typeof c['id'] === "number"){
const {id, ...rest} = c;
- return isContentContent(rest);
+ return isDocBody(rest);
}
return false;
}
@@ -59,11 +59,11 @@ export type QueryListOption = {
*/
use_offset?:boolean,
/**
- * cursor of contents
+ * cursor of documents
*/
cursor?:number,
/**
- * offset of contents
+ * offset of documents
*/
offset?:number,
/**
@@ -77,41 +77,41 @@ export type QueryListOption = {
content_type?:string
}
-export interface ContentAccessor{
+export interface DocumentAccessor{
/**
* find list by option
- * @returns content list
+ * @returns documents list
*/
- findList: (option?:QueryListOption)=>Promise,
+ findList: (option?:QueryListOption)=>Promise,
/**
- * @returns content if exist, otherwise undefined
+ * @returns document if exist, otherwise undefined
*/
- findById: (id:number,tagload?:boolean)=> Promise,
+ findById: (id:number,tagload?:boolean)=> Promise,
/**
*
*/
- findListByBasePath:(basepath: string)=>Promise;
+ findListByBasePath:(basepath: string)=>Promise;
/**
- * update content except tag.
+ * update document except tag.
*/
- update:(c:Partial & { id:number })=>Promise;
+ update:(c:Partial & { id:number })=>Promise;
/**
- * add content
+ * add document
*/
- add:(c:ContentContent)=>Promise;
+ add:(c:DocumentBody)=>Promise;
/**
- * delete content
+ * delete document
* @returns if it exists, return true.
*/
del:(id:number)=>Promise;
/**
- * @param c Valid Content
+ * @param c Valid Document
* @param tagname tag name to add
* @returns if success, return true
*/
- addTag:(c:Content,tag_name:string)=>Promise;
+ addTag:(c:Document,tag_name:string)=>Promise;
/**
* @returns if success, return true
*/
- delTag:(c:Content,tag_name:string)=>Promise;
+ delTag:(c:Document,tag_name:string)=>Promise;
};
\ No newline at end of file
diff --git a/src/model/mod.ts b/src/model/mod.ts
index c36784f..747d980 100644
--- a/src/model/mod.ts
+++ b/src/model/mod.ts
@@ -1,3 +1,3 @@
-export * from './contents';
+export * from './doc';
export * from './tag';
export * from './user';
\ No newline at end of file
diff --git a/src/route/all.ts b/src/route/all.ts
index 1237c03..9591cec 100644
--- a/src/route/all.ts
+++ b/src/route/all.ts
@@ -14,7 +14,7 @@ const all_middleware = (cont: string|undefined, restarg: string|undefined)=>asyn
ctx.status = 404;
return;
}
- if(ctx.state.content.type != cont){
+ if(ctx.state.location.type != cont){
console.error("not matched")
ctx.status = 404;
return;
@@ -24,8 +24,7 @@ const all_middleware = (cont: string|undefined, restarg: string|undefined)=>asyn
ctx.status = 404;
return;
}
- const rest = "/"+(restarg as string|undefined || "");
-
+ const rest = "/"+(restarg || "");
const result = router.match(rest,"GET");
if(!result.route){
return await next();
diff --git a/src/route/contents.ts b/src/route/contents.ts
index e1f5459..ee370f7 100644
--- a/src/route/contents.ts
+++ b/src/route/contents.ts
@@ -1,34 +1,34 @@
import { Context, Next } from 'koa';
import Router from 'koa-router';
-import {Content, ContentAccessor, isContentContent} from './../model/contents';
-import {QueryListOption} from './../model/contents';
+import {Document, DocumentAccessor, isDocBody} from '../model/doc';
+import {QueryListOption} from '../model/doc';
import {ParseQueryNumber, ParseQueryArray, ParseQueryBoolean} from './util'
import {sendError} from './error_handler';
-import { createContentReferrer } from '../content/mod';
import { join } from 'path';
import {AllContentRouter} from './all';
import {createPermissionCheckMiddleware as PerCheck, Permission as Per, AdminOnlyMiddleware as AdminOnly} from '../permission/permission';
+import {ContentLocation} from './context'
-const ContentIDHandler = (controller: ContentAccessor) => async (ctx: Context,next: Next)=>{
+const ContentIDHandler = (controller: DocumentAccessor) => async (ctx: Context,next: Next)=>{
const num = Number.parseInt(ctx.params['num']);
- let content = await controller.findById(num,true);
- if (content == undefined){
- return sendError(404,"content does not exist.");
+ let document = await controller.findById(num,true);
+ if (document == undefined){
+ return sendError(404,"document does not exist.");
}
- ctx.body = content;
+ ctx.body = document;
ctx.type = 'json';
- console.log(content.additional);
+ console.log(document.additional);
};
-const ContentTagIDHandler = (controller: ContentAccessor) => async (ctx: Context,next: Next)=>{
+const ContentTagIDHandler = (controller: DocumentAccessor) => async (ctx: Context,next: Next)=>{
const num = Number.parseInt(ctx.params['num']);
- let content = await controller.findById(num,true);
- if (content == undefined){
- return sendError(404,"content does not exist.");
+ let document = await controller.findById(num,true);
+ if (document == undefined){
+ return sendError(404,"document does not exist.");
}
- ctx.body = content.tags || [];
+ ctx.body = document.tags || [];
ctx.type = 'json';
};
-const ContentQueryHandler = (controller : ContentAccessor) => async (ctx: Context,next: Next)=>{
+const ContentQueryHandler = (controller : DocumentAccessor) => async (ctx: Context,next: Next)=>{
const limit = ParseQueryNumber(ctx.query['limit']);
const cursor = ParseQueryNumber(ctx.query['cursor']);
const word: string|undefined = ctx.query['word'];
@@ -52,35 +52,35 @@ const ContentQueryHandler = (controller : ContentAccessor) => async (ctx: Contex
use_offset: use_offset,
content_type:content_type,
};
- let content = await controller.findList(option);
- ctx.body = content;
+ let document = await controller.findList(option);
+ ctx.body = document;
ctx.type = 'json';
}
-const UpdateContentHandler = (controller : ContentAccessor) => async (ctx: Context, next: Next) => {
+const UpdateContentHandler = (controller : DocumentAccessor) => async (ctx: Context, next: Next) => {
const num = Number.parseInt(ctx.params['num']);
if(ctx.request.type !== 'json'){
- return sendError(400,"update fail. invalid content type: it is not json.");
+ return sendError(400,"update fail. invalid document type: it is not json.");
}
if(typeof ctx.request.body !== "object"){
return sendError(400,"update fail. invalid argument: not");
}
- const content_desc: Partial & {id: number} = {
+ const content_desc: Partial & {id: number} = {
id:num,...ctx.request.body
};
const success = await controller.update(content_desc);
ctx.body = JSON.stringify(success);
ctx.type = 'json';
}
-const CreateContentHandler = (controller : ContentAccessor) => async (ctx: Context, next: Next) => {
+const CreateContentHandler = (controller : DocumentAccessor) => async (ctx: Context, next: Next) => {
const content_desc = ctx.request.body;
- if(!isContentContent(content_desc)){
+ if(!isDocBody(content_desc)){
return sendError(400,"it is not a valid format");
}
const id = await controller.add(content_desc);
ctx.body = JSON.stringify(id);
ctx.type = 'json';
};
-const AddTagHandler = (controller: ContentAccessor)=>async (ctx: Context, next: Next)=>{
+const AddTagHandler = (controller: DocumentAccessor)=>async (ctx: Context, next: Next)=>{
let tag_name = ctx.params['tag'];
const num = Number.parseInt(ctx.params['num']);
if(typeof tag_name === undefined){
@@ -95,7 +95,7 @@ const AddTagHandler = (controller: ContentAccessor)=>async (ctx: Context, next:
ctx.body = JSON.stringify(r);
ctx.type = 'json';
};
-const DelTagHandler = (controller: ContentAccessor)=>async (ctx: Context, next: Next)=>{
+const DelTagHandler = (controller: DocumentAccessor)=>async (ctx: Context, next: Next)=>{
let tag_name = ctx.params['tag'];
const num = Number.parseInt(ctx.params['num']);
if(typeof tag_name === undefined){
@@ -110,24 +110,28 @@ const DelTagHandler = (controller: ContentAccessor)=>async (ctx: Context, next:
ctx.body = JSON.stringify(r);
ctx.type = 'json';
}
-const DeleteContentHandler = (controller : ContentAccessor) => async (ctx: Context, next: Next) => {
+const DeleteContentHandler = (controller : DocumentAccessor) => async (ctx: Context, next: Next) => {
const num = Number.parseInt(ctx.params['num']);
const r = await controller.del(num);
ctx.body = JSON.stringify(r);
ctx.type = 'json';
};
-const ContentHandler = (controller : ContentAccessor) => async (ctx:Context, next:Next) => {
+const ContentHandler = (controller : DocumentAccessor) => async (ctx:Context, next:Next) => {
const num = Number.parseInt(ctx.params['num']);
- let content = await controller.findById(num,true);
- if (content == undefined){
- return sendError(404,"content does not exist.");
+ let document = await controller.findById(num,true);
+ if (document == undefined){
+ return sendError(404,"document does not exist.");
}
- const path = join(content.basepath,content.filename);
- ctx.state['content'] = createContentReferrer(content.content_type,path,content.additional);
+ const path = join(document.basepath,document.filename);
+ ctx.state['location'] = {
+ path:path,
+ type:document.content_type,
+ additional:document.additional,
+ } as ContentLocation;
await next();
};
-export const getContentRouter = (controller: ContentAccessor)=>{
+export const getContentRouter = (controller: DocumentAccessor)=>{
const ret = new Router();
ret.get("/search",PerCheck(Per.QueryContent),ContentQueryHandler(controller));
ret.get("/:num(\\d+)",PerCheck(Per.QueryContent),ContentIDHandler(controller));
diff --git a/src/route/context.ts b/src/route/context.ts
index 9ca6268..2e08aba 100644
--- a/src/route/context.ts
+++ b/src/route/context.ts
@@ -1,5 +1,10 @@
import {ContentReferrer} from '../content/mod';
+export type ContentLocation = {
+ path:string,
+ type:string,
+ additional:object|undefined,
+}
export interface ContentContext{
- content:ContentReferrer
+ location:ContentLocation
}
\ No newline at end of file
diff --git a/src/route/manga.ts b/src/route/manga.ts
index a109c2a..a7972e8 100644
--- a/src/route/manga.ts
+++ b/src/route/manga.ts
@@ -37,14 +37,14 @@ export class MangaRouter extends Router{
constructor(){
super();
this.get("/",async (ctx,next)=>{
- await renderZipImage(ctx,ctx.state.content.path,0);
+ await renderZipImage(ctx,ctx.state.location.path,0);
});
this.get("/:page(\\d+)",async (ctx,next)=>{
const page = Number.parseInt(ctx.params['page']);
- await renderZipImage(ctx,ctx.state.content.path,page);
+ await renderZipImage(ctx,ctx.state.location.path,page);
});
this.get("/thumbnail", async (ctx,next)=>{
- await renderZipImage(ctx,ctx.state.content.path,0);
+ await renderZipImage(ctx,ctx.state.location.path,0);
});
}
}
diff --git a/src/route/video.ts b/src/route/video.ts
index c859384..2ab431e 100644
--- a/src/route/video.ts
+++ b/src/route/video.ts
@@ -57,10 +57,10 @@ export class VideoRouter extends Router{
constructor(){
super();
this.get("/", async (ctx,next)=>{
- await renderVideo(ctx,ctx.state.content.path);
+ await renderVideo(ctx,ctx.state.location.path);
});
this.get("/thumbnail", async (ctx,next)=>{
- await renderVideo(ctx,ctx.state.content.path);
+ await renderVideo(ctx,ctx.state.location.path);
})
}
}
diff --git a/src/server.ts b/src/server.ts
index ea8da2e..fd96956 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -7,7 +7,7 @@ import {Watcher} from './diff/diff'
import { createReadStream, readFileSync } from 'fs';
import getContentRouter from './route/contents';
-import { createKnexContentsAccessor } from './db/contents';
+import { createKnexContentsAccessor } from './db/doc';
import bodyparser from 'koa-bodyparser';
import {error_handler} from './route/error_handler';
@@ -64,8 +64,8 @@ export async function create_server(){
static_file_server('dist/js/bundle.js.map','text');
const content_router = getContentRouter(createKnexContentsAccessor(db));
- router.use('/content',content_router.routes());
- router.use('/content',content_router.allowedMethods());
+ router.use('/api/doc',content_router.routes());
+ router.use('/api/doc',content_router.allowedMethods());
router.post('/user/login',createLoginMiddleware(db));
router.post('/user/logout',LogoutMiddleware);
diff --git a/src/types/db.d.ts b/src/types/db.d.ts
index 696ec55..5545f6a 100644
--- a/src/types/db.d.ts
+++ b/src/types/db.d.ts
@@ -11,7 +11,7 @@ declare module "knex" {
password_hash: string;
password_salt: string;
};
- contents: {
+ document: {
id: number;
title: string;
content_type: string;
@@ -20,8 +20,8 @@ declare module "knex" {
content_hash?: string;
additional?: string;
};
- content_tag_relation: {
- content_id: number;
+ doc_tag_relation: {
+ doc_id: number;
tag_name: string;
};
permissions: {
diff --git a/webpack.config.js b/webpack.config.js
index 655cb81..32cba80 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -24,7 +24,7 @@ module.exports = ()=>{return {
},
plugins : [
new MiniCssExtractPlugin({
- "filename":'../css/style.css'})
+ "filename":'../css/style.css'}),
],
devtool: 'source-map',
resolve: {