add modified_at
This commit is contained in:
parent
6dd1d4d83a
commit
e889f98530
@ -1,6 +1,11 @@
|
|||||||
import {Knex} from 'knex';
|
import {Knex} from 'knex';
|
||||||
|
|
||||||
export async function up(knex:Knex) {
|
export async function up(knex:Knex) {
|
||||||
|
await knex.schema.createTable("schema_migration",(b)=>{
|
||||||
|
b.string("version");
|
||||||
|
b.boolean("dirty");
|
||||||
|
});
|
||||||
|
|
||||||
await knex.schema.createTable("users",(b)=>{
|
await knex.schema.createTable("users",(b)=>{
|
||||||
b.string("username").primary().comment("user's login id");
|
b.string("username").primary().comment("user's login id");
|
||||||
b.string("password_hash",64).notNullable();
|
b.string("password_hash",64).notNullable();
|
||||||
@ -15,6 +20,7 @@ export async function up(knex:Knex) {
|
|||||||
b.string("content_hash").nullable();
|
b.string("content_hash").nullable();
|
||||||
b.json("additional").nullable();
|
b.json("additional").nullable();
|
||||||
b.integer("created_at").notNullable();
|
b.integer("created_at").notNullable();
|
||||||
|
b.integer("modified_at").notNullable();
|
||||||
b.integer("deleted_at");
|
b.integer("deleted_at");
|
||||||
b.index("content_type","content_type_index");
|
b.index("content_type","content_type_index");
|
||||||
});
|
});
|
||||||
@ -44,10 +50,5 @@ export async function up(knex:Knex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function down(knex:Knex) {
|
export async function down(knex:Knex) {
|
||||||
//throw new Error('Downward migrations are not supported. Restore from backup.');
|
throw new Error('Downward migrations are not supported. Restore from backup.');
|
||||||
await knex.schema.dropTable("users");
|
|
||||||
await knex.schema.dropTable("document");
|
|
||||||
await knex.schema.dropTable("tags");
|
|
||||||
await knex.schema.dropTable("document_tag_relation");
|
|
||||||
await knex.schema.dropTable("permissions");
|
|
||||||
};
|
};
|
||||||
|
8
plan.md
8
plan.md
@ -40,4 +40,10 @@
|
|||||||
- ~~hash~~
|
- ~~hash~~
|
||||||
- search
|
- search
|
||||||
- client tag add and delete
|
- client tag add and delete
|
||||||
- pagination
|
- ~~pagination~~
|
||||||
|
|
||||||
|
add URL Render page 바꾸기
|
||||||
|
add modified_time
|
||||||
|
add support robots.txt
|
||||||
|
|
||||||
|
add vite ssr
|
3319
pnpm-lock.yaml
Normal file
3319
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
1068
src/client/pnpm-lock.yaml
Normal file
1068
src/client/pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
|||||||
import {Context, DefaultState, DefaultContext, Middleware, Next} from 'koa';
|
import {Context, DefaultState, DefaultContext, Middleware, Next} from 'koa';
|
||||||
import Router from 'koa-router';
|
import Router from 'koa-router';
|
||||||
import {createHash} from 'crypto';
|
import {createHash} from 'crypto';
|
||||||
import {promises} from 'fs'
|
import {promises, Stats} from 'fs'
|
||||||
import {extname} from 'path';
|
import {extname} from 'path';
|
||||||
import { DocumentBody } from '../model/mod';
|
import { DocumentBody } from '../model/mod';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@ -15,7 +15,7 @@ export interface ContentFile{
|
|||||||
readonly type: string;
|
readonly type: string;
|
||||||
}
|
}
|
||||||
export type ContentConstructOption = {
|
export type ContentConstructOption = {
|
||||||
hash: string
|
hash: string,
|
||||||
}
|
}
|
||||||
type ContentFileConstructor = (new (path:string,option?:ContentConstructOption) => ContentFile)&{content_type:string};
|
type ContentFileConstructor = (new (path:string,option?:ContentConstructOption) => ContentFile)&{content_type:string};
|
||||||
export const createDefaultClass = (type:string):ContentFileConstructor=>{
|
export const createDefaultClass = (type:string):ContentFileConstructor=>{
|
||||||
@ -24,13 +24,16 @@ export const createDefaultClass = (type:string):ContentFileConstructor=>{
|
|||||||
//type = type;
|
//type = type;
|
||||||
static content_type = type;
|
static content_type = type;
|
||||||
protected hash: string| undefined;
|
protected hash: string| undefined;
|
||||||
|
protected stat: Stats| undefined;
|
||||||
|
|
||||||
constructor(path:string,option?:ContentConstructOption){
|
constructor(path:string,option?:ContentConstructOption){
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.hash = option?.hash;
|
this.hash = option?.hash;
|
||||||
|
this.stat = undefined;
|
||||||
}
|
}
|
||||||
async createDocumentBody(): Promise<DocumentBody> {
|
async createDocumentBody(): Promise<DocumentBody> {
|
||||||
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,
|
||||||
@ -38,7 +41,8 @@ export const createDefaultClass = (type:string):ContentFileConstructor=>{
|
|||||||
content_type: cons.content_type,
|
content_type: cons.content_type,
|
||||||
filename: base,
|
filename: base,
|
||||||
tags: [],
|
tags: [],
|
||||||
content_hash: this.hash ?? await this.getHash(),
|
content_hash: await this.getHash(),
|
||||||
|
modified_at: await this.getMtime(),
|
||||||
} as DocumentBody;
|
} as DocumentBody;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -47,16 +51,21 @@ export const createDefaultClass = (type:string):ContentFileConstructor=>{
|
|||||||
}
|
}
|
||||||
async getHash():Promise<string>{
|
async getHash():Promise<string>{
|
||||||
if(this.hash !== undefined) return this.hash;
|
if(this.hash !== undefined) return this.hash;
|
||||||
const stat = await promises.stat(this.path);
|
this.stat = await promises.stat(this.path);
|
||||||
const hash = createHash("sha512");
|
const hash = createHash("sha512");
|
||||||
hash.update(extname(this.path));
|
hash.update(extname(this.path));
|
||||||
hash.update(stat.mode.toString());
|
hash.update(this.stat.mode.toString());
|
||||||
//if(this.desc !== undefined)
|
//if(this.desc !== undefined)
|
||||||
// hash.update(JSON.stringify(this.desc));
|
// hash.update(JSON.stringify(this.desc));
|
||||||
hash.update(stat.size.toString());
|
hash.update(this.stat.size.toString());
|
||||||
this.hash = hash.digest("base64");
|
this.hash = hash.digest("base64");
|
||||||
return this.hash;
|
return this.hash;
|
||||||
}
|
}
|
||||||
|
async getMtime():Promise<number>{
|
||||||
|
if(this.stat !== undefined) return this.stat.mtimeMs;
|
||||||
|
await this.getHash();
|
||||||
|
return this.stat!.mtimeMs;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return cons;
|
return cons;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ export interface DocumentBody{
|
|||||||
content_type : string,
|
content_type : string,
|
||||||
basepath : string,
|
basepath : string,
|
||||||
filename : string,
|
filename : string,
|
||||||
|
modified_at : number,
|
||||||
content_hash : string,
|
content_hash : string,
|
||||||
additional : JSONMap,
|
additional : JSONMap,
|
||||||
tags : string[],//eager loading
|
tags : string[],//eager loading
|
||||||
|
@ -16,6 +16,7 @@ import {createInterface as createReadlineInterface} from 'readline';
|
|||||||
import { DocumentAccessor, UserAccessor, TagAccessor } from './model/mod';
|
import { DocumentAccessor, UserAccessor, TagAccessor } from './model/mod';
|
||||||
import { createComicWatcher } from './diff/watcher/comic_watcher';
|
import { createComicWatcher } from './diff/watcher/comic_watcher';
|
||||||
import { getTagRounter } from './route/tags';
|
import { getTagRounter } from './route/tags';
|
||||||
|
|
||||||
class ServerApplication{
|
class ServerApplication{
|
||||||
readonly userController: UserAccessor;
|
readonly userController: UserAccessor;
|
||||||
readonly documentController: DocumentAccessor;
|
readonly documentController: DocumentAccessor;
|
||||||
|
Loading…
Reference in New Issue
Block a user