add electron

This commit is contained in:
monoid 2021-01-10 03:04:30 +09:00
parent 53aebb365e
commit 05814d8a31
9 changed files with 157 additions and 88 deletions

2
.gitignore vendored
View File

@ -8,3 +8,5 @@ data/**
data/ data/
package-lock.json package-lock.json
devdb.sqlite3 devdb.sqlite3
build/**
app/**

86
app.ts Normal file
View File

@ -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 = `<!DOCTYPE html>
<html lang="ko"><head>
<meta charset="UTF-8">
<title>react-sample</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<style>
body { margin-top: 100px; background-color: #3f51b5; color: #fff; text-align:center; }
h1 {
font: 2em 'Roboto', sans-serif;
margin-bottom: 40px;
}
#loading {
display: inline-block;
width: 50px;
height: 50px;
border: 3px solid rgba(255,255,255,.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg);}
}
</style>
<body>
<h1>Loading</h1>
<div id="loading"></div>
</body>
</html>
`;
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);
})();
}

View File

@ -2,14 +2,35 @@
"name": "followed", "name": "followed",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "server.ts", "main": "build/app.js",
"scripts": { "scripts": {
"test": "mocha",
"build:dev": "webpack --mode development", "build:dev": "webpack --mode development",
"build:prod": "webpack --mode production", "build:prod": "webpack --mode production",
"build:watch": "webpack --mode development -w", "build:watch": "webpack --mode development -w",
"start": "ts-node src/server.ts", "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": { "browserslist": {
"production": [ "production": [
@ -35,8 +56,7 @@
"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",
"sqlite3": "^5.0.0", "sqlite3": "^5.0.0"
"ts-node": "^9.1.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.10", "@babel/core": "^7.12.10",
@ -57,8 +77,11 @@
"babel-core": "^6.26.3", "babel-core": "^6.26.3",
"babel-loader": "^8.2.2", "babel-loader": "^8.2.2",
"css-loader": "^5.0.1", "css-loader": "^5.0.1",
"electron": "^11.1.1",
"electron-builder": "^22.9.1",
"mini-css-extract-plugin": "^1.3.3", "mini-css-extract-plugin": "^1.3.3",
"style-loader": "^2.0.0", "style-loader": "^2.0.0",
"ts-node": "^9.1.1",
"typescript": "^4.1.3", "typescript": "^4.1.3",
"webpack": "^5.11.0", "webpack": "^5.11.0",
"webpack-cli": "^4.2.0", "webpack-cli": "^4.2.0",

View File

@ -1,8 +1 @@
{ {"path":[],"localmode":true,"guest":false,"jwt_secretkey":"itsRandom","port":8080,"mode":"production","cli":true}
"path": [
"data"
],
"localmode": true,
"guest": false,
"jwt_secretkey": "itsRandom"
}

View File

@ -1,13 +1,11 @@
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import Knex from 'knex'; import Knex from 'knex';
import {Knex as KnexConfig} from './config'; import {Knex as KnexConfig} from './config';
import { get_setting } from './setting';
export async function connectDB(){ export async function connectDB(){
const env = process.env.NODE_ENV || 'development';
const config = KnexConfig.config; const config = KnexConfig.config;
if(env != "production" && env != "development"){ const env = get_setting().mode;
throw new Error("process unknown value in NODE_ENV: must be either \"development\" or \"production\"");
}
const init_need = !existsSync(config[env].connection.filename); const init_need = !existsSync(config[env].connection.filename);
const knex = Knex(config[env]); const knex = Knex(config[env]);
let tries = 0; let tries = 0;

View File

@ -16,18 +16,21 @@ import {UserMiddleWare,createLoginMiddleware, isAdminFirst, getAdmin, LogoutMidd
import {createInterface as createReadlineInterface} from 'readline'; import {createInterface as createReadlineInterface} from 'readline';
//let Koa = require("koa"); //let Koa = require("koa");
async function main(){
let settings = get_setting(); export async function create_server(){
let setting = get_setting();
let db = await connectDB(); let db = await connectDB();
const userAdmin = await getAdmin(db); if(setting.cli){
if(await isAdminFirst(userAdmin)){ const userAdmin = await getAdmin(db);
const rl = createReadlineInterface({input:process.stdin,output:process.stdout}); if(await isAdminFirst(userAdmin)){
rl.setPrompt("put admin password : "); const rl = createReadlineInterface({input:process.stdin,output:process.stdout});
rl.prompt(); rl.setPrompt("put admin password : ");
const pw = await new Promise((res:(data:string)=>void,err)=>{ rl.prompt();
rl.on('line',(data)=>res(data)); const pw = await new Promise((res:(data:string)=>void,err)=>{
}); rl.on('line',(data)=>res(data));
userAdmin.reset_password(pw); });
userAdmin.reset_password(pw);
}
} }
let app = new Koa(); let app = new Koa();
app.use(bodyparser()); app.use(bodyparser());
@ -39,8 +42,8 @@ async function main(){
let router = new Router(); let router = new Router();
let watcher = new Watcher(settings.path[0]); //let watcher = new Watcher(setting.path[0]);
await watcher.setup([]); //await watcher.setup([]);
const serveindex = (url:string)=>{ const serveindex = (url:string)=>{
router.get(url, (ctx)=>{ctx.type = 'html'; ctx.body = index_html;}) 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.routes());
app.use(router.allowedMethods()); app.use(router.allowedMethods());
console.log("start server");
app.listen(settings.port,settings.localmode ? "127.0.0.1" : "0.0.0.0");
return app; return app;
} }
main(); 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};

View File

@ -1,6 +1,6 @@
import { Settings } from '@material-ui/icons'; import { Settings } from '@material-ui/icons';
import { randomBytes } from 'crypto'; import { randomBytes } from 'crypto';
import { readFileSync, writeFileSync } from 'fs'; import { existsSync, readFileSync, writeFileSync } from 'fs';
export type Setting = { export type Setting = {
path: string[], path: string[],
@ -8,6 +8,8 @@ export type Setting = {
guest: boolean, guest: boolean,
jwt_secretkey: string, jwt_secretkey: string,
port:number, port:number,
mode:"development"|"production",
cli:boolean,
} }
const default_setting:Setting = { const default_setting:Setting = {
path:[], path:[],
@ -15,6 +17,8 @@ const default_setting:Setting = {
guest:false, guest:false,
jwt_secretkey:"itsRandom", jwt_secretkey:"itsRandom",
port:8080, port:8080,
mode:"production",
cli:false
} }
let setting: null|Setting = null; let setting: null|Setting = null;
@ -31,16 +35,21 @@ const setEmptyToDefault = (target:any,default_table:Setting)=>{
} }
export const read_setting_from_file = ()=>{ 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); const partial_occur = setEmptyToDefault(ret,default_setting);
if(partial_occur){ if(partial_occur){
writeFileSync("settings.json",JSON.stringify(ret)); writeFileSync("settings.json",JSON.stringify(ret));
} }
return ret; return ret as Setting;
} }
export function get_setting():Setting{ export function get_setting():Setting{
if(setting === null){ if(setting === null){
setting = read_setting_from_file(); 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; return setting;
} }

48
test.ts
View File

@ -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();

View File

@ -14,7 +14,7 @@
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single 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. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */ // "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
@ -64,7 +64,8 @@
/* Advanced Options */ /* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */ "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"],
} }