add electron
This commit is contained in:
parent
53aebb365e
commit
05814d8a31
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,3 +8,5 @@ data/**
|
|||||||
data/
|
data/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
devdb.sqlite3
|
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",
|
"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",
|
||||||
|
@ -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"
|
|
||||||
}
|
|
@ -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;
|
||||||
|
@ -16,9 +16,11 @@ 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();
|
||||||
|
if(setting.cli){
|
||||||
const userAdmin = await getAdmin(db);
|
const userAdmin = await getAdmin(db);
|
||||||
if(await isAdminFirst(userAdmin)){
|
if(await isAdminFirst(userAdmin)){
|
||||||
const rl = createReadlineInterface({input:process.stdin,output:process.stdout});
|
const rl = createReadlineInterface({input:process.stdin,output:process.stdout});
|
||||||
@ -29,6 +31,7 @@ async function main(){
|
|||||||
});
|
});
|
||||||
userAdmin.reset_password(pw);
|
userAdmin.reset_password(pw);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let app = new Koa();
|
let app = new Koa();
|
||||||
app.use(bodyparser());
|
app.use(bodyparser());
|
||||||
app.use(error_handler);
|
app.use(error_handler);
|
||||||
@ -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};
|
@ -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
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. */
|
// "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"],
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user