WIP: simplify DB queries and error handling
This commit is contained in:
parent
faedf69762
commit
553da64990
@ -64,7 +64,7 @@
|
||||
"@types/urijs": "^1.19.3",
|
||||
"axios": "^0.19.0",
|
||||
"commander": "^3.0.1",
|
||||
"idb-bridge": "^0.0.10",
|
||||
"idb-bridge": "^0.0.11",
|
||||
"qrcode-generator": "^1.4.3",
|
||||
"source-map-support": "^0.5.12",
|
||||
"urijs": "^1.18.10"
|
||||
|
3
packages/idb-bridge/.vscode/settings.json
vendored
3
packages/idb-bridge/.vscode/settings.json
vendored
@ -1,3 +1,4 @@
|
||||
{
|
||||
"editor.tabSize": 2
|
||||
"editor.tabSize": 2,
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
102
src/dbTypes.ts
102
src/dbTypes.ts
@ -36,6 +36,7 @@ import {
|
||||
} from "./talerTypes";
|
||||
|
||||
import { Index, Store } from "./query";
|
||||
import { Timestamp, OperationError } from "./walletTypes";
|
||||
|
||||
/**
|
||||
* Current database version, should be incremented
|
||||
@ -310,13 +311,10 @@ export class DenominationRecord {
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange record as stored in the wallet's database.
|
||||
* Details about the exchange that we only know after
|
||||
* querying /keys and /wire.
|
||||
*/
|
||||
export interface ExchangeRecord {
|
||||
/**
|
||||
* Base url of the exchange.
|
||||
*/
|
||||
baseUrl: string;
|
||||
export interface ExchangeDetails {
|
||||
/**
|
||||
* Master public key of the exchange.
|
||||
*/
|
||||
@ -331,22 +329,60 @@ export interface ExchangeRecord {
|
||||
*/
|
||||
currency: string;
|
||||
|
||||
/**
|
||||
* Timestamp for last update.
|
||||
*/
|
||||
lastUpdateTime: number;
|
||||
|
||||
/**
|
||||
* When did we actually use this exchange last (in milliseconds). If we
|
||||
* never used the exchange for anything but just updated its info, this is
|
||||
* set to 0. (Currently only updated when reserves are created.)
|
||||
*/
|
||||
lastUsedTime: number;
|
||||
|
||||
/**
|
||||
* Last observed protocol version.
|
||||
*/
|
||||
protocolVersion?: string;
|
||||
protocolVersion: string;
|
||||
|
||||
/**
|
||||
* Timestamp for last update.
|
||||
*/
|
||||
lastUpdateTime: Timestamp;
|
||||
}
|
||||
|
||||
export enum ExchangeUpdateStatus {
|
||||
NONE = "none",
|
||||
FETCH_KEYS = "fetch_keys",
|
||||
FETCH_WIRE = "fetch_wire",
|
||||
}
|
||||
|
||||
export interface ExchangeBankAccount {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface ExchangeWireInfo {
|
||||
feesForType: { [wireMethod: string]: WireFee[] };
|
||||
accounts: ExchangeBankAccount[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange record as stored in the wallet's database.
|
||||
*/
|
||||
export interface ExchangeRecord {
|
||||
/**
|
||||
* Base url of the exchange.
|
||||
*/
|
||||
baseUrl: string;
|
||||
|
||||
/**
|
||||
* Details, once known.
|
||||
*/
|
||||
details: ExchangeDetails | undefined;
|
||||
|
||||
/**
|
||||
* Mapping from wire method type to the wire fee.
|
||||
*/
|
||||
wireInfo: ExchangeWireInfo | undefined;
|
||||
|
||||
/**
|
||||
* Time when the update to the exchange has been started or
|
||||
* undefined if no update is in progress.
|
||||
*/
|
||||
updateStarted: Timestamp | undefined;
|
||||
|
||||
updateStatus: ExchangeUpdateStatus;
|
||||
|
||||
lastError?: OperationError;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -554,21 +590,6 @@ export class ProposalDownloadRecord {
|
||||
static checked: (obj: any) => ProposalDownloadRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wire fees for an exchange.
|
||||
*/
|
||||
export interface ExchangeWireFeesRecord {
|
||||
/**
|
||||
* Base URL of the exchange.
|
||||
*/
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Mapping from wire method type to the wire fee.
|
||||
*/
|
||||
feesForType: { [wireMethod: string]: WireFee[] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Status of a tip we got from a merchant.
|
||||
*/
|
||||
@ -931,12 +952,6 @@ export namespace Stores {
|
||||
constructor() {
|
||||
super("exchanges", { keyPath: "baseUrl" });
|
||||
}
|
||||
|
||||
pubKeyIndex = new Index<string, ExchangeRecord>(
|
||||
this,
|
||||
"pubKeyIndex",
|
||||
"masterPublicKey",
|
||||
);
|
||||
}
|
||||
|
||||
class CoinsStore extends Store<CoinRecord> {
|
||||
@ -1034,12 +1049,6 @@ export namespace Stores {
|
||||
}
|
||||
}
|
||||
|
||||
class ExchangeWireFeesStore extends Store<ExchangeWireFeesRecord> {
|
||||
constructor() {
|
||||
super("exchangeWireFees", { keyPath: "exchangeBaseUrl" });
|
||||
}
|
||||
}
|
||||
|
||||
class ReservesStore extends Store<ReserveRecord> {
|
||||
constructor() {
|
||||
super("reserves", { keyPath: "reserve_pub" });
|
||||
@ -1094,7 +1103,6 @@ export namespace Stores {
|
||||
export const config = new ConfigStore();
|
||||
export const currencies = new CurrenciesStore();
|
||||
export const denominations = new DenominationsStore();
|
||||
export const exchangeWireFees = new ExchangeWireFeesStore();
|
||||
export const exchanges = new ExchangeStore();
|
||||
export const precoins = new Store<PreCoinRecord>("precoins", {
|
||||
keyPath: "coinPub",
|
||||
|
@ -20,7 +20,6 @@
|
||||
import process = require("process");
|
||||
import path = require("path");
|
||||
import readline = require("readline");
|
||||
import { symlinkSync } from "fs";
|
||||
|
||||
class Converter<T> {}
|
||||
|
||||
@ -54,6 +53,7 @@ interface ArgumentDef {
|
||||
name: string;
|
||||
conv: Converter<any>;
|
||||
args: ArgumentArgs<any>;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
interface SubcommandDef {
|
||||
@ -181,7 +181,7 @@ export class CommandGroup<GN extends keyof any, TG> {
|
||||
return this as any;
|
||||
}
|
||||
|
||||
argument<N extends keyof any, V>(
|
||||
requiredArgument<N extends keyof any, V>(
|
||||
name: N,
|
||||
conv: Converter<V>,
|
||||
args: ArgumentArgs<V> = {},
|
||||
@ -190,6 +190,22 @@ export class CommandGroup<GN extends keyof any, TG> {
|
||||
args: args,
|
||||
conv: conv,
|
||||
name: name as string,
|
||||
required: true,
|
||||
};
|
||||
this.arguments.push(argDef);
|
||||
return this as any;
|
||||
}
|
||||
|
||||
maybeArgument<N extends keyof any, V>(
|
||||
name: N,
|
||||
conv: Converter<V>,
|
||||
args: ArgumentArgs<V> = {},
|
||||
): CommandGroup<GN, TG & SubRecord<GN, N, V | undefined>> {
|
||||
const argDef: ArgumentDef = {
|
||||
args: args,
|
||||
conv: conv,
|
||||
name: name as string,
|
||||
required: false,
|
||||
};
|
||||
this.arguments.push(argDef);
|
||||
return this as any;
|
||||
@ -401,10 +417,25 @@ export class CommandGroup<GN extends keyof any, TG> {
|
||||
process.exit(-1);
|
||||
throw Error("not reached");
|
||||
}
|
||||
myArgs[d.name] = unparsedArgs[i];
|
||||
posArgIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = posArgIndex; i < this.arguments.length; i++) {
|
||||
const d = this.arguments[i];
|
||||
const n = this.name ?? progname;
|
||||
if (d.required) {
|
||||
if (d.args.default !== undefined) {
|
||||
myArgs[d.name] = d.args.default;
|
||||
} else {
|
||||
console.error(`error: missing positional argument '${d.name}' for ${n}`);
|
||||
process.exit(-1);
|
||||
throw Error("not reached");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let option of this.options) {
|
||||
if (option.isFlag == false && option.required == true) {
|
||||
if (!foundOptions[option.name]) {
|
||||
@ -433,9 +464,7 @@ export class CommandGroup<GN extends keyof any, TG> {
|
||||
unparsedArgs.slice(i + 1),
|
||||
parsedArgs,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.myAction) {
|
||||
} else if (this.myAction) {
|
||||
this.myAction(parsedArgs);
|
||||
} else {
|
||||
this.printHelp(progname, parents);
|
||||
@ -513,18 +542,35 @@ export class Program<PN extends keyof any, T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a positional argument to the program.
|
||||
* Add a required positional argument to the program.
|
||||
*/
|
||||
argument<N extends keyof any, V>(
|
||||
requiredArgument<N extends keyof any, V>(
|
||||
name: N,
|
||||
conv: Converter<V>,
|
||||
args: ArgumentArgs<V> = {},
|
||||
): Program<N, T & SubRecord<PN, N, V>> {
|
||||
this.mainCommand.argument(name, conv, args);
|
||||
this.mainCommand.requiredArgument(name, conv, args);
|
||||
return this as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an optional argument to the program.
|
||||
*/
|
||||
maybeArgument<N extends keyof any, V>(
|
||||
name: N,
|
||||
conv: Converter<V>,
|
||||
args: ArgumentArgs<V> = {},
|
||||
): Program<N, T & SubRecord<PN, N, V | undefined>> {
|
||||
this.mainCommand.maybeArgument(name, conv, args);
|
||||
return this as any;
|
||||
}
|
||||
}
|
||||
|
||||
export type GetArgType<T> =
|
||||
T extends Program<any, infer AT> ? AT :
|
||||
T extends CommandGroup<any, infer AT> ? AT :
|
||||
any;
|
||||
|
||||
export function program<PN extends keyof any>(
|
||||
argKey: PN,
|
||||
args: ProgramArgs = {},
|
||||
@ -532,6 +578,8 @@ export function program<PN extends keyof any>(
|
||||
return new Program(argKey as string, args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function prompt(question: string): Promise<string> {
|
||||
const stdinReadline = readline.createInterface({
|
||||
input: process.stdin,
|
||||
|
@ -97,6 +97,28 @@ const walletCli = clk
|
||||
help: "Enable verbose output.",
|
||||
});
|
||||
|
||||
type WalletCliArgsType = clk.GetArgType<typeof walletCli>;
|
||||
|
||||
async function withWallet<T>(
|
||||
walletCliArgs: WalletCliArgsType,
|
||||
f: (w: Wallet) => Promise<T>,
|
||||
): Promise<T> {
|
||||
applyVerbose(walletCliArgs.wallet.verbose);
|
||||
const wallet = await getDefaultNodeWallet({
|
||||
persistentStoragePath: walletDbPath,
|
||||
});
|
||||
try {
|
||||
await wallet.fillDefaults();
|
||||
const ret = await f(wallet);
|
||||
return ret;
|
||||
} catch (e) {
|
||||
console.error("caught exception:", e);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
wallet.stop();
|
||||
}
|
||||
}
|
||||
|
||||
walletCli
|
||||
.subcommand("testPayCmd", "test-pay", { help: "create contract and pay" })
|
||||
.requiredOption("amount", ["-a", "--amount"], clk.STRING)
|
||||
@ -135,15 +157,11 @@ walletCli
|
||||
walletCli
|
||||
.subcommand("", "balance", { help: "Show wallet balance." })
|
||||
.action(async args => {
|
||||
applyVerbose(args.wallet.verbose);
|
||||
console.log("balance command called");
|
||||
const wallet = await getDefaultNodeWallet({
|
||||
persistentStoragePath: walletDbPath,
|
||||
});
|
||||
console.log("got wallet");
|
||||
withWallet(args, async (wallet) => {
|
||||
const balance = await wallet.getBalances();
|
||||
console.log(JSON.stringify(balance, undefined, 2));
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
@ -153,29 +171,19 @@ walletCli
|
||||
.requiredOption("limit", ["--limit"], clk.STRING)
|
||||
.requiredOption("contEvt", ["--continue-with"], clk.STRING)
|
||||
.action(async args => {
|
||||
applyVerbose(args.wallet.verbose);
|
||||
console.log("history command called");
|
||||
const wallet = await getDefaultNodeWallet({
|
||||
persistentStoragePath: walletDbPath,
|
||||
});
|
||||
console.log("got wallet");
|
||||
withWallet(args, async (wallet) => {
|
||||
const history = await wallet.getHistory();
|
||||
console.log(JSON.stringify(history, undefined, 2));
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("", "pending", { help: "Show pending operations." })
|
||||
.action(async args => {
|
||||
applyVerbose(args.wallet.verbose);
|
||||
console.log("history command called");
|
||||
const wallet = await getDefaultNodeWallet({
|
||||
persistentStoragePath: walletDbPath,
|
||||
});
|
||||
console.log("got wallet");
|
||||
withWallet(args, async (wallet) => {
|
||||
const pending = await wallet.getPendingOperations();
|
||||
console.log(JSON.stringify(pending, undefined, 2));
|
||||
process.exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
async function asyncSleep(milliSeconds: number): Promise<void> {
|
||||
@ -184,6 +192,16 @@ async function asyncSleep(milliSeconds: number): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
walletCli
|
||||
.subcommand("runPendingOpt", "run-pending", {
|
||||
help: "Run pending operations."
|
||||
})
|
||||
.action(async (args) => {
|
||||
withWallet(args, async (wallet) => {
|
||||
await wallet.processPending();
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("testMerchantQrcodeCmd", "test-merchant-qrcode")
|
||||
.requiredOption("amount", ["-a", "--amount"], clk.STRING, {
|
||||
@ -279,7 +297,7 @@ walletCli
|
||||
|
||||
walletCli
|
||||
.subcommand("withdrawUriCmd", "withdraw-uri")
|
||||
.argument("withdrawUri", clk.STRING)
|
||||
.requiredArgument("withdrawUri", clk.STRING)
|
||||
.action(async args => {
|
||||
applyVerbose(args.wallet.verbose);
|
||||
const cmdArgs = args.withdrawUriCmd;
|
||||
@ -318,7 +336,7 @@ walletCli
|
||||
|
||||
walletCli
|
||||
.subcommand("tipUriCmd", "tip-uri")
|
||||
.argument("uri", clk.STRING)
|
||||
.requiredArgument("uri", clk.STRING)
|
||||
.action(async args => {
|
||||
applyVerbose(args.wallet.verbose);
|
||||
const tipUri = args.tipUriCmd.uri;
|
||||
@ -334,7 +352,7 @@ walletCli
|
||||
|
||||
walletCli
|
||||
.subcommand("refundUriCmd", "refund-uri")
|
||||
.argument("uri", clk.STRING)
|
||||
.requiredArgument("uri", clk.STRING)
|
||||
.action(async args => {
|
||||
applyVerbose(args.wallet.verbose);
|
||||
const refundUri = args.refundUriCmd.uri;
|
||||
@ -346,20 +364,38 @@ walletCli
|
||||
wallet.stop();
|
||||
});
|
||||
|
||||
const exchangesCli = walletCli
|
||||
.subcommand("exchangesCmd", "exchanges", {
|
||||
help: "Manage exchanges."
|
||||
});
|
||||
|
||||
exchangesCli.subcommand("exchangesListCmd", "list", {
|
||||
help: "List known exchanges."
|
||||
const exchangesCli = walletCli.subcommand("exchangesCmd", "exchanges", {
|
||||
help: "Manage exchanges.",
|
||||
});
|
||||
|
||||
exchangesCli.subcommand("exchangesListCmd", "update");
|
||||
exchangesCli
|
||||
.subcommand("exchangesListCmd", "list", {
|
||||
help: "List known exchanges.",
|
||||
})
|
||||
.action(async args => {
|
||||
console.log("Listing exchanges ...");
|
||||
withWallet(args, async (wallet) => {
|
||||
const exchanges = await wallet.getExchanges();
|
||||
console.log("exchanges", exchanges);
|
||||
});
|
||||
});
|
||||
|
||||
exchangesCli
|
||||
.subcommand("exchangesUpdateCmd", "update", {
|
||||
help: "Update or add an exchange by base URL.",
|
||||
})
|
||||
.requiredArgument("url", clk.STRING, {
|
||||
help: "Base URL of the exchange.",
|
||||
})
|
||||
.action(async args => {
|
||||
withWallet(args, async (wallet) => {
|
||||
const res = await wallet.updateExchangeFromUrl(args.exchangesUpdateCmd.url);
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("payUriCmd", "pay-uri")
|
||||
.argument("url", clk.STRING)
|
||||
.requiredArgument("url", clk.STRING)
|
||||
.flag("autoYes", ["-y", "--yes"])
|
||||
.action(async args => {
|
||||
applyVerbose(args.wallet.verbose);
|
||||
@ -374,7 +410,7 @@ walletCli
|
||||
});
|
||||
|
||||
const testCli = walletCli.subcommand("testingArgs", "testing", {
|
||||
help: "Subcommands for testing GNU Taler deployments."
|
||||
help: "Subcommands for testing GNU Taler deployments.",
|
||||
});
|
||||
|
||||
testCli
|
||||
|
@ -25,6 +25,7 @@ import { AmountJson } from "./amounts";
|
||||
import * as Amounts from "./amounts";
|
||||
|
||||
import URI = require("urijs");
|
||||
import { Timestamp } from "./walletTypes";
|
||||
|
||||
/**
|
||||
* Show an amount in a form suitable for the user.
|
||||
@ -125,6 +126,19 @@ export function getTalerStampSec(stamp: string): number | null {
|
||||
return parseInt(m[1], 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a timestamp from a Taler timestamp string.
|
||||
*/
|
||||
export function extractTalerStamp(stamp: string): Timestamp | undefined {
|
||||
const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/);
|
||||
if (!m || !m[1]) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
t_ms: parseInt(m[1], 10) * 1000,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a timestamp is in the right format.
|
||||
*/
|
||||
|
351
src/logging.ts
351
src/logging.ts
@ -1,351 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 Inria
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Configurable logging. Allows to log persistently to a database.
|
||||
*/
|
||||
|
||||
import {
|
||||
QueryRoot,
|
||||
Store,
|
||||
} from "./query";
|
||||
import { openPromise } from "./promiseUtils";
|
||||
|
||||
/**
|
||||
* Supported log levels.
|
||||
*/
|
||||
export type Level = "error" | "debug" | "info" | "warn";
|
||||
|
||||
// Right now, our debug/info/warn/debug loggers just use the console based
|
||||
// loggers. This might change in the future.
|
||||
|
||||
function makeInfo() {
|
||||
return console.info.bind(console, "%o");
|
||||
}
|
||||
|
||||
function makeWarn() {
|
||||
return console.warn.bind(console, "%o");
|
||||
}
|
||||
|
||||
function makeError() {
|
||||
return console.error.bind(console, "%o");
|
||||
}
|
||||
|
||||
function makeDebug() {
|
||||
return console.log.bind(console, "%o");
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message using the configurable logger.
|
||||
*/
|
||||
export async function log(msg: string, level: Level = "info"): Promise<void> {
|
||||
const ci = getCallInfo(2);
|
||||
return record(level, msg, undefined, ci.file, ci.line, ci.column);
|
||||
}
|
||||
|
||||
function getCallInfo(level: number) {
|
||||
// see https://github.com/v8/v8/wiki/Stack-Trace-API
|
||||
const stack = Error().stack;
|
||||
if (!stack) {
|
||||
return unknownFrame;
|
||||
}
|
||||
const lines = stack.split("\n");
|
||||
return parseStackLine(lines[level + 1]);
|
||||
}
|
||||
|
||||
interface Frame {
|
||||
column?: number;
|
||||
file?: string;
|
||||
line?: number;
|
||||
method?: string;
|
||||
}
|
||||
|
||||
const unknownFrame: Frame = {
|
||||
column: 0,
|
||||
file: "(unknown)",
|
||||
line: 0,
|
||||
method: "(unknown)",
|
||||
};
|
||||
|
||||
/**
|
||||
* Adapted from https://github.com/errwischt/stacktrace-parser.
|
||||
*/
|
||||
function parseStackLine(stackLine: string): Frame {
|
||||
// tslint:disable-next-line:max-line-length
|
||||
const chrome = /^\s*at (?:(?:(?:Anonymous function)?|((?:\[object object\])?\S+(?: \[as \S+\])?)) )?\(?((?:file|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
|
||||
const gecko = /^(?:\s*([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$/i;
|
||||
const node = /^\s*at (?:((?:\[object object\])?\S+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i;
|
||||
let parts;
|
||||
|
||||
parts = gecko.exec(stackLine);
|
||||
if (parts) {
|
||||
const f: Frame = {
|
||||
column: parts[5] ? +parts[5] : undefined,
|
||||
file: parts[3],
|
||||
line: +parts[4],
|
||||
method: parts[1] || "(unknown)",
|
||||
};
|
||||
return f;
|
||||
}
|
||||
|
||||
parts = chrome.exec(stackLine);
|
||||
if (parts) {
|
||||
const f: Frame = {
|
||||
column: parts[4] ? +parts[4] : undefined,
|
||||
file: parts[2],
|
||||
line: +parts[3],
|
||||
method: parts[1] || "(unknown)",
|
||||
};
|
||||
return f;
|
||||
}
|
||||
|
||||
parts = node.exec(stackLine);
|
||||
if (parts) {
|
||||
const f: Frame = {
|
||||
column: parts[4] ? +parts[4] : undefined,
|
||||
file: parts[2],
|
||||
line: +parts[3],
|
||||
method: parts[1] || "(unknown)",
|
||||
};
|
||||
return f;
|
||||
}
|
||||
|
||||
return unknownFrame;
|
||||
}
|
||||
|
||||
|
||||
let db: IDBDatabase|undefined;
|
||||
|
||||
/**
|
||||
* A structured log entry as stored in the database.
|
||||
*/
|
||||
export interface LogEntry {
|
||||
/**
|
||||
* Soure code column where the error occured.
|
||||
*/
|
||||
col?: number;
|
||||
/**
|
||||
* Additional detail for the log statement.
|
||||
*/
|
||||
detail?: string;
|
||||
/**
|
||||
* Id of the log entry, used as primary
|
||||
* key for the database.
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* Log level, see [[Level}}.
|
||||
*/
|
||||
level: string;
|
||||
/**
|
||||
* Line where the log was created from.
|
||||
*/
|
||||
line?: number;
|
||||
/**
|
||||
* The actual log message.
|
||||
*/
|
||||
msg: string;
|
||||
/**
|
||||
* The source file where the log enctry
|
||||
* was created from.
|
||||
*/
|
||||
source?: string;
|
||||
/**
|
||||
* Time when the log entry was created.
|
||||
*/
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all logs. Only use for debugging, since this returns all logs ever made
|
||||
* at once without pagination.
|
||||
*/
|
||||
export async function getLogs(): Promise<LogEntry[]> {
|
||||
if (!db) {
|
||||
db = await openLoggingDb();
|
||||
}
|
||||
return await new QueryRoot(db).iter(logsStore).toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* The barrier ensures that only one DB write is scheduled against the log db
|
||||
* at the same time, so that the DB can stay responsive. This is a bit of a
|
||||
* design problem with IndexedDB, it doesn't guarantee fairness.
|
||||
*/
|
||||
let barrier: any;
|
||||
|
||||
/**
|
||||
* Record an exeption in the log.
|
||||
*/
|
||||
export async function recordException(msg: string, e: any): Promise<void> {
|
||||
let stack: string|undefined;
|
||||
let frame: Frame|undefined;
|
||||
try {
|
||||
stack = e.stack;
|
||||
if (stack) {
|
||||
const lines = stack.split("\n");
|
||||
frame = parseStackLine(lines[1]);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
if (!frame) {
|
||||
frame = unknownFrame;
|
||||
}
|
||||
return record("error", e.toString(), stack, frame.file, frame.line, frame.column);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cache for reports. Also used when something is so broken that we can't even
|
||||
* access the database.
|
||||
*/
|
||||
const reportCache: { [reportId: string]: any } = {};
|
||||
|
||||
|
||||
/**
|
||||
* Get a UUID that does not use cryptographically secure randomness.
|
||||
* Formatted as RFC4122 version 4 UUID.
|
||||
*/
|
||||
function getInsecureUuid() {
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c: string) => {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c === "x" ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store a report and return a unique identifier to retrieve it later.
|
||||
*/
|
||||
export async function storeReport(report: any): Promise<string> {
|
||||
const uid = getInsecureUuid();
|
||||
reportCache[uid] = report;
|
||||
return uid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a report by its unique identifier.
|
||||
*/
|
||||
export async function getReport(reportUid: string): Promise<any> {
|
||||
return reportCache[reportUid];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Record a log entry in the database.
|
||||
*/
|
||||
export async function record(level: Level,
|
||||
msg: string,
|
||||
detail?: string,
|
||||
source?: string,
|
||||
line?: number,
|
||||
col?: number): Promise<void> {
|
||||
if (typeof indexedDB === "undefined") {
|
||||
console.log("can't access DB for logging in this context");
|
||||
console.log("log was", { level, msg, detail, source, line, col });
|
||||
return;
|
||||
}
|
||||
|
||||
let myBarrier: any;
|
||||
|
||||
if (barrier) {
|
||||
const p = barrier.promise;
|
||||
myBarrier = barrier = openPromise();
|
||||
await p;
|
||||
} else {
|
||||
myBarrier = barrier = openPromise();
|
||||
}
|
||||
|
||||
try {
|
||||
if (!db) {
|
||||
db = await openLoggingDb();
|
||||
}
|
||||
|
||||
const count = await new QueryRoot(db).count(logsStore);
|
||||
|
||||
if (count > 1000) {
|
||||
await new QueryRoot(db).deleteIf(logsStore, (e, i) => (i < 200));
|
||||
}
|
||||
|
||||
const entry: LogEntry = {
|
||||
col,
|
||||
detail,
|
||||
level,
|
||||
line,
|
||||
msg,
|
||||
source,
|
||||
timestamp: new Date().getTime(),
|
||||
};
|
||||
await new QueryRoot(db).put(logsStore, entry);
|
||||
} finally {
|
||||
await Promise.resolve().then(() => myBarrier.resolve());
|
||||
}
|
||||
}
|
||||
|
||||
const loggingDbVersion = 2;
|
||||
|
||||
const logsStore: Store<LogEntry> = new Store<LogEntry>("logs");
|
||||
|
||||
/**
|
||||
* Get a handle to the IndexedDB used to store
|
||||
* logs.
|
||||
*/
|
||||
export function openLoggingDb(): Promise<IDBDatabase> {
|
||||
return new Promise<IDBDatabase>((resolve, reject) => {
|
||||
const req = indexedDB.open("taler-logging", loggingDbVersion);
|
||||
req.onerror = (e) => {
|
||||
reject(e);
|
||||
};
|
||||
req.onsuccess = (e) => {
|
||||
resolve(req.result);
|
||||
};
|
||||
req.onupgradeneeded = (e) => {
|
||||
const resDb = req.result;
|
||||
if (e.oldVersion !== 0) {
|
||||
try {
|
||||
resDb.deleteObjectStore("logs");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
resDb.createObjectStore("logs", { keyPath: "id", autoIncrement: true });
|
||||
resDb.createObjectStore("reports", { keyPath: "uid", autoIncrement: false });
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message at severity info.
|
||||
*/
|
||||
export const info = makeInfo();
|
||||
|
||||
/**
|
||||
* Log a message at severity debug.
|
||||
*/
|
||||
export const debug = makeDebug();
|
||||
|
||||
/**
|
||||
* Log a message at severity warn.
|
||||
*/
|
||||
export const warn = makeWarn();
|
||||
|
||||
/**
|
||||
* Log a message at severity error.
|
||||
*/
|
||||
export const error = makeError();
|
1182
src/query.ts
1182
src/query.ts
File diff suppressed because it is too large
Load Diff
1348
src/wallet.ts
1348
src/wallet.ts
File diff suppressed because it is too large
Load Diff
@ -34,8 +34,7 @@ import {
|
||||
CoinRecord,
|
||||
DenominationRecord,
|
||||
ExchangeRecord,
|
||||
ExchangeWireFeesRecord,
|
||||
TipRecord,
|
||||
ExchangeWireInfo,
|
||||
} from "./dbTypes";
|
||||
import { CoinPaySig, ContractTerms, PayReq } from "./talerTypes";
|
||||
|
||||
@ -98,7 +97,7 @@ export interface ReserveCreationInfo {
|
||||
/**
|
||||
* Wire fees from the exchange.
|
||||
*/
|
||||
wireFees: ExchangeWireFeesRecord;
|
||||
wireFees: ExchangeWireInfo;
|
||||
|
||||
/**
|
||||
* Does the wallet know about an auditor for
|
||||
@ -475,7 +474,6 @@ export interface PreparePayResultError {
|
||||
error: string;
|
||||
}
|
||||
|
||||
|
||||
export interface PreparePayResultPaid {
|
||||
status: "paid";
|
||||
contractTerms: ContractTerms;
|
||||
@ -517,18 +515,40 @@ export interface WalletDiagnostics {
|
||||
}
|
||||
|
||||
export interface PendingWithdrawOperation {
|
||||
type: "withdraw"
|
||||
type: "withdraw";
|
||||
}
|
||||
|
||||
export interface PendingRefreshOperation {
|
||||
type: "refresh"
|
||||
type: "refresh";
|
||||
}
|
||||
|
||||
export interface PendingPayOperation {
|
||||
type: "pay"
|
||||
type: "pay";
|
||||
}
|
||||
|
||||
export type PendingOperationInfo = PendingWithdrawOperation
|
||||
export interface OperationError {
|
||||
type: string;
|
||||
message: string;
|
||||
details: any;
|
||||
}
|
||||
|
||||
export interface PendingExchangeUpdateOperation {
|
||||
type: "exchange-update";
|
||||
stage: string;
|
||||
exchangeBaseUrl: string;
|
||||
lastError?: OperationError;
|
||||
}
|
||||
|
||||
export interface PendingBugOperation {
|
||||
type: "bug";
|
||||
message: string;
|
||||
details: any;
|
||||
}
|
||||
|
||||
export type PendingOperationInfo =
|
||||
| PendingWithdrawOperation
|
||||
| PendingBugOperation
|
||||
| PendingExchangeUpdateOperation;
|
||||
|
||||
export interface PendingOperationsResponse {
|
||||
pendingOperations: PendingOperationInfo[];
|
||||
@ -542,3 +562,23 @@ export interface HistoryQuery {
|
||||
*/
|
||||
level: number;
|
||||
}
|
||||
|
||||
export interface Timestamp {
|
||||
/**
|
||||
* Timestamp in milliseconds.
|
||||
*/
|
||||
t_ms: number;
|
||||
}
|
||||
|
||||
export interface Duration {
|
||||
/**
|
||||
* Duration in milliseconds.
|
||||
*/
|
||||
d_ms: number;
|
||||
}
|
||||
|
||||
export function getTimestampNow(): Timestamp {
|
||||
return {
|
||||
t_ms: new Date().getTime(),
|
||||
};
|
||||
}
|
||||
|
@ -73,14 +73,6 @@ export interface MessageMap {
|
||||
request: { baseUrl: string };
|
||||
response: dbTypes.ExchangeRecord;
|
||||
};
|
||||
"currency-info": {
|
||||
request: { name: string };
|
||||
response: dbTypes.CurrencyRecord;
|
||||
};
|
||||
"hash-contract": {
|
||||
request: { contract: object };
|
||||
response: string;
|
||||
};
|
||||
"reserve-creation-info": {
|
||||
request: { baseUrl: string; amount: AmountJson };
|
||||
response: walletTypes.ReserveCreationInfo;
|
||||
@ -145,14 +137,6 @@ export interface MessageMap {
|
||||
request: {};
|
||||
response: void;
|
||||
};
|
||||
"log-and-display-error": {
|
||||
request: any;
|
||||
response: void;
|
||||
};
|
||||
"get-report": {
|
||||
request: { reportUid: string };
|
||||
response: void;
|
||||
};
|
||||
"get-purchase-details": {
|
||||
request: { contractTermsHash: string };
|
||||
response: walletTypes.PurchaseDetails;
|
||||
|
@ -1,18 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Taler Wallet: Error Occured</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../style/wallet.css">
|
||||
|
||||
<link rel="icon" href="/img/icon.png">
|
||||
|
||||
<script src="/dist/page-common-bundle.js"></script>
|
||||
<script src="/dist/error-bundle.js"></script>
|
||||
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
</body>
|
||||
</html>
|
@ -1,129 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2015-2016 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Page shown to the user to confirm creation
|
||||
* of a reserve, usually requested by the bank.
|
||||
*
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import URI = require("urijs");
|
||||
|
||||
import * as wxApi from "../wxApi";
|
||||
|
||||
import { Collapsible } from "../renderHtml";
|
||||
|
||||
interface ErrorProps {
|
||||
report: any;
|
||||
}
|
||||
|
||||
class ErrorView extends React.Component<ErrorProps, { }> {
|
||||
render(): JSX.Element {
|
||||
const report = this.props.report;
|
||||
if (!report) {
|
||||
return (
|
||||
<div id="main">
|
||||
<h1>Error Report Not Found</h1>
|
||||
<p>This page is supposed to display an error reported by the GNU Taler wallet,
|
||||
but the corresponding error report can't be found.</p>
|
||||
<p>Maybe the error occured before the browser was restarted or the wallet was reloaded.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
try {
|
||||
switch (report.name) {
|
||||
case "pay-post-failed": {
|
||||
const summary = report.contractTerms.summary || report.contractTerms.order_id;
|
||||
return (
|
||||
<div id="main">
|
||||
<h1>Failed to send payment</h1>
|
||||
<p>
|
||||
Failed to send payment for <strong>{summary}</strong>{" "}
|
||||
to merchant <strong>{report.contractTerms.merchant.name}</strong>.
|
||||
</p>
|
||||
<p>
|
||||
You can <a href={report.contractTerms.fulfillment_url}>retry</a> the payment.{" "}
|
||||
If this problem persists, please contact the mechant with the error details below.
|
||||
</p>
|
||||
<Collapsible initiallyCollapsed={true} title="Error Details">
|
||||
<pre>
|
||||
{JSON.stringify(report, null, " ")}
|
||||
</pre>
|
||||
</Collapsible>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return (
|
||||
<div id="main">
|
||||
<h1>Unknown Error</h1>
|
||||
The GNU Taler wallet reported an unknown error. Here are the details:
|
||||
<pre>
|
||||
{JSON.stringify(report, null, " ")}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
return (
|
||||
<div id="main">
|
||||
<h1>Error</h1>
|
||||
The GNU Taler wallet reported an error. Here are the details:
|
||||
<pre>
|
||||
{JSON.stringify(report, null, " ")}
|
||||
</pre>
|
||||
A detailed error report could not be generated:
|
||||
<pre>
|
||||
{e.toString()}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const url = new URI(document.location.href);
|
||||
const query: any = URI.parseQuery(url.query());
|
||||
|
||||
const container = document.getElementById("container");
|
||||
if (!container) {
|
||||
console.error("fatal: can't mount component, countainer missing");
|
||||
return;
|
||||
}
|
||||
|
||||
// report that we'll render, either looked up from the
|
||||
// logging module or synthesized here for fixed/fatal errors
|
||||
let report;
|
||||
|
||||
const reportUid: string = query.reportUid;
|
||||
if (!reportUid) {
|
||||
report = {
|
||||
name: "missing-error",
|
||||
};
|
||||
} else {
|
||||
report = await wxApi.getReport(reportUid);
|
||||
}
|
||||
|
||||
ReactDOM.render(<ErrorView report={report} />, container);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => main());
|
@ -1,27 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Taler Wallet: Logs</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../style/wallet.css">
|
||||
|
||||
<link rel="icon" href="/img/icon.png">
|
||||
|
||||
<script src="/dist/page-common-bundle.js"></script>
|
||||
<script src="/dist/logs-bundle.js"></script>
|
||||
|
||||
<style>
|
||||
.tree-item {
|
||||
margin: 2em;
|
||||
border-radius: 5px;
|
||||
border: 1px solid gray;
|
||||
padding: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
</body>
|
||||
</html>
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 Inria
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Show wallet logs.
|
||||
*
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
import {
|
||||
LogEntry,
|
||||
getLogs,
|
||||
} from "../../logging";
|
||||
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
||||
interface LogViewProps {
|
||||
log: LogEntry;
|
||||
}
|
||||
|
||||
class LogView extends React.Component<LogViewProps, {}> {
|
||||
render(): JSX.Element {
|
||||
const e = this.props.log;
|
||||
return (
|
||||
<div className="tree-item">
|
||||
<ul>
|
||||
<li>level: {e.level}</li>
|
||||
<li>msg: {e.msg}</li>
|
||||
<li>id: {e.id || "unknown"}</li>
|
||||
<li>file: {e.source || "(unknown)"}</li>
|
||||
<li>line: {e.line || "(unknown)"}</li>
|
||||
<li>col: {e.col || "(unknown)"}</li>
|
||||
{(e.detail ? <li> detail: <pre>{e.detail}</pre></li> : [])}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface LogsState {
|
||||
logs: LogEntry[]|undefined;
|
||||
}
|
||||
|
||||
class Logs extends React.Component<{}, LogsState> {
|
||||
constructor(props: {}) {
|
||||
super({});
|
||||
this.update();
|
||||
this.state = {} as any;
|
||||
}
|
||||
|
||||
async update() {
|
||||
const logs = await getLogs();
|
||||
this.setState({logs});
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const logs = this.state.logs;
|
||||
if (!logs) {
|
||||
return <span>...</span>;
|
||||
}
|
||||
return (
|
||||
<div className="tree-item">
|
||||
Logs:
|
||||
{logs.map((e) => <LogView log={e} />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
ReactDOM.render(<Logs />, document.getElementById("container")!);
|
||||
});
|
@ -137,12 +137,12 @@ function AuditorDetailsView(props: {
|
||||
</p>
|
||||
);
|
||||
}
|
||||
if (rci.exchangeInfo.auditors.length === 0) {
|
||||
if ((rci.exchangeInfo.details?.auditors ?? []).length === 0) {
|
||||
return <p>The exchange is not audited by any auditors.</p>;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
{rci.exchangeInfo.auditors.map(a => (
|
||||
{(rci.exchangeInfo.details?.auditors ?? []).map(a => (
|
||||
<div>
|
||||
<h3>Auditor {a.auditor_url}</h3>
|
||||
<p>
|
||||
@ -231,7 +231,7 @@ function FeeDetailsView(props: {
|
||||
<div>
|
||||
<h3>Overview</h3>
|
||||
<p>
|
||||
Public key: <ExpanderText text={rci.exchangeInfo.masterPublicKey} />
|
||||
Public key: <ExpanderText text={rci.exchangeInfo.details?.masterPublicKey ?? "??"} />
|
||||
</p>
|
||||
<p>
|
||||
{i18n.str`Withdrawal fees:`} {withdrawFee}
|
||||
|
@ -123,13 +123,6 @@ export function getCurrencies(): Promise<CurrencyRecord[]> {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get information about a specific currency.
|
||||
*/
|
||||
export function getCurrency(name: string): Promise<CurrencyRecord|null> {
|
||||
return callBackend("currency-info", {name});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get information about a specific exchange.
|
||||
@ -225,12 +218,6 @@ export function submitPay(contractTermsHash: string, sessionId: string | undefin
|
||||
return callBackend("submit-pay", { contractTermsHash, sessionId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a contract. Throws if its not a valid contract.
|
||||
*/
|
||||
export function hashContract(contract: object): Promise<string> {
|
||||
return callBackend("hash-contract", { contract });
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a reserve as confirmed.
|
||||
@ -284,25 +271,6 @@ export function returnCoins(args: { amount: AmountJson, exchange: string, sender
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Record an error report and display it in a tabl.
|
||||
*
|
||||
* If sameTab is set, the error report will be opened in the current tab,
|
||||
* otherwise in a new tab.
|
||||
*/
|
||||
export function logAndDisplayError(args: any): Promise<void> {
|
||||
return callBackend("log-and-display-error", args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an error report from the logging database for the
|
||||
* given report UID.
|
||||
*/
|
||||
export function getReport(reportUid: string): Promise<any> {
|
||||
return callBackend("get-report", { reportUid });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Look up a purchase in the wallet database from
|
||||
* the contract terms hash.
|
||||
|
@ -24,7 +24,6 @@
|
||||
* Imports.
|
||||
*/
|
||||
import { BrowserHttpLib } from "../http";
|
||||
import * as logging from "../logging";
|
||||
import { AmountJson } from "../amounts";
|
||||
import {
|
||||
ConfirmReserveRequest,
|
||||
@ -138,22 +137,6 @@ async function handleMessage(
|
||||
}
|
||||
return needsWallet().updateExchangeFromUrl(detail.baseUrl);
|
||||
}
|
||||
case "currency-info": {
|
||||
if (!detail.name) {
|
||||
return Promise.resolve({ error: "name missing" });
|
||||
}
|
||||
return needsWallet().getCurrencyRecord(detail.name);
|
||||
}
|
||||
case "hash-contract": {
|
||||
if (!detail.contract) {
|
||||
return Promise.resolve({ error: "contract missing" });
|
||||
}
|
||||
return needsWallet()
|
||||
.hashContract(detail.contract)
|
||||
.then(hash => {
|
||||
return hash;
|
||||
});
|
||||
}
|
||||
case "reserve-creation-info": {
|
||||
if (!detail.baseUrl || typeof detail.baseUrl !== "string") {
|
||||
return Promise.resolve({ error: "bad url" });
|
||||
@ -243,20 +226,6 @@ async function handleMessage(
|
||||
};
|
||||
return resp;
|
||||
}
|
||||
case "log-and-display-error":
|
||||
logging.storeReport(detail).then(reportUid => {
|
||||
const url = chrome.extension.getURL(
|
||||
`/src/webex/pages/error.html?reportUid=${reportUid}`,
|
||||
);
|
||||
if (detail.sameTab && sender && sender.tab && sender.tab.id) {
|
||||
chrome.tabs.update(detail.tabId, { url });
|
||||
} else {
|
||||
chrome.tabs.create({ url });
|
||||
}
|
||||
});
|
||||
return;
|
||||
case "get-report":
|
||||
return logging.getReport(detail.reportUid);
|
||||
case "get-purchase-details": {
|
||||
const contractTermsHash = detail.contractTermsHash;
|
||||
if (!contractTermsHash) {
|
||||
@ -574,17 +543,6 @@ export async function wxMain() {
|
||||
chrome.runtime.reload();
|
||||
});
|
||||
|
||||
window.onerror = (m, source, lineno, colno, error) => {
|
||||
logging.record(
|
||||
"error",
|
||||
"".concat(m as any, error as any),
|
||||
undefined,
|
||||
source || "(unknown)",
|
||||
lineno || 0,
|
||||
colno || 0,
|
||||
);
|
||||
};
|
||||
|
||||
chrome.tabs.query({}, tabs => {
|
||||
console.log("got tabs", tabs);
|
||||
for (const tab of tabs) {
|
||||
|
@ -53,7 +53,6 @@
|
||||
"src/index.ts",
|
||||
"src/libtoolVersion-test.ts",
|
||||
"src/libtoolVersion.ts",
|
||||
"src/logging.ts",
|
||||
"src/promiseUtils.ts",
|
||||
"src/query.ts",
|
||||
"src/talerTypes.ts",
|
||||
@ -72,8 +71,6 @@
|
||||
"src/webex/pages/add-auditor.tsx",
|
||||
"src/webex/pages/auditors.tsx",
|
||||
"src/webex/pages/benchmark.tsx",
|
||||
"src/webex/pages/error.tsx",
|
||||
"src/webex/pages/logs.tsx",
|
||||
"src/webex/pages/pay.tsx",
|
||||
"src/webex/pages/payback.tsx",
|
||||
"src/webex/pages/popup.tsx",
|
||||
|
@ -80,8 +80,6 @@ module.exports = function (env) {
|
||||
"pay": "./src/webex/pages/pay.tsx",
|
||||
"withdraw": "./src/webex/pages/withdraw.tsx",
|
||||
"welcome": "./src/webex/pages/welcome.tsx",
|
||||
"error": "./src/webex/pages/error.tsx",
|
||||
"logs": "./src/webex/pages/logs.tsx",
|
||||
"payback": "./src/webex/pages/payback.tsx",
|
||||
"popup": "./src/webex/pages/popup.tsx",
|
||||
"reset-required": "./src/webex/pages/reset-required.tsx",
|
||||
|
@ -3378,10 +3378,10 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
idb-bridge@^0.0.10:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.10.tgz#69d59550dc722f6bf62cb98a4d4a2f2b9e66653c"
|
||||
integrity sha512-AE8YipDGjnS+APSlupcPNCNslCAErRwfUgJ8aNZigmVIr3xzI0YPGWr6NC6UI6ofpQ1DczUlaKqA86m1R/COGQ==
|
||||
idb-bridge@^0.0.11:
|
||||
version "0.0.11"
|
||||
resolved "https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.11.tgz#ba2fbd24b7e6f7f4de8333ed12b0912e64dda308"
|
||||
integrity sha512-fLlHce/WwT6eD3sc54gsfvM5fZqrhAPwBNH4uU/y6D0C1+0higH7OgC5/wploMhkmNYkQID3BMNZvSUBr0leSQ==
|
||||
|
||||
ieee754@^1.1.4:
|
||||
version "1.1.13"
|
||||
|
Loading…
Reference in New Issue
Block a user