Skip to content

Commit

Permalink
Complete mythbackend setup through web app
Browse files Browse the repository at this point in the history
Complete backend setup through the web app including
database setup when mythbackend is started without a database.
  • Loading branch information
bennettpeter committed May 12, 2023
1 parent 67f2304 commit 9e56d96
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 127 deletions.
17 changes: 12 additions & 5 deletions mythtv/html/assets/i18n/en_US.json
Expand Up @@ -18,7 +18,8 @@
"nosave": "Close without saving",
"unsaved_heading": "Unsaved Changes",
"unsaved_message": "There are unsaved changes.",
"submit": "Submit"
"submit": "Submit",
"instructions": "Instructions"
},
"dashboard": {
"warning": "Please note this is a work in progress and some features are not fully working!"
Expand Down Expand Up @@ -186,7 +187,13 @@
"WOLEnabled_label": "Enable database server wakeup",
"WOLEnabled_desc": "If enabled, the frontend will use database wakeup parameters to reconnect to the database server.",
"WOLCommand_label": "Wake Command or MAC",
"WOLCommand_desc": "The command executed on this machine or server MAC to wake up the database server (eg. sudo /etc/init.d/mysql restart or 32:D2:86:00:17:A8)."
"WOLCommand_desc": "The command executed on this machine or server MAC to wake up the database server (eg. sudo /etc/init.d/mysql restart or 32:D2:86:00:17:A8).",
"sqlinstructions": "If this is a new MythTV setup, create your mysql user id and database. Save the below commands to a file called setup.sql on the backend:",
"runcommand": "Then run this command on the backend:",
"tztables": "If not already done, create timezone tables by running this on the backend:",
"ignoremessages": "Ignore messages that say \"Unable to load 'xxxxx' as time zone. Skipping it.\"",
"savethis": "Save this page.",
"restartbe": "Restart the backend using the button at the top of this page."
},
"testbed": {
"title": "This is a testbed area",
Expand Down Expand Up @@ -338,11 +345,11 @@
"title": "Host Address Backend Setup",
"subtitle": "These settings tell the backend which IP addresse(s) and ports to listen on and whether it is a master backend.",
"backendport_label": "Port",
"backendport_desc": "The port the backend listens on for connections (Unless you have a good reason don't change this).",
"backendport_desc": "The port the backend listens on for connections (Unless you have a good reason don't change this). Default is 6543",
"statusport_label": "Status Port",
"statusport_desc": "The port on which the server will listen on for HTTP requests, including backend status and MythXML requests.",
"statusport_desc": "The port on which the server will listen on for HTTP requests, including backend status and MythXML requests. Default is 6544",
"pin_label": "Security PIN (required)",
"pin_desc": "The PIN code required for a frontend to connect to the backend. Blank prevents all connections; 0000 allows any client.",
"pin_desc": "The PIN code required for a frontend to connect to the backend. Blank prevents all connections; 0000 allows any client. Default is 0000",
"pin_reqd": "Security PIN is required",
"allowconn_label": "Allow Connections from all Subnets",
"allowconn_desc": "Allow this backend to receive connections from any IP address on the internet. <strong>NOT recommended for most users.</strong> Use this only if you have secure IPv4 and IPv6 firewalls.",
Expand Down
2 changes: 0 additions & 2 deletions mythtv/html/backend/src/app/app-routing.module.ts
Expand Up @@ -4,7 +4,6 @@ import { SetupWizardComponent } from './config/setupwizard/setupwizard.component
import { DashboardComponent } from './dashboard/dashboard.component';
import { GuideComponent } from './guide/guide.component';
import { StatusComponent } from './status/status.component';
import { TestbedComponent } from './testbed/testbed.component';
import { SettingsComponent } from './config/settings/general/general-settings.component';
import { CanDeactivateGuardService } from './can-deactivate-guard.service';
import { CaptureCardsComponent } from './config/settings/capture-cards/capture-cards.component';
Expand Down Expand Up @@ -51,7 +50,6 @@ const routes: Routes = [
path: 'settings/system-events', component: SystemEventsComponent,
canDeactivate: [CanDeactivateGuardService]
},
{ path: 'testbed', component: TestbedComponent },
{ path: 'guide', component: GuideComponent },
];

Expand Down
@@ -1,10 +1,9 @@
import { HttpParams } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { DvrService } from 'src/app/services/dvr.service';
import { BackendInfo } from 'src/app/services/interfaces/backend.interface';
import { ScheduleOrProgram } from 'src/app/services/interfaces/program.interface';
import { MythService } from 'src/app/services/myth.service';
import { SetupService } from 'src/app/services/setup.service';
import { SetupWizardService } from 'src/app/services/setupwizard.service';

@Component({
selector: 'app-backend-warning',
Expand All @@ -23,7 +22,7 @@ export class BackendWarningComponent implements OnInit {
busy = false;

constructor(private mythService: MythService, public setupService: SetupService,
private dvrService: DvrService) {
private dvrService: DvrService, private wizardService: SetupWizardService) {
this.getBackendInfo();
this.refreshInfo();
}
Expand Down Expand Up @@ -51,6 +50,13 @@ export class BackendWarningComponent implements OnInit {
.subscribe({
next: data => {
this.setupService.schedulingEnabled = data.BackendInfo.Env.SchedulingEnabled;
this.setupService.isDatabaseIgnored = data.BackendInfo.Env.IsDatabaseIgnored;
this.setupService.DBTimezoneSupport = data.BackendInfo.Env.DBTimezoneSupport;
if (this.setupService.isDatabaseIgnored)
this.wizardService.wizardItems = this.wizardService.dbSetupMenu;
else
this.wizardService.wizardItems = this.wizardService.fullMenu;
this.wizardService.getWizardData();
this.retryCount = 0;
setTimeout(() => this.getUpcoming(), this.delay);
this.delay = 0;
Expand Down
Expand Up @@ -82,7 +82,7 @@ <h2>{{ 'settings.general.title' | translate }}</h2>

<div class="flex justify-content-end">
<p-button class="p-3" label="{{ 'common.back' | translate }}"
(onClick)="router.navigate(['setupwizard/dbsetup'])" icon="pi pi-angle-left"
(onClick)="router.navigate(['setupwizard/selectlanguage'])" icon="pi pi-angle-left"
iconPos="left"></p-button>

<p-button class="p-3" label="{{ 'common.next' | translate }}"
Expand Down
Expand Up @@ -67,25 +67,28 @@
</div>

<div class="field col-12">
<p-checkbox [(ngModel)]="m_wizardData.Database.LocalEnabled" name="LocalEnabled" class="mb-2 w-full label block"
[binary]="true" label="{{ 'setupwizard.LocalEnabled_label' | translate }}">
<p-checkbox [(ngModel)]="m_wizardData.Database.LocalEnabled" name="LocalEnabled"
class="mb-2 w-full label block" [binary]="true"
label="{{ 'setupwizard.LocalEnabled_label' | translate }}">
</p-checkbox>
<small class="block">
{{ 'setupwizard.LocalEnabled_desc' | translate }}
</small>
</div>

<div class="field col-12" *ngIf="m_wizardData.Database.LocalEnabled">
<label for="LocalHostName" class="label block">{{ 'setupwizard.LocalHostName_label' | translate }}</label>
<label for="LocalHostName" class="label block">{{ 'setupwizard.LocalHostName_label' | translate
}}</label>
<input pInputText id="LocalHostName" type="text" aria-describedby="LocalHostName-help"
[(ngModel)]="m_wizardData.Database.LocalHostName" name="LocalHostName" #LocalHostName="ngModel"
class="mb-2 form-control w-full" />
<small id="LocalHostName-help">{{ 'setupwizard.LocalHostName_desc' | translate }}</small>
</div>

<div class="field col-12">
<p-checkbox [(ngModel)]="m_wizardData.Database.WOLEnabled" name="WOLEnabled" class="mb-2 w-full label block"
[binary]="true" label="{{ 'setupwizard.WOLEnabled_label' | translate }}">
<p-checkbox [(ngModel)]="m_wizardData.Database.WOLEnabled" name="WOLEnabled"
class="mb-2 w-full label block" [binary]="true"
label="{{ 'setupwizard.WOLEnabled_label' | translate }}">
</p-checkbox>
<small class="block">
{{ 'setupwizard.WOLEnabled_desc' | translate }}
Expand All @@ -96,8 +99,8 @@
<label for="WOLReconnect" class="label block">
{{ 'settings.vsource.WOLReconnect_label' | translate }}
</label>
<p-inputNumber [(ngModel)]="m_wizardData.Database.WOLReconnect" name="WOLReconnect" id="WOLReconnect"
[min]="0" [max]="60" [step]="1" [useGrouping]="false" [showButtons]="true">
<p-inputNumber [(ngModel)]="m_wizardData.Database.WOLReconnect" name="WOLReconnect"
id="WOLReconnect" [min]="0" [max]="60" [step]="1" [useGrouping]="false" [showButtons]="true">
</p-inputNumber>
<small class="block">
{{ 'settings.vsource.WOLReconnect_desc' | translate }}
Expand All @@ -108,8 +111,8 @@
<label for="WOLRetry" class="label block">
{{ 'settings.vsource.WOLRetry_label' | translate }}
</label>
<p-inputNumber [(ngModel)]="m_wizardData.Database.WOLRetry" name="WOLRetry" id="WOLRetry"
[min]="1" [max]="10" [step]="1" [useGrouping]="false" [showButtons]="true">
<p-inputNumber [(ngModel)]="m_wizardData.Database.WOLRetry" name="WOLRetry" id="WOLRetry" [min]="1"
[max]="10" [step]="1" [useGrouping]="false" [showButtons]="true">
</p-inputNumber>
<small class="block">
{{ 'settings.vsource.WOLRetry_desc' | translate }}
Expand All @@ -124,7 +127,6 @@
<small id="WOLCommand-help">{{ 'setupwizard.WOLCommand_desc' | translate }}</small>
</div>


</div>
<div>
<p-button type="button" class="mr-1 p-button-raised p-button-secondary"
Expand Down Expand Up @@ -173,6 +175,67 @@
</div>
</ng-template>
</div>
<!-- <div *ngIf="connectionFail"> -->
<div *ngIf="setupService.isDatabaseIgnored">
<h2>{{ 'common.instructions' | translate }}</h2>
<div *ngIf="connectionFail">
<p>
{{ 'setupwizard.sqlinstructions' | translate }}
</p>
<table style="white-space: 'pre-line'">
<tr>
<td class="align-items-center bg-blue-100" style="font-family: monospace;">
{{ commandlist }}
</td>
<td class="align-items-center">
<button pButton pRipple type="button" icon="pi pi-copy"
class="p-button-rounded p-button-text" (click)="copyToclipboard(commandlist)"
pTooltip="{{ 'settings.common.clipboard_tooltip' | translate }}"></button>
</td>
</tr>
</table>
<p>{{ 'setupwizard.runcommand' | translate }}</p>
<table>
<tr>
<td class="align-items-center bg-blue-100" style="font-family: monospace;">
{{ mySqlCommand }}
</td>
<td class="align-items-center">
<button pButton pRipple type="button" icon="pi pi-copy"
class="p-button-rounded p-button-text" (click)="copyToclipboard(mySqlCommand)"
pTooltip="{{ 'settings.common.clipboard_tooltip' | translate }}"></button>
</td>
</tr>
</table>
</div>

<!-- <div
*ngIf="connectionFail || m_wizardData.DatabaseStatus.DatabaseStatus.Connected && !this.setupService.DBTimezoneSupport">
<h3>Instructions</h3> -->
<p class=>{{ 'setupwizard.tztables' | translate }}</p>
<table>
<tr>
<td class="align-items-center bg-blue-100" style="font-family: monospace;">
{{ tzCommand }}
</td>
<td class="align-items-center">
<button pButton pRipple type="button" icon="pi pi-copy"
class="p-button-rounded p-button-text" (click)="copyToclipboard(tzCommand)"
pTooltip="{{ 'settings.common.clipboard_tooltip' | translate }}"></button>
</td>
</tr>
</table>
<p>{{ 'setupwizard.ignoremessages' | translate }}
</p>
<p>{{ 'setupwizard.savethis' | translate }}</p>
<p>{{ 'setupwizard.restartbe' | translate }}</p>
<!-- </div>
<div
*ngIf="successCount > 0 && successCount == expectedCount && errorCount == 0 && !databaseForm.dirty && setupService.isDatabaseIgnored">
<h2>Instructions</h2> -->
<!-- <p>{{ 'setupwizard.restartbe' | translate }}</p> -->
</div>


</ng-template>

Expand All @@ -190,20 +253,15 @@

<p-button class="col pr-1" label="{{'common.save' | translate}}" icon="pi pi-save" iconPos="left"
(onClick)="saveForm(true); databaseForm.form.markAsPristine()"
[disabled]="!databaseForm.dirty || setupService.schedulingEnabled"></p-button>

<p-button class="pr-3" label="{{ 'common.back' | translate }}" (onClick)="router.navigate(['setupwizard/selectlanguage'])"
icon="pi pi-angle-left" iconPos="left"></p-button>
[disabled]="(!databaseForm.dirty || setupService.schedulingEnabled) && !setupService.isDatabaseIgnored"></p-button>

<p-button class="pr-3" label="{{ 'common.next' | translate }}" (onClick)="router.navigate(['setupwizard/general'])"
icon="pi pi-angle-right" iconPos="right"
[disabled]="(m_wizardData.Country.Code == '') || (m_wizardData.Language.Code == '')">
<p-button class="pr-3" label="{{ 'common.next' | translate }}"
(onClick)="router.navigate(['setupwizard/selectlanguage'])" icon="pi pi-angle-right" iconPos="right"
[disabled]="setupService.isDatabaseIgnored">
</p-button>

</div>
</ng-template>



</p-card>
</form>
Expand Up @@ -10,6 +10,7 @@ import { TranslateService } from '@ngx-translate/core';
import { NgForm } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { SetupService } from 'src/app/services/setup.service';
import { Clipboard } from '@angular/cdk/clipboard';

@Component({
selector: 'app-dbsetup',
Expand All @@ -27,6 +28,9 @@ export class DbsetupComponent implements OnInit {
errorCount = 0;
expectedCount = 2;
connectionFail = false;
commandlist = '';
mySqlCommand = 'sudo mysql -u root < setup.sql'
tzCommand = 'mysql_tzinfo_to_sql /usr/share/zoneinfo | sudo mysql -u root mysql'

msg_testconnection = 'setupwizard.testConnection';
msg_connectionsuccess = 'setupwizard.connectionsuccess';
Expand All @@ -40,7 +44,8 @@ export class DbsetupComponent implements OnInit {
private wizardService: SetupWizardService,
private translate: TranslateService,
private messageService: MessageService,
public setupService: SetupService) {
public setupService: SetupService,
private clipboard: Clipboard) {
this.translate.get(this.msg_testconnection).subscribe(data => this.msg_testconnection = data);
this.translate.get(this.msg_connectionsuccess).subscribe(data => this.msg_connectionsuccess = data);
this.translate.get(this.msg_connectionfail).subscribe(data => this.msg_connectionfail = data);
Expand All @@ -52,6 +57,10 @@ export class DbsetupComponent implements OnInit {
this.m_wizardData = this.wizardService.getWizardData();
}

copyToclipboard(value: string): void {
this.clipboard.copy(value);
}

saveObserver = {
next: (x: any) => {
if (x.bool) {
Expand Down Expand Up @@ -83,6 +92,7 @@ export class DbsetupComponent implements OnInit {
DBName: this.m_wizardData.Database.Name,
dbPort: this.m_wizardData.Database.Port
}
this.commandlist = '';
this.mythService.TestDBSettings(params).subscribe(result => {
if (result.bool) {
if (doSave) {
Expand All @@ -95,6 +105,11 @@ export class DbsetupComponent implements OnInit {
else {
this.messageService.add({ severity: 'error', life: 5000, summary: this.msg_testconnection, detail: this.msg_connectionfail });
this.connectionFail = true;
this.commandlist =
`CREATE DATABASE IF NOT EXISTS ${this.m_wizardData.Database.Name};\n` +
`ALTER DATABASE ${this.m_wizardData.Database.Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;\n` +
`CREATE USER IF NOT EXISTS '${this.m_wizardData.Database.UserName}'@'localhost' IDENTIFIED WITH mysql_native_password by '${this.m_wizardData.Database.Password}';\n` +
`GRANT ALL ON ${this.m_wizardData.Database.Name}.* TO '${this.m_wizardData.Database.UserName}'@'localhost';`
}
});
}
Expand Down

0 comments on commit 9e56d96

Please sign in to comment.