diff --git a/.gitignore b/.gitignore index 0ce0d91..e3b7355 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ data/** data/ package-lock.json devdb.sqlite3 +build/** +app/** \ No newline at end of file diff --git a/app.ts b/app.ts new file mode 100644 index 0000000..05a8116 --- /dev/null +++ b/app.ts @@ -0,0 +1,86 @@ +import { app, BrowserWindow } from "electron"; +import { get_setting } from "./src/setting"; +import { create_server, start_server } from "./src/server"; + +const loading_html = ` + + +react-sample + + + + +

Loading

+
+ + +`; + +const setting = get_setting(); +if (!setting.cli) { + let window: BrowserWindow | null = null; + + const createWindow = async () => { + window = new BrowserWindow({ + width: 800, + height: 600, + center: true, + useContentSize: true, + }); + await window.loadURL(`data:text/html;base64,`+Buffer.from(loading_html).toString('base64')); + const server = await create_server(); + start_server(server); + await window.loadURL(`http://localhost:${setting.port}`); + window.on("closed", () => { + window = null; + }); + }; + + const isPrimary = app.requestSingleInstanceLock(); + if (!isPrimary) { + app.quit(); //exit window + app.exit(); + } + app.on("second-instance", () => { + if (window != null) { + if (window.isMinimized()) { + window.restore(); + } + window.focus(); + } + }); + app.on("ready", (event, info) => { + createWindow(); + }); + + app.on("window-all-closed", () => { // quit when all windows are closed + if (process.platform != "darwin") app.quit(); // (except leave MacOS app active until Cmd+Q) + }); + + app.on("activate", () => { // re-recreate window when dock icon is clicked and no other windows open + if (window == null) createWindow(); + }); +} else { + (async () => { + const server = await create_server(); + start_server(server); + })(); +} diff --git a/package.json b/package.json index 7c417d3..9ca8a54 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,35 @@ "name": "followed", "version": "1.0.0", "description": "", - "main": "server.ts", + "main": "build/app.js", "scripts": { - "test": "mocha", "build:dev": "webpack --mode development", "build:prod": "webpack --mode production", "build:watch": "webpack --mode development -w", "start": "ts-node src/server.ts", - "check-types": "tsc" + "compile": "tsc", + "compile:watch": "tsc -w", + "app": "electron build/app.js", + "app:build:win64": "electron-builder --win --x64" + }, + "build": { + "asar": false, + "files":[ + "build/**/*", + "node_modules/**/*", + "package.json" + ], + "appId": "com.prelude.ionian.app", + "productName": "Ionian", + "win": { + "target": [ + "zip" + ] + }, + "directories": { + "output": "app/", + "app": "." + } }, "browserslist": { "production": [ @@ -35,8 +56,7 @@ "react": "^17.0.1", "react-dom": "^17.0.1", "react-router-dom": "^5.2.0", - "sqlite3": "^5.0.0", - "ts-node": "^9.1.1" + "sqlite3": "^5.0.0" }, "devDependencies": { "@babel/core": "^7.12.10", @@ -57,8 +77,11 @@ "babel-core": "^6.26.3", "babel-loader": "^8.2.2", "css-loader": "^5.0.1", + "electron": "^11.1.1", + "electron-builder": "^22.9.1", "mini-css-extract-plugin": "^1.3.3", "style-loader": "^2.0.0", + "ts-node": "^9.1.1", "typescript": "^4.1.3", "webpack": "^5.11.0", "webpack-cli": "^4.2.0", diff --git a/settings.json b/settings.json index a34dd05..91736e8 100644 --- a/settings.json +++ b/settings.json @@ -1,8 +1 @@ -{ - "path": [ - "data" - ], - "localmode": true, - "guest": false, - "jwt_secretkey": "itsRandom" -} \ No newline at end of file +{"path":[],"localmode":true,"guest":false,"jwt_secretkey":"itsRandom","port":8080,"mode":"production","cli":true} \ No newline at end of file diff --git a/src/database.ts b/src/database.ts index e2b90c3..388451c 100644 --- a/src/database.ts +++ b/src/database.ts @@ -1,13 +1,11 @@ import { existsSync } from 'fs'; import Knex from 'knex'; import {Knex as KnexConfig} from './config'; +import { get_setting } from './setting'; export async function connectDB(){ - const env = process.env.NODE_ENV || 'development'; const config = KnexConfig.config; - if(env != "production" && env != "development"){ - throw new Error("process unknown value in NODE_ENV: must be either \"development\" or \"production\""); - } + const env = get_setting().mode; const init_need = !existsSync(config[env].connection.filename); const knex = Knex(config[env]); let tries = 0; diff --git a/src/server.ts b/src/server.ts index f0f0467..7cde715 100644 --- a/src/server.ts +++ b/src/server.ts @@ -16,18 +16,21 @@ import {UserMiddleWare,createLoginMiddleware, isAdminFirst, getAdmin, LogoutMidd import {createInterface as createReadlineInterface} from 'readline'; //let Koa = require("koa"); -async function main(){ - let settings = get_setting(); + +export async function create_server(){ + let setting = get_setting(); let db = await connectDB(); - const userAdmin = await getAdmin(db); - if(await isAdminFirst(userAdmin)){ - const rl = createReadlineInterface({input:process.stdin,output:process.stdout}); - rl.setPrompt("put admin password : "); - rl.prompt(); - const pw = await new Promise((res:(data:string)=>void,err)=>{ - rl.on('line',(data)=>res(data)); - }); - userAdmin.reset_password(pw); + if(setting.cli){ + const userAdmin = await getAdmin(db); + if(await isAdminFirst(userAdmin)){ + const rl = createReadlineInterface({input:process.stdin,output:process.stdout}); + rl.setPrompt("put admin password : "); + rl.prompt(); + const pw = await new Promise((res:(data:string)=>void,err)=>{ + rl.on('line',(data)=>res(data)); + }); + userAdmin.reset_password(pw); + } } let app = new Koa(); app.use(bodyparser()); @@ -39,8 +42,8 @@ async function main(){ let router = new Router(); - let watcher = new Watcher(settings.path[0]); - await watcher.setup([]); + //let watcher = new Watcher(setting.path[0]); + //await watcher.setup([]); const serveindex = (url:string)=>{ router.get(url, (ctx)=>{ctx.type = 'html'; ctx.body = index_html;}) } @@ -74,9 +77,11 @@ async function main(){ }); app.use(router.routes()); app.use(router.allowedMethods()); - - console.log("start server"); - app.listen(settings.port,settings.localmode ? "127.0.0.1" : "0.0.0.0"); return app; } -main(); \ No newline at end of file +export const start_server = (server: Koa)=>{ + let setting = get_setting(); + console.log("start server"); + return server.listen(setting.port,setting.localmode ? "127.0.0.1" : "0.0.0.0"); +} +export default {create_server, start_server}; \ No newline at end of file diff --git a/src/setting.ts b/src/setting.ts index 4e946e9..feda49c 100644 --- a/src/setting.ts +++ b/src/setting.ts @@ -1,6 +1,6 @@ import { Settings } from '@material-ui/icons'; import { randomBytes } from 'crypto'; -import { readFileSync, writeFileSync } from 'fs'; +import { existsSync, readFileSync, writeFileSync } from 'fs'; export type Setting = { path: string[], @@ -8,6 +8,8 @@ export type Setting = { guest: boolean, jwt_secretkey: string, port:number, + mode:"development"|"production", + cli:boolean, } const default_setting:Setting = { path:[], @@ -15,6 +17,8 @@ const default_setting:Setting = { guest:false, jwt_secretkey:"itsRandom", port:8080, + mode:"production", + cli:false } let setting: null|Setting = null; @@ -31,16 +35,21 @@ const setEmptyToDefault = (target:any,default_table:Setting)=>{ } export const read_setting_from_file = ()=>{ - let ret = JSON.parse(readFileSync("settings.json",{encoding:"utf8"})) as Setting; + let ret = existsSync("settings.json") ? JSON.parse(readFileSync("settings.json",{encoding:"utf8"})) : {}; const partial_occur = setEmptyToDefault(ret,default_setting); if(partial_occur){ writeFileSync("settings.json",JSON.stringify(ret)); } - return ret; + return ret as Setting; } export function get_setting():Setting{ if(setting === null){ setting = read_setting_from_file(); + const env = process.env.NODE_ENV || 'development'; + if(env != "production" && env != "development"){ + throw new Error("process unknown value in NODE_ENV: must be either \"development\" or \"production\""); + } + setting.mode = env || setting.mode; } return setting; } diff --git a/test.ts b/test.ts deleted file mode 100644 index 6c52fc4..0000000 --- a/test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import StreamZip, { ZipEntry } from 'node-stream-zip'; -import {orderBy}from 'natural-orderby'; -import {readZip,entriesByNaturalOrder} from './src/util/ziputil'; -import {connectDB} from './src/database'; - -import { createKnexUserController } from './src/db/user'; -import { createKnexContentsAccessor} from './src/db/contents'; - -console.log("on"); -async function test_main1(){ - let sz = await readZip("testdata/test_zip.zip"); - let e = entriesByNaturalOrder(sz).filter(v=>v.name.split('.').pop() === "jpg"); - console.log(e[0]); - console.log(e.map(v=>v.name)); -} -async function test_main2(){ - let db = await connectDB(); - let user:{id:number}[] = await db.select('id').from('users'); - console.log(user[0].id); -} - -async function test_main3(){ - let db = await connectDB(); - let user_controller = createKnexUserController(db); - let bs = await user_controller.delUser("sss"); - if(!bs) console.log("doesn't exist") - let retuser = await user_controller.createUser({username:"sss",password:"sss"}); - let user = await user_controller.findUser("sss"); - if(user !== undefined){ - user.add("create"); - console.log(user.username); - } -} -async function test_main4(){ - let db = await connectDB(); - const cntr = createKnexContentsAccessor(db); - await cntr.add({ - title:"aaa", - basepath:"testdata", - content_type:"manga", - filename:"test_zip.zip", - additional:{comment:"aaab"}, - tags:[] - }); - const list = await cntr.findList(); - console.log(list); -} -test_main4(); diff --git a/tsconfig.json b/tsconfig.json index 8773d4d..0867e75 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,7 @@ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ + "outDir": "./build", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ @@ -64,7 +64,8 @@ /* Advanced Options */ "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ }, - "include": ["src","./"] + "include": ["./"], + "exclude": ["src/client","app","seeds"], }