add commitall api and button
This commit is contained in:
		
							parent
							
								
									e7906dd889
								
							
						
					
					
						commit
						902c845e8a
					
				
					 15 changed files with 165 additions and 68 deletions
				
			
		
							
								
								
									
										1
									
								
								app.ts
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								app.ts
									
										
									
									
									
								
							| 
						 | 
					@ -5,6 +5,7 @@ import { getAdminAccessTokenValue,getAdminRefreshTokenValue, accessTokenName, re
 | 
				
			||||||
import { join } from "path";
 | 
					import { join } from "path";
 | 
				
			||||||
import { ipcMain } from 'electron';
 | 
					import { ipcMain } from 'electron';
 | 
				
			||||||
import { UserAccessor } from "./src/model/mod";
 | 
					import { UserAccessor } from "./src/model/mod";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function registerChannel(cntr: UserAccessor){
 | 
					function registerChannel(cntr: UserAccessor){
 | 
				
			||||||
  ipcMain.handle('reset_password', async(event,username:string,password:string)=>{
 | 
					  ipcMain.handle('reset_password', async(event,username:string,password:string)=>{
 | 
				
			||||||
    const user = await cntr.findUser(username);
 | 
					    const user = await cntr.findUser(username);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import Knex from 'knex';
 | 
					import {Knex} from 'knex';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function up(knex:Knex) {
 | 
					export async function up(knex:Knex) {
 | 
				
			||||||
    await knex.schema.createTable("users",(b)=>{
 | 
					    await knex.schema.createTable("users",(b)=>{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										13
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								package.json
									
										
									
									
									
								
							| 
						 | 
					@ -25,7 +25,10 @@
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        "from": "dist/",
 | 
					        "from": "dist/",
 | 
				
			||||||
        "to": "dist/",
 | 
					        "to": "dist/",
 | 
				
			||||||
        "filter":["**/*","!**/*.map"]
 | 
					        "filter": [
 | 
				
			||||||
 | 
					          "**/*",
 | 
				
			||||||
 | 
					          "!**/*.map"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "index.html"
 | 
					      "index.html"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
| 
						 | 
					@ -49,24 +52,26 @@
 | 
				
			||||||
  "author": "",
 | 
					  "author": "",
 | 
				
			||||||
  "license": "ISC",
 | 
					  "license": "ISC",
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "@louislam/sqlite3": "^6.0.0",
 | 
				
			||||||
    "chokidar": "^3.5.1",
 | 
					    "chokidar": "^3.5.1",
 | 
				
			||||||
    "jsonschema": "^1.4.0",
 | 
					    "jsonschema": "^1.4.0",
 | 
				
			||||||
    "jsonwebtoken": "^8.5.1",
 | 
					    "jsonwebtoken": "^8.5.1",
 | 
				
			||||||
    "knex": "^0.21.16",
 | 
					    "knex": "^0.95.11",
 | 
				
			||||||
    "koa": "^2.13.1",
 | 
					    "koa": "^2.13.1",
 | 
				
			||||||
    "koa-bodyparser": "^4.3.0",
 | 
					    "koa-bodyparser": "^4.3.0",
 | 
				
			||||||
    "koa-router": "^10.0.0",
 | 
					    "koa-router": "^10.0.0",
 | 
				
			||||||
    "natural-orderby": "^2.0.3",
 | 
					    "natural-orderby": "^2.0.3",
 | 
				
			||||||
    "node-stream-zip": "^1.12.0",
 | 
					    "node-stream-zip": "^1.12.0",
 | 
				
			||||||
    "sqlite3": "^5.0.1"
 | 
					    "sqlite3": "^5.0.2",
 | 
				
			||||||
 | 
					    "tiny-async-pool": "^1.2.0"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@types/jsonwebtoken": "^8.5.0",
 | 
					    "@types/jsonwebtoken": "^8.5.0",
 | 
				
			||||||
    "@types/knex": "^0.16.1",
 | 
					 | 
				
			||||||
    "@types/koa": "^2.11.6",
 | 
					    "@types/koa": "^2.11.6",
 | 
				
			||||||
    "@types/koa-bodyparser": "^4.3.0",
 | 
					    "@types/koa-bodyparser": "^4.3.0",
 | 
				
			||||||
    "@types/koa-router": "^7.4.1",
 | 
					    "@types/koa-router": "^7.4.1",
 | 
				
			||||||
    "@types/node": "^14.14.22",
 | 
					    "@types/node": "^14.14.22",
 | 
				
			||||||
 | 
					    "@types/tiny-async-pool": "^1.0.0",
 | 
				
			||||||
    "electron": "^11.2.0",
 | 
					    "electron": "^11.2.0",
 | 
				
			||||||
    "electron-builder": "^22.9.1",
 | 
					    "electron-builder": "^22.9.1",
 | 
				
			||||||
    "eslint-plugin-node": "^11.1.0",
 | 
					    "eslint-plugin-node": "^11.1.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@material-ui/core": "^4.11.2",
 | 
					    "@material-ui/core": "^4.11.2",
 | 
				
			||||||
    "@material-ui/icons": "^4.11.2",
 | 
					    "@material-ui/icons": "^4.11.2",
 | 
				
			||||||
 | 
					    "@mui/material": "^5.0.3",
 | 
				
			||||||
    "react": "^17.0.1",
 | 
					    "react": "^17.0.1",
 | 
				
			||||||
    "react-dom": "^17.0.1",
 | 
					    "react-dom": "^17.0.1",
 | 
				
			||||||
    "react-router-dom": "^5.2.0"
 | 
					    "react-router-dom": "^5.2.0"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ import React, { useContext, useEffect, useState } from 'react';
 | 
				
			||||||
import { CommonMenuList, Headline } from "../component/mod";
 | 
					import { CommonMenuList, Headline } from "../component/mod";
 | 
				
			||||||
import { UserContext } from "../state";
 | 
					import { UserContext } from "../state";
 | 
				
			||||||
import { Box, Grid, Paper, Typography,Button, makeStyles, Theme } from "@material-ui/core";
 | 
					import { Box, Grid, Paper, Typography,Button, makeStyles, Theme } from "@material-ui/core";
 | 
				
			||||||
 | 
					import {Stack} from '@mui/material';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const useStyles = makeStyles((theme:Theme)=>({
 | 
					const useStyles = makeStyles((theme:Theme)=>({
 | 
				
			||||||
    paper:{
 | 
					    paper:{
 | 
				
			||||||
| 
						 | 
					@ -26,17 +27,25 @@ type FileDifference = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function TypeDifference(prop:{
 | 
					function TypeDifference(prop:{
 | 
				
			||||||
    content:FileDifference,
 | 
					    content:FileDifference,
 | 
				
			||||||
    onCommit:(v:{type:string,path:string})=>void
 | 
					    onCommit:(v:{type:string,path:string})=>void,
 | 
				
			||||||
 | 
					    onCommitAll:(type:string) => void
 | 
				
			||||||
}){
 | 
					}){
 | 
				
			||||||
    const classes = useStyles();
 | 
					    const classes = useStyles();
 | 
				
			||||||
    const x = prop.content;
 | 
					    const x = prop.content;
 | 
				
			||||||
    const [button_disable,set_disable] = useState(false);
 | 
					    const [button_disable,set_disable] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (<Paper className={classes.paper}>
 | 
					    return (<Paper className={classes.paper}>
 | 
				
			||||||
                    <Typography variant='h3' className={classes.contentTitle}>{x.type}</Typography>
 | 
					                    <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=>(
 | 
					                        {x.value.map(y=>(
 | 
				
			||||||
                            <Box className={classes.commitable} key={y.path}>
 | 
					                            <Box className={classes.commitable} key={y.path}>
 | 
				
			||||||
                            <Button onClick={()=>{
 | 
					                            <Button variant="contained" onClick={()=>{
 | 
				
			||||||
                                set_disable(true);
 | 
					                                set_disable(true);
 | 
				
			||||||
                                prop.onCommit(y);
 | 
					                                prop.onCommit(y);
 | 
				
			||||||
                                set_disable(false);
 | 
					                                set_disable(false);
 | 
				
			||||||
| 
						 | 
					@ -76,6 +85,25 @@ export function DifferencePage(){
 | 
				
			||||||
        if(bb.ok){
 | 
					        if(bb.ok){
 | 
				
			||||||
            doLoad();
 | 
					            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(
 | 
					    useEffect(
 | 
				
			||||||
        ()=>{
 | 
					        ()=>{
 | 
				
			||||||
| 
						 | 
					@ -90,7 +118,7 @@ export function DifferencePage(){
 | 
				
			||||||
    return (<Headline menu={menu}>
 | 
					    return (<Headline menu={menu}>
 | 
				
			||||||
        {(ctx.username == "admin") ? (<div>
 | 
					        {(ctx.username == "admin") ? (<div>
 | 
				
			||||||
            {(diffList.map(x=>
 | 
					            {(diffList.map(x=>
 | 
				
			||||||
                <TypeDifference key={x.type} content={x} onCommit={Commit}/>))}
 | 
					                <TypeDifference key={x.type} content={x} onCommit={Commit} onCommitAll={CommitAll}/>))}
 | 
				
			||||||
        </div>)
 | 
					        </div>)
 | 
				
			||||||
        :(<Typography variant='h2'>Not Allowed : please login as an admin</Typography>)
 | 
					        :(<Typography variant='h2'>Not Allowed : please login as an admin</Typography>)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import { Document, DocumentBody, DocumentAccessor, QueryListOption } from '../model/doc';
 | 
					import { Document, DocumentBody, DocumentAccessor, QueryListOption } from '../model/doc';
 | 
				
			||||||
import Knex from 'knex';
 | 
					import {Knex} from 'knex';
 | 
				
			||||||
import {createKnexTagController} from './tag';
 | 
					import {createKnexTagController} from './tag';
 | 
				
			||||||
import { TagAccessor } from '../model/tag';
 | 
					import { TagAccessor } from '../model/tag';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,43 @@ class KnexDocumentAccessor implements DocumentAccessor{
 | 
				
			||||||
        this.knex = knex;
 | 
					        this.knex = knex;
 | 
				
			||||||
        this.tagController = createKnexTagController(knex);
 | 
					        this.tagController = createKnexTagController(knex);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    async addList(content_list: DocumentBody[]):Promise<number[]>{
 | 
				
			||||||
 | 
					        return await this.knex.transaction(async (trx)=>{
 | 
				
			||||||
 | 
					            //add tags
 | 
				
			||||||
 | 
					            const tagCollected = new Set<string>();
 | 
				
			||||||
 | 
					            content_list.map(x=>x.tags).forEach((x)=>{
 | 
				
			||||||
 | 
					                x.forEach(x=>{
 | 
				
			||||||
 | 
					                    tagCollected.add(x);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            const tagCollectPromiseList = [];
 | 
				
			||||||
 | 
					            const tagController = createKnexTagController(trx);
 | 
				
			||||||
 | 
					            for (const it of tagCollected){
 | 
				
			||||||
 | 
					                const p = tagController.addTag({name:it});
 | 
				
			||||||
 | 
					                tagCollectPromiseList.push(p);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            await Promise.all(tagCollectPromiseList);
 | 
				
			||||||
 | 
					            //add for each contents
 | 
				
			||||||
 | 
					            const ret = [];
 | 
				
			||||||
 | 
					            for (const content of content_list) {
 | 
				
			||||||
 | 
					                const {tags,additional, ...rest} = content;
 | 
				
			||||||
 | 
					                const id_lst = await trx.insert({
 | 
				
			||||||
 | 
					                        additional:JSON.stringify(additional),
 | 
				
			||||||
 | 
					                        created_at:Date.now(),
 | 
				
			||||||
 | 
					                        ...rest
 | 
				
			||||||
 | 
					                }).into("document");
 | 
				
			||||||
 | 
					                const id = id_lst[0];
 | 
				
			||||||
 | 
					                if(tags.length > 0){
 | 
				
			||||||
 | 
					                    await trx.insert(tags.map(y=>({
 | 
				
			||||||
 | 
					                        doc_id:id,
 | 
				
			||||||
 | 
					                        tag_name:y
 | 
				
			||||||
 | 
					                    }))).into('doc_tag_relation');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ret.push(id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return ret;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    async add(c: DocumentBody){
 | 
					    async add(c: DocumentBody){
 | 
				
			||||||
        const {tags,additional, ...rest} = c;
 | 
					        const {tags,additional, ...rest} = c;
 | 
				
			||||||
        const id_lst = await this.knex.insert({
 | 
					        const id_lst = await this.knex.insert({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import {Tag, TagAccessor} from '../model/tag';
 | 
					import {Tag, TagAccessor} from '../model/tag';
 | 
				
			||||||
import Knex from 'knex';
 | 
					import {Knex} from 'knex';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DBTags = {
 | 
					type DBTags = {
 | 
				
			||||||
    name: string,
 | 
					    name: string,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import Knex from 'knex';
 | 
					import {Knex} from 'knex';
 | 
				
			||||||
import {IUser,UserCreateInput, UserAccessor, Password} from '../model/user';
 | 
					import {IUser,UserCreateInput, UserAccessor, Password} from '../model/user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PermissionTable = {
 | 
					type PermissionTable = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,12 @@ import {ContentList} from './content_list';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//refactoring needed.
 | 
					//refactoring needed.
 | 
				
			||||||
export class ContentDiffHandler{
 | 
					export class ContentDiffHandler{
 | 
				
			||||||
 | 
					    /** content file list waiting to add */
 | 
				
			||||||
    waiting_list:ContentList;
 | 
					    waiting_list:ContentList;
 | 
				
			||||||
 | 
					    /** deleted contents */
 | 
				
			||||||
    tombstone: Map<string,Document>;//hash, contentfile
 | 
					    tombstone: Map<string,Document>;//hash, contentfile
 | 
				
			||||||
    doc_cntr: DocumentAccessor;
 | 
					    doc_cntr: DocumentAccessor;
 | 
				
			||||||
 | 
					    /** content type of handle */
 | 
				
			||||||
    content_type: string;
 | 
					    content_type: string;
 | 
				
			||||||
    constructor(cntr: DocumentAccessor,content_type:string){
 | 
					    constructor(cntr: DocumentAccessor,content_type:string){
 | 
				
			||||||
        this.waiting_list = new ContentList();
 | 
					        this.waiting_list = new ContentList();
 | 
				
			||||||
| 
						 | 
					@ -30,14 +33,19 @@ export class ContentDiffHandler{
 | 
				
			||||||
    private async OnDeleted(cpath: string){
 | 
					    private async OnDeleted(cpath: string){
 | 
				
			||||||
        const basepath = dirname(cpath);
 | 
					        const basepath = dirname(cpath);
 | 
				
			||||||
        const filename = basename(cpath);
 | 
					        const filename = basename(cpath);
 | 
				
			||||||
        if(this.waiting_list.hasPath(cpath)){
 | 
					        //if it wait to add, delete it from waiting list.
 | 
				
			||||||
            this.waiting_list.deletePath(cpath);
 | 
					        if(this.waiting_list.hasByPath(cpath)){
 | 
				
			||||||
 | 
					            this.waiting_list.deleteByPath(cpath);
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const dbc = await this.doc_cntr.findByPath(basepath,filename);
 | 
					        const dbc = await this.doc_cntr.findByPath(basepath,filename);
 | 
				
			||||||
        if(dbc.length === 0) return; //ignore
 | 
					        //when there is no related content in db, ignore.
 | 
				
			||||||
        if(this.waiting_list.hasHash(dbc[0].content_hash)){
 | 
					        if(dbc.length === 0) return;
 | 
				
			||||||
            //if path changed, update changed path.
 | 
					        // When a path is changed, it takes into account when the
 | 
				
			||||||
 | 
					        // creation event occurs first and the deletion occurs, not
 | 
				
			||||||
 | 
					        // the change event. 
 | 
				
			||||||
 | 
					        if(this.waiting_list.hasByHash(dbc[0].content_hash)){
 | 
				
			||||||
 | 
					            //if a path is changed, update the changed path.
 | 
				
			||||||
            await this.doc_cntr.update({
 | 
					            await this.doc_cntr.update({
 | 
				
			||||||
                id:dbc[0].id,
 | 
					                id:dbc[0].id,
 | 
				
			||||||
                deleted_at: null,
 | 
					                deleted_at: null,
 | 
				
			||||||
| 
						 | 
					@ -46,7 +54,7 @@ export class ContentDiffHandler{
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        //db invalidate
 | 
					        //invalidate db and add it to tombstone.
 | 
				
			||||||
        await this.doc_cntr.update({
 | 
					        await this.doc_cntr.update({
 | 
				
			||||||
            id:dbc[0].id,
 | 
					            id:dbc[0].id,
 | 
				
			||||||
            deleted_at: Date.now(),
 | 
					            deleted_at: Date.now(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,35 +1,25 @@
 | 
				
			||||||
import { ContentFile } from '../content/mod';
 | 
					import { ContentFile } from '../content/mod';
 | 
				
			||||||
import event from 'events';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ContentListEvent{
 | 
					export class ContentList{
 | 
				
			||||||
    'set':(c:ContentFile)=>void,
 | 
					    /** path map */
 | 
				
			||||||
    'delete':(c:ContentFile)=>void,
 | 
					    private cl:Map<string,ContentFile>;
 | 
				
			||||||
}
 | 
					    /** hash map */
 | 
				
			||||||
 | 
					    private hl:Map<string,ContentFile>;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
export class ContentList extends event.EventEmitter{
 | 
					 | 
				
			||||||
    cl:Map<string,ContentFile>;
 | 
					 | 
				
			||||||
    hl:Map<string,ContentFile>;
 | 
					 | 
				
			||||||
    on<U extends keyof ContentListEvent>(event:U,listener:ContentListEvent[U]): this{
 | 
					 | 
				
			||||||
        return super.on(event,listener);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    emit<U extends keyof ContentListEvent>(event:U,...arg:Parameters<ContentListEvent[U]>): boolean{
 | 
					 | 
				
			||||||
        return super.emit(event,...arg);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    constructor(){
 | 
					    constructor(){
 | 
				
			||||||
        super();
 | 
					 | 
				
			||||||
        this.cl = new Map;
 | 
					        this.cl = new Map;
 | 
				
			||||||
        this.hl = new Map;
 | 
					        this.hl = new Map;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    hasHash(s:string){
 | 
					    hasByHash(s:string){
 | 
				
			||||||
        return this.hl.has(s);
 | 
					        return this.hl.has(s);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    hasPath(p:string){
 | 
					    hasByPath(p:string){
 | 
				
			||||||
        return this.cl.has(p);
 | 
					        return this.cl.has(p);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    getHash(s:string){
 | 
					    getByHash(s:string){
 | 
				
			||||||
        return this.hl.get(s)
 | 
					        return this.hl.get(s)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    getPath(p:string){
 | 
					    getByPath(p:string){
 | 
				
			||||||
        return this.cl.get(p);
 | 
					        return this.cl.get(p);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    async set(c:ContentFile){
 | 
					    async set(c:ContentFile){
 | 
				
			||||||
| 
						 | 
					@ -37,25 +27,29 @@ export class ContentList extends event.EventEmitter{
 | 
				
			||||||
        const hash = await c.getHash();
 | 
					        const hash = await c.getHash();
 | 
				
			||||||
        this.cl.set(path,c);
 | 
					        this.cl.set(path,c);
 | 
				
			||||||
        this.hl.set(hash,c);
 | 
					        this.hl.set(hash,c);
 | 
				
			||||||
        this.emit('set',c);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    /** delete content file */
 | 
				
			||||||
    async delete(c:ContentFile){
 | 
					    async delete(c:ContentFile){
 | 
				
			||||||
 | 
					        const hash = await c.getHash();
 | 
				
			||||||
        let r = true;
 | 
					        let r = true;
 | 
				
			||||||
        r = this.cl.delete(c.path) && r;
 | 
					        r = this.cl.delete(c.path) && r;
 | 
				
			||||||
        r = this.hl.delete(await c.getHash()) && r;
 | 
					        r = this.hl.delete(hash) && r;
 | 
				
			||||||
        this.emit('delete',c);
 | 
					 | 
				
			||||||
        return r;
 | 
					        return r;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    async deletePath(p:string){
 | 
					    async deleteByPath(p:string){
 | 
				
			||||||
        const o = this.getPath(p);
 | 
					        const o = this.getByPath(p);
 | 
				
			||||||
        if(o === undefined) return false;
 | 
					        if(o === undefined) return false;
 | 
				
			||||||
        return this.delete(o);
 | 
					        return this.delete(o);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    async deleteHash(s:string){
 | 
					    async deleteByHash(s:string){
 | 
				
			||||||
        const o = this.getHash(s);
 | 
					        const o = this.getByHash(s);
 | 
				
			||||||
        if(o === undefined) return false;
 | 
					        if(o === undefined) return false;
 | 
				
			||||||
        return this.delete(o);
 | 
					        return this.delete(o);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    clear(){
 | 
				
			||||||
 | 
					        this.cl.clear();
 | 
				
			||||||
 | 
					        this.hl.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    getAll(){
 | 
					    getAll(){
 | 
				
			||||||
        return [...this.cl.values()];
 | 
					        return [...this.cl.values()];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
import { DocumentAccessor } from '../model/doc';
 | 
					import { DocumentAccessor } from '../model/doc';
 | 
				
			||||||
import {ContentDiffHandler} from './content_handler';
 | 
					import {ContentDiffHandler} from './content_handler';
 | 
				
			||||||
import { IDiffWatcher } from './watcher';
 | 
					import { IDiffWatcher } from './watcher';
 | 
				
			||||||
 | 
					import asyncPool from 'tiny-async-pool';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//import {join as pathjoin} from 'path';
 | 
					 | 
				
			||||||
export class DiffManager{
 | 
					export class DiffManager{
 | 
				
			||||||
    watching: {[content_type:string]:ContentDiffHandler};
 | 
					    watching: {[content_type:string]:ContentDiffHandler};
 | 
				
			||||||
    doc_cntr: DocumentAccessor;
 | 
					    doc_cntr: DocumentAccessor;
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ export class DiffManager{
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    async commit(type:string,path:string){
 | 
					    async commit(type:string,path:string){
 | 
				
			||||||
        const list = this.watching[type].waiting_list;
 | 
					        const list = this.watching[type].waiting_list;
 | 
				
			||||||
        const c = list.getPath(path);
 | 
					        const c = list.getByPath(path);
 | 
				
			||||||
        if(c===undefined){
 | 
					        if(c===undefined){
 | 
				
			||||||
            throw new Error("path is not exist");
 | 
					            throw new Error("path is not exist");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,14 @@ export class DiffManager{
 | 
				
			||||||
        const id = await this.doc_cntr.add(body);
 | 
					        const id = await this.doc_cntr.add(body);
 | 
				
			||||||
        return id;
 | 
					        return id;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    async commitAll(type:string){
 | 
				
			||||||
 | 
					        const list = this.watching[type].waiting_list;
 | 
				
			||||||
 | 
					        const contentFiles = list.getAll();
 | 
				
			||||||
 | 
					        list.clear();
 | 
				
			||||||
 | 
					        const bodies = await asyncPool(30,contentFiles,async (x)=>await x.createDocumentBody());
 | 
				
			||||||
 | 
					        const ids = await this.doc_cntr.addList(bodies);
 | 
				
			||||||
 | 
					        return ids;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    getAdded(){
 | 
					    getAdded(){
 | 
				
			||||||
        return Object.keys(this.watching).map(x=>({
 | 
					        return Object.keys(this.watching).map(x=>({
 | 
				
			||||||
            type:x,
 | 
					            type:x,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,6 @@ function checkPostAddedBody(body: any): body is PostAddedBody{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const postAdded = (diffmgr:DiffManager) => async (ctx:Router.IRouterContext,next:Koa.Next)=>{
 | 
					export const postAdded = (diffmgr:DiffManager) => async (ctx:Router.IRouterContext,next:Koa.Next)=>{
 | 
				
			||||||
    const reqbody = ctx.request.body;
 | 
					    const reqbody = ctx.request.body;
 | 
				
			||||||
    console.log(reqbody);
 | 
					 | 
				
			||||||
    if(!checkPostAddedBody(reqbody)){
 | 
					    if(!checkPostAddedBody(reqbody)){
 | 
				
			||||||
        sendError(400,"format exception");
 | 
					        sendError(400,"format exception");
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
| 
						 | 
					@ -45,6 +44,27 @@ export const postAdded = (diffmgr:DiffManager) => async (ctx:Router.IRouterConte
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ctx.type = 'json';
 | 
					    ctx.type = 'json';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					export const postAddedAll = (diffmgr: DiffManager) => async (ctx:Router.IRouterContext,next:Koa.Next) => {
 | 
				
			||||||
 | 
					    if (!ctx.is('json')){
 | 
				
			||||||
 | 
					        sendError(400,"format exception");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const reqbody = ctx.request.body as Record<string,unknown>;
 | 
				
			||||||
 | 
					    if(!("type" in reqbody)){
 | 
				
			||||||
 | 
					        sendError(400,"format exception: there is no \"type\"");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const t = reqbody["type"];
 | 
				
			||||||
 | 
					    if(typeof t !== "string"){
 | 
				
			||||||
 | 
					        sendError(400,"format exception: invalid type of \"type\"");
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    await diffmgr.commitAll(t);
 | 
				
			||||||
 | 
					    ctx.body = {
 | 
				
			||||||
 | 
					        ok:true
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    ctx.type = 'json';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
export const getNotWatched = (diffmgr : DiffManager)=> (ctx:Router.IRouterContext,next:Koa.Next)=>{
 | 
					export const getNotWatched = (diffmgr : DiffManager)=> (ctx:Router.IRouterContext,next:Koa.Next)=>{
 | 
				
			||||||
    ctx.body = {
 | 
					    ctx.body = {
 | 
				
			||||||
| 
						 | 
					@ -58,5 +78,6 @@ export function createDiffRouter(diffmgr: DiffManager){
 | 
				
			||||||
    const ret = new Router();
 | 
					    const ret = new Router();
 | 
				
			||||||
    ret.get("/list",AdminOnlyMiddleware,getAdded(diffmgr));
 | 
					    ret.get("/list",AdminOnlyMiddleware,getAdded(diffmgr));
 | 
				
			||||||
    ret.post("/commit",AdminOnlyMiddleware,postAdded(diffmgr));
 | 
					    ret.post("/commit",AdminOnlyMiddleware,postAdded(diffmgr));
 | 
				
			||||||
 | 
					    ret.post("/commitall",AdminOnlyMiddleware,postAddedAll(diffmgr));
 | 
				
			||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -102,6 +102,10 @@ export interface DocumentAccessor{
 | 
				
			||||||
     * add document
 | 
					     * add document
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    add:(c:DocumentBody)=>Promise<number>;
 | 
					    add:(c:DocumentBody)=>Promise<number>;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * add document list
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    addList:(content_list:DocumentBody[]) => Promise<number[]>;
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * delete document
 | 
					     * delete document
 | 
				
			||||||
     * @returns if it exists, return true.
 | 
					     * @returns if it exists, return true.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,15 +71,7 @@ const UpdateContentHandler = (controller : DocumentAccessor) => async (ctx: Cont
 | 
				
			||||||
    ctx.body = JSON.stringify(success);
 | 
					    ctx.body = JSON.stringify(success);
 | 
				
			||||||
    ctx.type = 'json';
 | 
					    ctx.type = 'json';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/*const CreateContentHandler = (controller : DocumentAccessor) => async (ctx: Context, next: Next) => {
 | 
					
 | 
				
			||||||
    const content_desc = ctx.request.body;
 | 
					 | 
				
			||||||
    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: DocumentAccessor)=>async (ctx: Context, next: Next)=>{
 | 
					const AddTagHandler = (controller: DocumentAccessor)=>async (ctx: Context, next: Next)=>{
 | 
				
			||||||
    let tag_name = ctx.params['tag'];
 | 
					    let tag_name = ctx.params['tag'];
 | 
				
			||||||
    const num = Number.parseInt(ctx.params['num']);
 | 
					    const num = Number.parseInt(ctx.params['num']);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4
									
								
								src/types/db.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								src/types/db.d.ts
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
import Knex from "knex";
 | 
					import {Knex} from "knex";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
declare module "knex" {
 | 
					declare module "knex" {
 | 
				
			||||||
  interface Tables {
 | 
					  interface Tables {
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,4 @@ declare module "knex" {
 | 
				
			||||||
      name: string;
 | 
					      name: string;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  namespace Knex {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue