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"],
}