add electron
This commit is contained in:
parent
53aebb365e
commit
05814d8a31
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,3 +8,5 @@ data/**
|
||||
data/
|
||||
package-lock.json
|
||||
devdb.sqlite3
|
||||
build/**
|
||||
app/**
|
86
app.ts
Normal file
86
app.ts
Normal 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);
|
||||
})();
|
||||
}
|
33
package.json
33
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",
|
||||
|
@ -1,8 +1 @@
|
||||
{
|
||||
"path": [
|
||||
"data"
|
||||
],
|
||||
"localmode": true,
|
||||
"guest": false,
|
||||
"jwt_secretkey": "itsRandom"
|
||||
}
|
||||
{"path":[],"localmode":true,"guest":false,"jwt_secretkey":"itsRandom","port":8080,"mode":"production","cli":true}
|
@ -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;
|
||||
|
@ -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();
|
||||
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};
|
@ -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;
|
||||
}
|
||||
|
48
test.ts
48
test.ts
@ -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();
|
@ -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"],
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user