live backend setup

This commit is contained in:
Drew Ansbacher
2021-08-17 17:19:46 -06:00
committed by Matt Hill
parent c232ee6a81
commit 09c85b79c3
3 changed files with 262 additions and 2 deletions

View File

@@ -4,6 +4,8 @@ import { RouteReuseStrategy } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { ApiService } from './services/api/api.service'
import { MockApiService } from './services/api/mock-api.service'
import { LiveApiService } from './services/api/live-api.service'
import { HttpService } from './services/api/http.service'
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import * as config from './config/config'
@@ -23,9 +25,14 @@ import { AppRoutingModule } from './app-routing.module';
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
{
provide: ApiService ,
useFactory: () => {
return new MockApiService()
useFactory: (http: HttpService) => {
if(config.config.useMocks) {
return new MockApiService()
} else {
return new LiveApiService(http)
}
},
deps: [HttpService]
},
],
bootstrap: [AppComponent],

View File

@@ -0,0 +1,178 @@
import { Injectable } from '@angular/core'
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http'
import { Observable, from, interval, race, Subject } from 'rxjs'
import { map, take } from 'rxjs/operators'
@Injectable({
providedIn: 'root',
})
export class HttpService {
private unauthorizedApiResponse$ = new Subject()
fullUrl: string
constructor (
private readonly http: HttpClient,
) {
const port = window.location.port
this.fullUrl = `${window.location.protocol}//${window.location.hostname}:${port}`
}
watchUnauth$ (): Observable<{ }> {
return this.unauthorizedApiResponse$.asObservable()
}
async rpcRequest<T> (rpcOpts: RPCOptions): Promise<T> {
rpcOpts.params = rpcOpts.params || { }
const httpOpts = {
method: Method.POST,
body: rpcOpts,
url: `this.fullUrl`,
}
const res = await this.httpRequest<RPCResponse<T>>(httpOpts)
if (isRpcError(res)) {
if (res.error.code === 34) this.unauthorizedApiResponse$.next(true)
throw new RpcError(res.error)
}
if (isRpcSuccess(res)) return res.result
}
async httpRequest<T> (httpOpts: HttpOptions): Promise<T> {
if (httpOpts.withCredentials !== false) {
httpOpts.withCredentials = true
}
const urlIsRelative = httpOpts.url.startsWith('/')
const url = urlIsRelative ?
this.fullUrl + httpOpts.url :
httpOpts.url
Object.keys(httpOpts.params || { }).forEach(key => {
if (httpOpts.params[key] === undefined) {
delete httpOpts.params[key]
}
})
const options = {
responseType: httpOpts.responseType || 'json',
body: httpOpts.body,
observe: 'events',
reportProgress: false,
withCredentials: httpOpts.withCredentials,
headers: httpOpts.headers,
params: httpOpts.params,
timeout: httpOpts.timeout,
} as any
let req: Observable<{ body: T }>
switch (httpOpts.method) {
case Method.GET: req = this.http.get(url, options) as any; break
case Method.POST: req = this.http.post(url, httpOpts.body, options) as any; break
case Method.PUT: req = this.http.put(url, httpOpts.body, options) as any; break
case Method.PATCH: req = this.http.patch(url, httpOpts.body, options) as any; break
case Method.DELETE: req = this.http.delete(url, options) as any; break
}
return (httpOpts.timeout ? withTimeout(req, httpOpts.timeout) : req)
.toPromise()
.then(res => res.body)
.catch(e => { throw new HttpError(e) })
}
}
function RpcError (e: RPCError['error']): void {
const { code, message, data } = e
this.code = code
this.message = message
if (typeof data === 'string') {
this.details = e.data
} else {
this.details = data.details
}
}
function HttpError (e: HttpErrorResponse): void {
const { status, statusText } = e
this.code = status
this.message = statusText
this.details = null
}
function isRpcError<Error, Result> (arg: { error: Error } | { result: Result}): arg is { error: Error } {
return !!(arg as any).error
}
function isRpcSuccess<Error, Result> (arg: { error: Error } | { result: Result}): arg is { result: Result } {
return !!(arg as any).result
}
export enum Method {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
PATCH = 'PATCH',
DELETE = 'DELETE',
}
export interface RPCOptions {
method: string
// @TODO what are valid params? object, bool?
params?: {
[param: string]: string | number | boolean | object | string[] | number[];
}
}
interface RPCBase {
jsonrpc: '2.0'
id: string
}
export interface RPCRequest<T> extends RPCBase {
method: string
params?: T
}
export interface RPCSuccess<T> extends RPCBase {
result: T
}
export interface RPCError extends RPCBase {
error: {
code: number,
message: string
data?: {
details: string
} | string
}
}
export type RPCResponse<T> = RPCSuccess<T> | RPCError
type HttpError = HttpErrorResponse & { error: { code: string, message: string } }
export interface HttpOptions {
method: Method
url: string
headers?: HttpHeaders | {
[header: string]: string | string[]
}
params?: HttpParams | {
[param: string]: string | string[]
}
responseType?: 'json' | 'text'
withCredentials?: boolean
body?: any
timeout?: number
}
function withTimeout<U> (req: Observable<U>, timeout: number): Observable<U> {
return race(
from(req.toPromise()), // this guarantees it only emits on completion, intermediary emissions are suppressed.
interval(timeout).pipe(take(1), map(() => { throw new Error('timeout') })),
)
}

View File

@@ -0,0 +1,75 @@
import { Injectable } from '@angular/core'
import { pauseFor } from '../state.service'
import { ApiService } from './api.service'
import { HttpService } from './http.service'
@Injectable({
providedIn: 'root'
})
export class LiveApiService extends ApiService {
constructor(
private readonly http: HttpService
) { super() }
async verifyProductKey(key) {
await pauseFor(2000)
return {
"is-recovering": false,
"tor-address": null
}
}
async getDataTransferProgress() {
tries = Math.min(tries + 1, 4)
return {
'bytes-transfered': tries,
'total-bytes': 4
}
}
async getEmbassyDrives() {
return [
{
logicalname: 'Name1',
labels: ['label 1', 'label 2'],
capacity: 1600.66666,
used: 200.1255312,
},
{
logicalname: 'Name2',
labels: [],
capacity: 1600.01234,
used: 0.00,
}
]
}
async getRecoveryDrives() {
await pauseFor(2000)
return [
{
logicalname: 'Name1',
version: '0.3.3',
name: 'My Embassy'
},
{
logicalname: 'Name2',
version: '0.2.7',
name: 'My Embassy'
}
]
}
async verifyRecoveryPassword(logicalname, password) {
await pauseFor(2000)
return password.length > 8
}
async setupEmbassy (setupInfo) {
await pauseFor(2000)
return { "tor-address": 'asdfasdfasdf.onion' }
}
}
let tries = 0