Skip to content

Commit

Permalink
Merge pull request #75 from webosbrew/fix/renew-devmode
Browse files Browse the repository at this point in the history
fixed #72
  • Loading branch information
mariotaku authored Feb 18, 2023
2 parents 7341a13 + 9069c71 commit 8c90ab3
Show file tree
Hide file tree
Showing 12 changed files with 212 additions and 38 deletions.
62 changes: 40 additions & 22 deletions src-tauri/src/plugins/devmode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use tauri::plugin::{Builder, TauriPlugin};
use tauri::regex::Regex;
use tauri::{Runtime, State};

use crate::device_manager::Device;
use crate::device_manager::{Device, DeviceManager};
use crate::error::Error;
use crate::session_manager::SessionManager;

Expand All @@ -23,6 +23,17 @@ struct DevModeSession {
error_msg: Option<String>,
}

#[tauri::command]
async fn token(manager: State<'_, SessionManager>, device: Device) -> Result<String, Error> {
if device.username != "prisoner" {
return Err(Error::Unsupported);
}
if let Some(token) = valid_token(&manager, device).await? {
return Ok(token);
}
return Err(Error::Unsupported);
}

#[tauri::command]
async fn status(
manager: State<'_, SessionManager>,
Expand All @@ -31,6 +42,31 @@ async fn status(
if device.username != "prisoner" {
return Err(Error::Unsupported);
}
if let Some(token) = valid_token(&manager, device).await? {
let url = Url::parse_with_params(
"https://developer.lge.com/secure/CheckDevModeSession.dev",
&[("sessionToken", token.clone())],
)
.expect("should be valid url");
let session: DevModeSession = reqwest::get(url).await?.json().await?;
if session.result == "success" {
return Ok(DevModeStatus {
token: Some(token),
remaining: Some(session.error_msg.unwrap_or(String::from(""))),
});
}
return Ok(DevModeStatus {
token: Some(token),
remaining: None,
});
}
return Ok(DevModeStatus {
token: None,
remaining: None,
});
}

async fn valid_token(manager: &SessionManager, device: Device) -> Result<Option<String>, Error> {
let token = match manager
.exec(device, "cat /var/luna/preferences/devmode_enabled", None)
.await
Expand All @@ -43,32 +79,14 @@ async fn status(
};
let regex = Regex::new("^[0-9a-zA-Z]+$").unwrap();
if !regex.is_match(&token) {
return Ok(DevModeStatus {
token: None,
remaining: None,
});
return Ok(None);
}
let url = Url::parse_with_params(
"https://developer.lge.com/secure/CheckDevModeSession.dev",
&[("sessionToken", token.clone())],
)
.expect("should be valid url");
let session: DevModeSession = reqwest::get(url).await?.json().await?;
if session.result == "success" {
return Ok(DevModeStatus {
token: Some(token),
remaining: Some(session.error_msg.unwrap_or(String::from(""))),
});
}
return Ok(DevModeStatus {
token: Some(token),
remaining: None,
});
return Ok(Some(token));
}

/// Initializes the plugin.
pub fn plugin<R: Runtime>(name: &'static str) -> TauriPlugin<R> {
Builder::new(name)
.invoke_handler(tauri::generate_handler![status,])
.invoke_handler(tauri::generate_handler![status, token])
.build()
}
4 changes: 4 additions & 0 deletions src/app/core/services/dev-mode.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export class DevModeService extends BackendClient {
async status(device: Device): Promise<DevModeStatus> {
return this.invoke('status', {device});
}

async token(device: Device): Promise<string> {
return this.invoke('token', {device});
}
}

export interface DevModeStatus {
Expand Down
40 changes: 29 additions & 11 deletions src/app/core/services/device-manager.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import {BackendClient} from "./backend-client";
import {FileSessionImpl} from "./file.session";
import {HomebrewChannelConfiguration, SystemInfo} from "../../types/luna-apis";
import {basename} from "@tauri-apps/api/path";
import {RemoteLunaService} from "./remote-luna.service";
import {LunaResponseError, RemoteLunaService} from "./remote-luna.service";
import {RemoteCommandService} from "./remote-command.service";
import {Buffer} from "buffer";
import {RemoteFileService} from "./remote-file.service";
import {app} from "@tauri-apps/api";
import {DevModeService} from "./dev-mode.service";

@Injectable({
providedIn: 'root'
Expand All @@ -18,7 +20,8 @@ export class DeviceManagerService extends BackendClient {
private devicesSubject: Subject<Device[]>;
private selectedSubject: Subject<Device | null>;

constructor(zone: NgZone, private cmd: RemoteCommandService, private file: RemoteFileService, private luna: RemoteLunaService) {
constructor(zone: NgZone, private cmd: RemoteCommandService, private file: RemoteFileService,
private luna: RemoteLunaService, private devMode: DevModeService) {
super(zone, 'device-manager');
this.devicesSubject = new BehaviorSubject<Device[]>([]);
this.selectedSubject = new BehaviorSubject<Device | null>(null);
Expand Down Expand Up @@ -69,14 +72,7 @@ export class DeviceManagerService extends BackendClient {
}

async devModeToken(device: Device): Promise<string> {
return await this.file.read(device, '/var/luna/preferences/devmode_enabled', 'utf-8')
.then(()=> 'cf961c5c0c87c79ec42a80762971cb06dccbc1a087c3a31a7e49338881311112')
.then(s => {
if (!s || !s.match(/^[0-9a-zA-Z]+$/)) {
throw new Error('No valid dev mode token');
}
return s;
});
return await this.devMode.token(device);
}

async listCrashReports(device: Device): Promise<CrashReport[]> {
Expand Down Expand Up @@ -110,6 +106,25 @@ export class DeviceManagerService extends BackendClient {
});
}

async takeScreenshot(device: DeviceLike): Promise<string> {
const tmpPath = `/tmp/devman_shot_${Date.now()}.png`
const param: Record<string, any> = {
path: tmpPath,
method: "DISPLAY",
format: "PNG",
width: 1920,
height: 1080
};
await (this.luna.call(device, 'luna://com.webos.service.capture/executeOneShot', param, false)
.catch((e) => {
if (LunaResponseError.isCompatible(e) && e['errorText']?.includes('Service does not exist')) {
return this.luna.call(device, 'luna://com.webos.service.tv.capture/executeOneShot', param, false);
}
throw e;
}));
return tmpPath;
}

async getHbChannelConfig(device: Device): Promise<Partial<HomebrewChannelConfiguration>> {
return await this.luna.call(device, 'luna://org.webosbrew.hbchannel.service/getConfiguration', {});
}
Expand Down Expand Up @@ -156,7 +171,10 @@ export class CrashReport implements CrashReportEntry {
saveName = summary.replace(/\//g, '_');
}
if (appDirIdx < 0) {
return {title: `${processName} (${processId})`, summary, saveName};
if (processName && processId && summary) {
return {title: `${processName} (${processId})`, summary, saveName};
}
return {title: 'Unknown crash', summary: name, saveName}
}
const substr = name.substring(appDirIdx + appDirPrefix.length);
const firstSlash = substr.indexOf('/'), lastSlash = substr.lastIndexOf('/');
Expand Down
21 changes: 20 additions & 1 deletion src/app/core/services/remote-luna.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Injectable} from "@angular/core";
import {DeviceLike} from "../../types";
import {catchError, finalize, Observable} from "rxjs";
import {map} from "rxjs/operators";
import {omit} from "lodash";

export declare interface LunaResponse extends Record<string, any> {
returnValue: boolean,
Expand Down Expand Up @@ -34,7 +35,7 @@ export class RemoteLunaService {
throw new Error(`Bad response ${out}`);
}
if (!typed.returnValue) {
throw new Error(out);
throw new LunaResponseError(typed);
}
return typed;
});
Expand Down Expand Up @@ -62,3 +63,21 @@ export class LunaUnsupportedError extends Error {
super(message);
}
}

export class LunaResponseError extends Error {
declare returnValue: false;
details: string;

[values: string]: any;

constructor(payload: Record<string, any>) {
super(`Luna call returned negative response: ${payload['errorText']}`);
this.details = payload['errorText'];
Object.assign(this, omit(payload, 'message', 'reason', 'details'))
}

static isCompatible(e: any): e is LunaResponseError {
return typeof (e.message) === 'string' && e.returnValue === false;
}

}
6 changes: 6 additions & 0 deletions src/app/debug/debug.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
<app-crashes [device]="device"></app-crashes>
</ng-template>
</li>
<li ngbNavItem="messages" *ngIf="false">
<a ngbNavLink>System Logs</a>
<ng-template ngbNavContent>
<app-log-messages [device]="device"></app-log-messages>
</ng-template>
</li>
</ul>

<div class="flex-fill overflow-hidden debug-content" [ngbNavOutlet]="nav"></div>
Expand Down
4 changes: 3 additions & 1 deletion src/app/debug/debug.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import {DebugComponent} from './debug.component';
import {NgbNavModule, NgbTooltipModule} from "@ng-bootstrap/ng-bootstrap";
import {CrashesComponent} from "./crashes/crashes.component";
import {SharedModule} from "../shared/shared.module";
import { MessagesComponent } from './messages/messages.component';


@NgModule({
declarations: [
DebugComponent,
CrashesComponent
CrashesComponent,
MessagesComponent
],
imports: [
CommonModule,
Expand Down
3 changes: 3 additions & 0 deletions src/app/debug/messages/messages.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<ul class="overflow-scroll" *ngIf="logs">
<li *ngFor="let log of logs">{{log}}</li>
</ul>
Empty file.
25 changes: 25 additions & 0 deletions src/app/debug/messages/messages.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { MessagesComponent } from './messages.component';

describe('MessagesComponent', () => {
let component: MessagesComponent;
let fixture: ComponentFixture<MessagesComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MessagesComponent ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(MessagesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
49 changes: 49 additions & 0 deletions src/app/debug/messages/messages.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {Device} from "../../types";
import {RemoteCommandService} from "../../core/services/remote-command.service";
import {noop, Observable, Subscription, tap} from "rxjs";

@Component({
selector: 'app-log-messages',
templateUrl: './messages.component.html',
styleUrls: ['./messages.component.scss']
})
export class MessagesComponent implements OnDestroy {

logs: string[] = [];

private deviceField: Device | null = null;
private subscription?: Subscription;

constructor(private cmd: RemoteCommandService) {
}

ngOnDestroy(): void {
this.subscription?.unsubscribe();
}

get device(): Device | null {
return this.deviceField;
}

@Input()
set device(device: Device | null) {
this.deviceField = device;
this.subscription?.unsubscribe();
this.subscription = undefined;
this.logs = [];
if (device) {
this.reload(device).catch(noop);
}
}

private async reload(device: Device): Promise<void> {
this.subscription = (await this.logread(device)).subscribe((row) => {
this.logs.push(row);
});
}

private async logread(device: Device) {
return await this.cmd.popen(device, 'tail -f /var/log/messages', 'utf-8');
}
}
5 changes: 4 additions & 1 deletion src/app/info/info.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ <h5 class="card-title">Device - {{sysInfo.modelName}}</h5>
<br>
<strong>webOS version: </strong><span>{{sysInfo.sdkVersion}}</span>
</p>
<div>
<button class="btn btn-primary" (click)="takeScreenshot()">Take Screenshot</button>
</div>
</div>
</div>
<div class="card mt-4" *ngIf="sysInfo && devModeInfo">
Expand All @@ -19,7 +22,7 @@ <h5 class="card-title">Dev Mode</h5>
<strong *ngIf="devModeRemaining">Remaining duration: </strong><span>{{devModeRemaining | async}}</span><br>
<small>Remaining time displaying on your TV will only update on boot.</small>
</p>
<div>
<div *ngIf="devModeInfo.token">
<button class="btn btn-primary me-2" (click)="renewDevMode()">Renew session</button>
<button class="btn btn-primary" (click)="renewScript()">Renew automatically...</button>
</div>
Expand Down
Loading

0 comments on commit 8c90ab3

Please sign in to comment.