Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto start and stop Postgres on application start and close, respectively. #29

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { app, BrowserWindow, screen } from 'electron';
import { spawnSync } from 'child_process';
import * as path from 'path';
import * as url from 'url';
import * as windowStateKeeper from 'electron-window-state';
Expand All @@ -11,6 +12,27 @@ const args = process.argv.slice(1),
app.commandLine.appendSwitch('ignore-certificate-errors', 'true');
app.commandLine.appendSwitch('allow-insecure-localhost', 'true')

// Set env vars for Postgres
process.env.PGDATABASE = 'seed';
process.env.PGUSER = 'seeduser';
process.env.PGPASSWORD = 'password';
process.env.PGPORT = '5442';
let basePath;
if (process.platform === 'win32') {
basePath = path.resolve(process.env.ProgramData);
} else if (process.platform === 'darwin') {
basePath = path.resolve(`${process.env.HOME}/Library/Application Support/`);
}

process.env.PG_EXECS_DIR = path.resolve(
app.getAppPath(),
app.isPackaged ? '../pg12/bin' : './resources/pg12/bin'
);
const envName = app.isPackaged ? 'PROD' : 'DEV';
process.env.PGDATA = path.join(basePath, 'SEED-Platform', envName, 'pg12');



function createWindow(): BrowserWindow {

const electronScreen = screen;
Expand Down Expand Up @@ -68,6 +90,9 @@ function createWindow(): BrowserWindow {

// Emitted when the window is closed.
win.on('closed', () => {
// Stop postgres
spawnSync(path.resolve(process.env.PG_EXECS_DIR, 'pg_ctl'), ['stop', '--wait']);

// Dereference the window object, usually you would store window
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
Expand All @@ -88,9 +113,11 @@ try {
app.on('window-all-closed', () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}

// But since users can't open a new window right now, this feature is disabled.
// if (process.platform !== 'darwin') {
// app.quit();
// }
});

app.on('activate', () => {
Expand Down
21 changes: 0 additions & 21 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,5 @@ export class AppComponent {
) {
this.translate.setDefaultLang('en');
console.log('AppConfig', AppConfig);

if (electronService.isElectron) {
process.env.PGDATABASE = 'seed';
process.env.PGUSER = 'seeduser';
process.env.PGPASSWORD = 'password';
process.env.PGPORT = '5442';

let basePath;
if (process.platform === 'win32') {
basePath = electronService.path.resolve(process.env.ProgramData);
} else if (process.platform === 'darwin') {
basePath = electronService.path.resolve(`${process.env.HOME}/Library/Application Support/`);
}
process.env.PGDATA = electronService.path.join(basePath, 'SEED-Platform', AppConfig.environment, 'pg12');

// console.log('process.env', process.env);
// console.log('Run in electron');
// console.log('Electron ipcRenderer', this.electronService.ipcRenderer);
} else {
// console.log('Run in browser');
}
}
}
48 changes: 11 additions & 37 deletions src/app/core/services/postgres/postgres.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { ElectronService } from '../electron/electron.service';
providedIn: 'root'
})
export class PostgresService {
private readonly _postgresDir: string;
private _initializedSubject = new BehaviorSubject(false);
readonly initialized$ = this._initializedSubject.asObservable().pipe(distinctUntilChanged());
private _runningSubject = new BehaviorSubject(false);
Expand All @@ -35,10 +34,6 @@ export class PostgresService {
private electronService: ElectronService,
private ngZone: NgZone
) {
this._postgresDir = electronService.path.resolve(
electronService.remote.app.getAppPath(),
AppConfig.environment === 'PROD' ? '../pg12/bin' : './resources/pg12/bin'
);
this._status().finally(async () => {
const running = this._runningSubject.value;
const initialized = this._initializedSubject.value;
Expand All @@ -56,6 +51,9 @@ export class PostgresService {
this.running$.subscribe(async (running) => {
if (running) {
await this._checkMigrations();
} else {
await this.startDb();
await this._checkMigrations();
}
});
});
Expand All @@ -65,7 +63,7 @@ export class PostgresService {
return new Promise((resolve, reject) => {
let stdout = '';
let stderr = '';
const child = this.electronService.childProcess.spawn(this._resolve('psql'), ['--version'], {cwd: this._postgresDir});
const child = this.electronService.childProcess.spawn(this._resolve('psql'), ['--version'], {cwd: process.env.PG_EXECS_DIR});
child.stdout.on('data', (data: string) => stdout += `${data}`);
child.stderr.on('data', (data: string) => stderr += `${data}`);
child.on('error', error => {
Expand All @@ -92,7 +90,7 @@ export class PostgresService {
}

console.log('Initializing DB...');
const child = this.electronService.childProcess.spawn(this._resolve('initdb'), initOptions, {cwd: this._postgresDir});
const child = this.electronService.childProcess.spawn(this._resolve('initdb'), initOptions, {cwd: process.env.PG_EXECS_DIR});

child.stdout.on('data', (data: string) => this.ngZone.run(() => {
data = data.toString().trim();
Expand Down Expand Up @@ -121,7 +119,7 @@ export class PostgresService {
private _createDb(): Promise<void> {
return new Promise((resolve, reject) => {
console.log('Creating DB...');
const child = this.electronService.childProcess.spawn(this._resolve('createdb'), {cwd: this._postgresDir});
const child = this.electronService.childProcess.spawn(this._resolve('createdb'), {cwd: process.env.PG_EXECS_DIR});

child.stdout.on('data', (data: string) => this.ngZone.run(() => {
data = data.toString().trim();
Expand All @@ -146,7 +144,7 @@ export class PostgresService {
private _psql(query: string): Promise<void> {
return new Promise((resolve, reject) => {
console.log('psql', query);
const child = this.electronService.childProcess.spawn(this._resolve('psql'), ['-c', query], {cwd: this._postgresDir});
const child = this.electronService.childProcess.spawn(this._resolve('psql'), ['-c', query], {cwd: process.env.PG_EXECS_DIR});

child.stdout.on('data', (data: string) => this.ngZone.run(() => {
data = data.toString().trim();
Expand All @@ -172,7 +170,7 @@ export class PostgresService {
returnPsql(query: string): Promise<string> {
return new Promise((resolve, reject) => {
console.log('psql', query);
const child = this.electronService.childProcess.spawn(this._resolve('psql'), ['-c', query], {cwd: this._postgresDir});
const child = this.electronService.childProcess.spawn(this._resolve('psql'), ['-c', query], {cwd: process.env.PG_EXECS_DIR});

child.stdout.on('data', (data: string) => this.ngZone.run(() => {
data = data.toString().trim();
Expand Down Expand Up @@ -200,7 +198,7 @@ export class PostgresService {
startDb(): Promise<void> {
return new Promise((resolve, reject) => {
console.log('Starting DB...');
const child = this.electronService.childProcess.spawn(this._resolve('pg_ctl'), ['start', '--wait'], {cwd: this._postgresDir});
const child = this.electronService.childProcess.spawn(this._resolve('pg_ctl'), ['start', '--wait'], {cwd: process.env.PG_EXECS_DIR});

child.stdout.on('data', (data: string) => this.ngZone.run(() => {
data = data.toString().trim();
Expand Down Expand Up @@ -237,34 +235,10 @@ export class PostgresService {
})
}

stopDb(): void {
console.log('Stopping DB...');
const child = this.electronService.childProcess.spawn(this._resolve('pg_ctl'), ['stop', '--wait'], {cwd: this._postgresDir});

child.stdout.on('data', (data: string) => this.ngZone.run(() => {
data = data.toString().trim();
console.log(`stopDb stdout: '${data}'`);
if (data.endsWith('server stopped')) {
this._runningSubject.next(false);
}
this._kill(child.pid);
}));
child.stderr.on('data', (data: string) => {
data = data.toString().trim();
console.error(`stopDb stderr: '${data}'`);
if (data.endsWith('Is server running?')) {
this._runningSubject.next(false);
}
});
child.on('close', (code) => {
console.log(`stopDb exited with code ${code}`);
});
}

// Resolve with boolean if running/not-running, reject with exit code if failure
private _status(): Promise<boolean> {
return new Promise((resolve, reject) => {
const child = this.electronService.childProcess.spawn(this._resolve('pg_ctl'), ['status'], {cwd: this._postgresDir});
const child = this.electronService.childProcess.spawn(this._resolve('pg_ctl'), ['status'], {cwd: process.env.PG_EXECS_DIR});
child.stdout.on('data', (data: string) => this.ngZone.run(() => {
data = data.toString().trim();
console.log(`status stdout: '${data}'`);
Expand Down Expand Up @@ -300,7 +274,7 @@ export class PostgresService {
}

private _resolve(executable: string): string {
return this.electronService.path.resolve(this._postgresDir, executable);
return this.electronService.path.resolve(process.env.PG_EXECS_DIR, executable);
}

// Manually kill Windows processes
Expand Down
32 changes: 14 additions & 18 deletions src/app/home/home.component.html
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
<div class="container" *ngIf="{initialized: (postgresService.initialized$ | async) || false, running: (postgresService.running$ | async) || false, pending: (postgresService.pending$ | async) || false} as status">
<h1 class="title">Environment: {{ env }}</h1>
<h1 class="title">PSQL Version: {{ postgresVersion }}</h1>
<h1 class="title">Running: {{ status.running }}</h1>

<div fxLayoutGap="16px">
<button mat-raised-button (click)="postgresService.initDb()" [disabled]="status.initialized || status.pending || status.running">
<mat-progress-spinner *ngIf="status.pending" class="spinner" diameter="20" mode="indeterminate" style="display: inline-flex"></mat-progress-spinner>
<span>Init DB</span>
</button>
<button mat-raised-button (click)="postgresService.startDb()" [disabled]="!status.initialized || status.pending || status.running">Start DB</button>
<button mat-raised-button (click)="postgresService.stopDb(); clearQueryResult()" [disabled]="!status.initialized || status.pending || !status.running">Stop DB</button>
<button mat-raised-button (click)="returnPsql()" [disabled]="!status.initialized || status.pending || !status.running">Query PostGIS version</button>
<button mat-raised-button (click)="testButton()" [disabled]="!status.initialized || status.pending || !status.running">TestButton</button>
<div class="container" *ngIf="{initialized: (postgresService.initialized$ | async) || false, running: (postgresService.running$ | async) || false, pending: (postgresService.pending$ | async) || false} as status" >
<div *ngIf="status.pending" class="spinner-container">
<div>Setting up Seedling...</div>
<mat-progress-spinner diameter="40" mode="indeterminate"></mat-progress-spinner>
</div>

<textarea disabled>{{ latestQueryResult }}</textarea>

<app-properties-upload></app-properties-upload>
<app-taxlots-upload></app-taxlots-upload>
<div [ngStyle]="{'opacity': status.pending ? '0.3' : '1'}">
<div>
<h1>Upload Data</h1>
<app-properties-upload></app-properties-upload>
<app-taxlots-upload></app-taxlots-upload>
</div>

<h4 class="title">PSQL Version: {{ postgresVersion }}</h4>
<h4 class="title">Database Running: {{ status.running }}</h4>
</div>
</div>
19 changes: 17 additions & 2 deletions src/app/home/home.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,24 @@
padding: 16px;
}

.spinner {
.spinner-container {
width: max-content;
height: max-content;

position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;

display: inline-flex;
margin-right: 16px;
align-items: center;

div {
margin-right: 5px;
}

}

textarea {
Expand Down
Loading