You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1260 lines
39 KiB
1260 lines
39 KiB
<template>
|
|
<QuickNav />
|
|
|
|
<!-- CONTENT -->
|
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
|
<!-- Breadcrumb -->
|
|
<div class="mb-8">
|
|
<!-- Back -->
|
|
<div class="text-lg text-center font-light relative px-7">
|
|
<h1
|
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
|
@click="$router.back()"
|
|
>
|
|
<font-awesome icon="chevron-left" class="fa-fw"></font-awesome>
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Heading -->
|
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
|
Test
|
|
</h1>
|
|
</div>
|
|
|
|
<div v-if="isNotProdServer">
|
|
<h2 class="text-xl font-bold mb-4">User Registration</h2>
|
|
<button :class="primaryButtonClasses" @click="registerMe()">
|
|
Register Yourself
|
|
</button>
|
|
<button :class="primaryButtonClasses" @click="becomeUser0()">
|
|
Become User 0 (who can register others)
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Notiwind Alerts</h2>
|
|
|
|
<!-- Notification test buttons using computed configuration -->
|
|
<button
|
|
v-for="config in notificationTestButtons"
|
|
:key="config.label"
|
|
:class="config.classes"
|
|
@click="triggerTestNotification(config)"
|
|
>
|
|
{{ config.label }}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">SQL Operations</h2>
|
|
<div class="flex gap-2 mt-2">
|
|
<button :class="sqlLinkClasses" @click="setAllTablesQuery">
|
|
All Tables
|
|
</button>
|
|
<button :class="sqlLinkClasses" @click="setAccountsQuery">
|
|
Accounts
|
|
</button>
|
|
<button :class="sqlLinkClasses" @click="setContactsQuery">
|
|
Contacts
|
|
</button>
|
|
<button :class="sqlLinkClasses" @click="setSettingsQuery">
|
|
Settings
|
|
</button>
|
|
</div>
|
|
<div>
|
|
<textarea
|
|
v-model="sqlQuery"
|
|
class="w-full h-32 p-2 border border-gray-300 rounded-md font-mono"
|
|
placeholder="Enter your SQL query here..."
|
|
></textarea>
|
|
</div>
|
|
<div class="mt-4">
|
|
<button :class="primaryButtonClasses" @click="executeSql">
|
|
Execute
|
|
</button>
|
|
</div>
|
|
<div v-if="sqlResult" class="mt-4">
|
|
<h3 class="text-lg font-semibold mb-2">Result:</h3>
|
|
<pre class="bg-gray-100 p-4 rounded-md overflow-x-auto">{{
|
|
JSON.stringify(sqlResult, null, 2)
|
|
}}</pre>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Image Sharing</h2>
|
|
Populates the "shared-photo" view as if they used "share_target".
|
|
<input type="file" data-testId="fileInput" @change="uploadFile" />
|
|
<router-link
|
|
v-if="showFileNextStep()"
|
|
:to="{
|
|
name: 'shared-photo',
|
|
query: { fileName },
|
|
}"
|
|
class="block w-full text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2"
|
|
data-testId="fileUploadButton"
|
|
>
|
|
Go to Shared Page
|
|
</router-link>
|
|
</div>
|
|
|
|
<!-- URL Flow Testing Section -->
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">URL Flow Testing</h2>
|
|
<p class="text-sm text-gray-600 mb-3">
|
|
Test claim and partner server URL flow from initialization to change
|
|
propagation.
|
|
</p>
|
|
|
|
<div class="space-y-4">
|
|
<div class="p-4 border border-gray-300 rounded-md bg-gray-50">
|
|
<h3 class="font-semibold mb-2">Current URL State</h3>
|
|
<div class="space-y-2 text-sm">
|
|
<div>
|
|
<strong>API Server:</strong>
|
|
<span class="font-mono">{{ apiServer || "Not Set" }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Partner API Server:</strong>
|
|
<span class="font-mono">{{ partnerApiServer || "Not Set" }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Active DID:</strong>
|
|
<span class="font-mono">{{ activeDid || "Not Set" }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Platform:</strong>
|
|
<span class="font-mono">{{ getCurrentPlatform() }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
:disabled="isUrlTestRunning"
|
|
@click="testUrlFlow()"
|
|
>
|
|
{{ isUrlTestRunning ? "Testing..." : "Test URL Flow" }}
|
|
</button>
|
|
|
|
<button :class="secondaryButtonClasses" @click="changeApiServer()">
|
|
Change API Server (Test → Prod)
|
|
</button>
|
|
|
|
<button
|
|
:class="secondaryButtonClasses"
|
|
@click="changePartnerApiServer()"
|
|
>
|
|
Change Partner API Server (Test → Prod)
|
|
</button>
|
|
|
|
<button :class="warningButtonClasses" @click="resetToDefaults()">
|
|
Reset to Defaults
|
|
</button>
|
|
|
|
<button :class="secondaryButtonClasses" @click="refreshSettings()">
|
|
Refresh Settings
|
|
</button>
|
|
|
|
<button
|
|
:class="secondaryButtonClasses"
|
|
@click="logEnvironmentState()"
|
|
>
|
|
Log Environment State
|
|
</button>
|
|
</div>
|
|
|
|
<div class="p-4 border border-gray-300 rounded-md bg-gray-50">
|
|
<h3 class="font-semibold mb-2">URL Flow Test Results</h3>
|
|
<div class="max-h-64 overflow-y-auto space-y-2">
|
|
<div
|
|
v-for="(result, index) in urlTestResults"
|
|
:key="index"
|
|
class="p-2 border border-gray-200 rounded text-xs font-mono bg-white"
|
|
>
|
|
{{ result }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Passkeys</h2>
|
|
See console for results.
|
|
<br />
|
|
See existing passkeys in Chrome at: chrome://settings/passkeys
|
|
<br />
|
|
Active DID: {{ activeDIDDisplay }}
|
|
{{ passkeyStatusDisplay }}
|
|
|
|
<div>
|
|
Register Passkey
|
|
<button :class="primaryButtonClasses" @click="registerPasskey()">
|
|
Simplewebauthn
|
|
</button>
|
|
</div>
|
|
|
|
<div>
|
|
Create JWT
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="createJwtSimplewebauthn()"
|
|
>
|
|
Simplewebauthn
|
|
</button>
|
|
<button :class="primaryButtonClasses" @click="createJwtNavigator()">
|
|
Navigator
|
|
</button>
|
|
</div>
|
|
|
|
<div v-if="jwt">
|
|
Verify New JWT
|
|
<button :class="primaryButtonClasses" @click="verifySimplewebauthn()">
|
|
Simplewebauthn
|
|
</button>
|
|
<button :class="primaryButtonClasses" @click="verifyWebCrypto()">
|
|
WebCrypto
|
|
</button>
|
|
<button :class="primaryButtonClasses" @click="verifyP256()">
|
|
p256 - broken
|
|
</button>
|
|
</div>
|
|
<div v-else>Verify New JWT -- requires creation first</div>
|
|
<button :class="primaryButtonClasses" @click="verifyMyJwt()">
|
|
Verify Hard-Coded JWT
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Encryption & Decryption</h2>
|
|
See console for more output.
|
|
<div>
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="testMessageEncryptionDecryption()"
|
|
>
|
|
Run Test for Message Encryption/Decryption
|
|
</button>
|
|
{{ encryptionTestResultDisplay }}
|
|
</div>
|
|
<div>
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="testSimpleEncryptionDecryption()"
|
|
>
|
|
Run Test for Simple Encryption/Decryption
|
|
</button>
|
|
{{ simpleEncryptionTestResultDisplay }}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Component Tests</h2>
|
|
Interactive tests for Vue components and their functionality.
|
|
|
|
<div class="mt-4">
|
|
<h3 class="text-lg font-semibold mb-2">EntityGrid Function Props</h3>
|
|
<p class="text-sm text-gray-600 mb-3">
|
|
Test the new function prop functionality in EntityGrid component.
|
|
</p>
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="showEntityGridTest = !showEntityGridTest"
|
|
>
|
|
{{ showEntityGridTest ? "Hide" : "Show" }} EntityGrid Function Prop
|
|
Test
|
|
</button>
|
|
|
|
<div
|
|
v-if="showEntityGridTest"
|
|
class="mt-4 p-4 border border-gray-300 rounded-md bg-gray-50"
|
|
>
|
|
<EntityGridFunctionPropTest />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<h3 class="text-lg font-semibold mb-2">Platform Service Mixin</h3>
|
|
<p class="text-sm text-gray-600 mb-3">
|
|
Test database operations through PlatformServiceMixin.
|
|
</p>
|
|
<button
|
|
:class="primaryButtonClasses"
|
|
@click="showPlatformServiceTest = !showPlatformServiceTest"
|
|
>
|
|
{{ showPlatformServiceTest ? "Hide" : "Show" }} Platform Service Test
|
|
</button>
|
|
|
|
<div
|
|
v-if="showPlatformServiceTest"
|
|
class="mt-4 p-4 border border-gray-300 rounded-md bg-gray-50"
|
|
>
|
|
<PlatformServiceMixinTest />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Buffer } from "buffer/";
|
|
import { Base64URLString } from "@simplewebauthn/types";
|
|
import { ref } from "vue";
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
import { Router } from "vue-router";
|
|
|
|
import QuickNav from "../components/QuickNav.vue";
|
|
import { AppString, NotificationIface } from "../constants/app";
|
|
import {
|
|
NOTIFY_SQL_ERROR,
|
|
createSqlErrorMessage,
|
|
createPasskeyNameModal,
|
|
} from "../constants/notifications";
|
|
import * as vcLib from "../libs/crypto/vc";
|
|
import * as cryptoLib from "../libs/crypto";
|
|
|
|
import {
|
|
PeerSetup,
|
|
verifyJwtP256,
|
|
verifyJwtSimplewebauthn,
|
|
verifyJwtWebCrypto,
|
|
} from "../libs/crypto/vc/passkeyDidPeer";
|
|
import {
|
|
blobToBase64,
|
|
retrieveAccountMetadata,
|
|
registerAndSavePasskey,
|
|
SHARED_PHOTO_BASE64_KEY,
|
|
} from "../libs/util";
|
|
import { testBecomeUser0, testServerRegisterUser } from "@/test";
|
|
import { logger } from "../utils/logger";
|
|
import { Account } from "../db/tables/accounts";
|
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|
import EntityGridFunctionPropTest from "../test/EntityGridFunctionPropTest.vue";
|
|
import PlatformServiceMixinTest from "../test/PlatformServiceMixinTest.vue";
|
|
|
|
const inputFileNameRef = ref<Blob>();
|
|
|
|
const TEST_PAYLOAD = {
|
|
vc: {
|
|
credentialSubject: {
|
|
"@context": "https://schema.org",
|
|
"@type": "GiveAction",
|
|
description: "pizza",
|
|
},
|
|
},
|
|
};
|
|
|
|
/**
|
|
* TestView Component
|
|
*
|
|
* Development/testing interface providing comprehensive testing tools for:
|
|
* - Notification system testing (8 different types)
|
|
* - Interactive SQL operations and database queries
|
|
* - File upload and image sharing functionality
|
|
* - Passkey registration and JWT verification
|
|
* - Encryption/decryption testing
|
|
* - Various crypto operations
|
|
*
|
|
* Features:
|
|
* - Raw SQL query execution interface for database testing
|
|
* - Notification type demonstrations
|
|
* - Passkey and JWT verification workflows
|
|
* - File upload with temporary storage
|
|
* - Crypto library testing utilities
|
|
*
|
|
* Security Considerations:
|
|
* - Test environment only - not for production use
|
|
* - SQL operations are intentionally raw for testing purposes
|
|
* - File uploads stored temporarily for testing workflows
|
|
*
|
|
* @author Matthew Raymer
|
|
*/
|
|
@Component({
|
|
components: {
|
|
QuickNav,
|
|
EntityGridFunctionPropTest,
|
|
PlatformServiceMixinTest,
|
|
},
|
|
mixins: [PlatformServiceMixin],
|
|
})
|
|
export default class Help extends Vue {
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
|
$router!: Router;
|
|
|
|
// for encryption/decryption
|
|
messageEncryptionTestResult?: boolean;
|
|
simpleEncryptionTestResult?: boolean;
|
|
|
|
// for file import
|
|
fileName?: string;
|
|
|
|
// for passkeys
|
|
credIdHex?: string;
|
|
activeDid?: string;
|
|
apiServer?: string;
|
|
jwt?: string;
|
|
peerSetup?: PeerSetup;
|
|
userName?: string;
|
|
|
|
// for SQL operations
|
|
sqlQuery = "";
|
|
sqlResult: unknown = null;
|
|
|
|
cryptoLib = cryptoLib;
|
|
|
|
// for component tests
|
|
showEntityGridTest = false;
|
|
showPlatformServiceTest = false;
|
|
|
|
// for URL flow testing
|
|
isUrlTestRunning = false;
|
|
urlTestResults: string[] = [];
|
|
partnerApiServer: string | undefined;
|
|
|
|
/**
|
|
* Computed properties for template streamlining
|
|
* Eliminates repeated classes and logic in template
|
|
*/
|
|
|
|
/**
|
|
* Standard button class for primary actions
|
|
*/
|
|
get primaryButtonClasses(): string {
|
|
return "font-bold capitalize bg-slate-500 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Dark button class for primary test actions
|
|
*/
|
|
get darkButtonClasses(): string {
|
|
return "font-bold capitalize bg-slate-900 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Secondary button class for secondary test actions
|
|
*/
|
|
get secondaryButtonClasses(): string {
|
|
return "font-bold capitalize bg-slate-600 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Success button class for success notifications
|
|
*/
|
|
get successButtonClasses(): string {
|
|
return "font-bold capitalize bg-emerald-600 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Warning button class for warning notifications
|
|
*/
|
|
get warningButtonClasses(): string {
|
|
return "font-bold capitalize bg-amber-600 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* Danger button class for danger notifications
|
|
*/
|
|
get dangerButtonClasses(): string {
|
|
return "font-bold capitalize bg-rose-600 text-white px-3 py-2 rounded-md mr-2";
|
|
}
|
|
|
|
/**
|
|
* SQL link button class for inline SQL query buttons
|
|
*/
|
|
get sqlLinkClasses(): string {
|
|
return "text-sm text-blue-600 hover:text-blue-800 underline";
|
|
}
|
|
|
|
/**
|
|
* Formatted display of active DID status
|
|
*/
|
|
get activeDIDDisplay(): string {
|
|
return this.activeDid || "nothing, which";
|
|
}
|
|
|
|
/**
|
|
* Formatted display of passkey status
|
|
*/
|
|
get passkeyStatusDisplay(): string {
|
|
return this.credIdHex ? "has a passkey ID" : "has no passkey ID";
|
|
}
|
|
|
|
/**
|
|
* Formatted display of encryption test result
|
|
*/
|
|
get encryptionTestResultDisplay(): string {
|
|
return this.messageEncryptionTestResult !== undefined
|
|
? `Result: ${this.messageEncryptionTestResult}`
|
|
: "Result: Not tested";
|
|
}
|
|
|
|
/**
|
|
* Formatted display of simple encryption test result
|
|
*/
|
|
get simpleEncryptionTestResultDisplay(): string {
|
|
return this.simpleEncryptionTestResult !== undefined
|
|
? `Result: ${this.simpleEncryptionTestResult}`
|
|
: "Result: Not tested";
|
|
}
|
|
|
|
/**
|
|
* SQL query presets for template buttons
|
|
* Extracts inline SQL assignments from template for better organization
|
|
*/
|
|
setAllTablesQuery() {
|
|
this.sqlQuery = "SELECT * FROM sqlite_master WHERE type='table';";
|
|
this.executeSql();
|
|
}
|
|
|
|
setAccountsQuery() {
|
|
this.sqlQuery = "SELECT * FROM accounts;";
|
|
this.executeSql();
|
|
}
|
|
|
|
setContactsQuery() {
|
|
this.sqlQuery = "SELECT * FROM contacts;";
|
|
this.executeSql();
|
|
}
|
|
|
|
setSettingsQuery() {
|
|
this.sqlQuery = "SELECT * FROM settings;";
|
|
this.executeSql();
|
|
}
|
|
|
|
/**
|
|
* Configuration for notification test buttons
|
|
* Eliminates repetitive notification button definitions in template
|
|
*/
|
|
get notificationTestButtons() {
|
|
return [
|
|
{
|
|
label: "Toast",
|
|
classes: this.darkButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "toast",
|
|
title: "Toast",
|
|
text: "I'm a toast. Without a timeout, I'm stuck.",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Info",
|
|
classes: this.secondaryButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "info",
|
|
title: "Information Alert",
|
|
text: "Just wanted you to know.",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Success",
|
|
classes: this.successButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "success",
|
|
title: "Success Alert",
|
|
text: "Congratulations!",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Warning",
|
|
classes: this.warningButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "warning",
|
|
title: "Warning Alert",
|
|
text: "You might wanna look at this.",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Danger",
|
|
classes: this.dangerButtonClasses,
|
|
notification: {
|
|
group: "alert",
|
|
type: "danger",
|
|
title: "Danger Alert",
|
|
text: "Something terrible has happened!",
|
|
},
|
|
timeout: 5000,
|
|
},
|
|
{
|
|
label: "Notif ON",
|
|
classes: this.secondaryButtonClasses,
|
|
notification: {
|
|
group: "modal",
|
|
type: "notification-permission",
|
|
title: "Notification Permission",
|
|
text: "Enable notifications?",
|
|
},
|
|
timeout: -1,
|
|
},
|
|
{
|
|
label: "Notif MUTE",
|
|
classes: this.secondaryButtonClasses,
|
|
notification: {
|
|
group: "modal",
|
|
type: "notification-mute",
|
|
title: "Notification Settings",
|
|
text: "Notifications muted",
|
|
},
|
|
timeout: -1,
|
|
},
|
|
{
|
|
label: "Notif OFF",
|
|
classes: this.secondaryButtonClasses,
|
|
notification: {
|
|
group: "modal",
|
|
type: "notification-off",
|
|
title: "Notifications",
|
|
text: "Notifications turned off",
|
|
},
|
|
timeout: -1,
|
|
},
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Loads user settings and account information for testing interface
|
|
* Uses PlatformServiceMixin for database access
|
|
*/
|
|
async mounted() {
|
|
logger.info(
|
|
"[TestView] 🚀 Component mounting - starting URL flow tracking",
|
|
);
|
|
|
|
// Boot-time logging for initial configuration
|
|
logger.info("[TestView] 🌍 Boot-time configuration detected:", {
|
|
platform: process.env.VITE_PLATFORM,
|
|
defaultEndorserApiServer: process.env.VITE_DEFAULT_ENDORSER_API_SERVER,
|
|
defaultPartnerApiServer: process.env.VITE_DEFAULT_PARTNER_API_SERVER,
|
|
nodeEnv: process.env.NODE_ENV,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
|
|
try {
|
|
// Track settings loading
|
|
logger.info("[TestView] 📥 Loading account settings...");
|
|
const settings = await this.$accountSettings();
|
|
|
|
logger.info("[TestView] 📊 Settings loaded:", {
|
|
activeDid: settings.activeDid,
|
|
apiServer: settings.apiServer,
|
|
partnerApiServer: settings.partnerApiServer,
|
|
isRegistered: settings.isRegistered,
|
|
firstName: settings.firstName,
|
|
});
|
|
|
|
// Update component state
|
|
|
|
// Get activeDid from active_identity table (single source of truth)
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const activeIdentity = await (this as any).$getActiveIdentity();
|
|
this.activeDid = activeIdentity.activeDid || "";
|
|
|
|
this.apiServer = settings.apiServer || "";
|
|
this.partnerApiServer = settings.partnerApiServer || "";
|
|
this.userName = settings.firstName;
|
|
|
|
logger.info("[TestView] ✅ Component state updated:", {
|
|
activeDid: this.activeDid,
|
|
apiServer: this.apiServer,
|
|
partnerApiServer: this.partnerApiServer,
|
|
});
|
|
|
|
// Load account metadata
|
|
if (this.activeDid) {
|
|
logger.info(
|
|
"[TestView] 🔍 Loading account metadata for DID:",
|
|
this.activeDid,
|
|
);
|
|
const account = await retrieveAccountMetadata(this.activeDid);
|
|
|
|
if (account) {
|
|
this.credIdHex = account.passkeyCredIdHex as string;
|
|
logger.info("[TestView] ✅ Account metadata loaded:", {
|
|
did: account.did,
|
|
hasPasskey: !!account.passkeyCredIdHex,
|
|
passkeyId: account.passkeyCredIdHex,
|
|
});
|
|
} else {
|
|
logger.warn(
|
|
"[TestView] ⚠️ No account found for DID:",
|
|
this.activeDid,
|
|
);
|
|
alert("No account found for DID " + this.activeDid);
|
|
}
|
|
}
|
|
|
|
logger.info("[TestView] 🎯 Component initialization complete:", {
|
|
activeDid: this.activeDid,
|
|
apiServer: this.apiServer,
|
|
partnerApiServer: this.partnerApiServer,
|
|
hasPasskey: !!this.credIdHex,
|
|
platform: this.getCurrentPlatform(),
|
|
});
|
|
} catch (error) {
|
|
logger.error(
|
|
"[TestView] ❌ Error during component initialization:",
|
|
error,
|
|
);
|
|
this.$notify(
|
|
{
|
|
group: "error",
|
|
type: "error",
|
|
title: "Initialization Error",
|
|
text: `Failed to initialize component: ${error instanceof Error ? error.message : String(error)}`,
|
|
},
|
|
5000,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if running on production server
|
|
*
|
|
* @returns True if not on production server (enables test utilities)
|
|
*/
|
|
public isNotProdServer() {
|
|
return this.apiServer !== AppString.PROD_ENDORSER_API_SERVER;
|
|
}
|
|
|
|
async registerMe() {
|
|
const response = await testServerRegisterUser();
|
|
if (response.status === 201) {
|
|
alert("Registration successful.");
|
|
this.$router.push({ name: "home" }); // because this page checks for registered status and sets things if it detects a change
|
|
} else {
|
|
logger.error("Registration failure response:", response);
|
|
alert("Registration failed: " + (response.data.error || response.data));
|
|
}
|
|
}
|
|
|
|
async becomeUser0() {
|
|
await testBecomeUser0();
|
|
alert("You are now User 0.");
|
|
this.$router.push({ name: "home" }); // because this page checks for registered status and sets things if it detects a change
|
|
}
|
|
|
|
/**
|
|
* Method to trigger notification test
|
|
* Centralizes notification testing logic
|
|
*/
|
|
triggerTestNotification(config: {
|
|
notification: NotificationIface;
|
|
timeout?: number;
|
|
}) {
|
|
this.$notify(config.notification, config.timeout);
|
|
}
|
|
|
|
/**
|
|
* Handles file upload for image sharing tests
|
|
*
|
|
* Processes uploaded files and stores them in temp table for shared photo testing
|
|
* Uses PlatformServiceMixin service methods for temp table operations
|
|
*/
|
|
async uploadFile(event: Event) {
|
|
const target = event.target as HTMLInputElement;
|
|
inputFileNameRef.value = target.files?.[0];
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/File
|
|
// ... plus it has a `type` property from my testing
|
|
const file = inputFileNameRef.value;
|
|
if (file != null) {
|
|
const reader = new FileReader();
|
|
reader.onload = async (e) => {
|
|
const data = e.target?.result as ArrayBuffer;
|
|
if (data) {
|
|
const blob = new Blob([new Uint8Array(data)], {
|
|
type: file.type,
|
|
});
|
|
const blobB64 = await blobToBase64(blob);
|
|
this.fileName = (file as File).name;
|
|
|
|
// Use service methods for temp table operations
|
|
const temp = await this.$getTemp(SHARED_PHOTO_BASE64_KEY);
|
|
if (temp) {
|
|
await this.$updateEntity("temp", { blobB64 }, "id = ?", [
|
|
SHARED_PHOTO_BASE64_KEY,
|
|
]);
|
|
} else {
|
|
await this.$insertEntity(
|
|
"temp",
|
|
{ id: SHARED_PHOTO_BASE64_KEY, blobB64 },
|
|
["id", "blobB64"],
|
|
);
|
|
}
|
|
}
|
|
};
|
|
reader.readAsArrayBuffer(file as Blob);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if file upload next step should be shown
|
|
*/
|
|
showFileNextStep() {
|
|
return !!inputFileNameRef.value;
|
|
}
|
|
|
|
/**
|
|
* Handles passkey registration for testing
|
|
*
|
|
* Creates new passkey with user name or default test name
|
|
* Includes validation and user confirmation workflow
|
|
* Uses notification helpers for consistent messaging
|
|
*/
|
|
public async registerPasskey() {
|
|
const DEFAULT_USERNAME = AppString.APP_NAME + " Tester";
|
|
if (!this.userName) {
|
|
const modalConfig = createPasskeyNameModal(
|
|
DEFAULT_USERNAME,
|
|
async () => {
|
|
this.userName = DEFAULT_USERNAME;
|
|
},
|
|
async () => {
|
|
this.$router.push({ name: "new-edit-account" });
|
|
},
|
|
);
|
|
|
|
this.$notify(modalConfig, -1);
|
|
return;
|
|
}
|
|
const account = await registerAndSavePasskey(
|
|
AppString.APP_NAME + " - " + this.userName,
|
|
);
|
|
this.activeDid = account.did;
|
|
this.credIdHex = account.passkeyCredIdHex;
|
|
}
|
|
|
|
/**
|
|
* Tests message encryption/decryption functionality
|
|
*/
|
|
public async testMessageEncryptionDecryption() {
|
|
this.messageEncryptionTestResult =
|
|
await cryptoLib.testMessageEncryptionDecryption();
|
|
}
|
|
|
|
/**
|
|
* Tests simple encryption/decryption functionality
|
|
*/
|
|
public async testSimpleEncryptionDecryption() {
|
|
this.simpleEncryptionTestResult =
|
|
await cryptoLib.testSimpleEncryptionDecryption();
|
|
}
|
|
|
|
/**
|
|
* Creates JWT using SimpleWebAuthn for testing
|
|
*/
|
|
public async createJwtSimplewebauthn() {
|
|
const account: Account | undefined = await retrieveAccountMetadata(
|
|
this.activeDid || "",
|
|
);
|
|
if (!vcLib.isFromPasskey(account)) {
|
|
alert(`The DID ${this.activeDid} is not passkey-enabled.`);
|
|
return;
|
|
}
|
|
this.peerSetup = new PeerSetup();
|
|
this.jwt = await this.peerSetup.createJwtSimplewebauthn(
|
|
this.activeDid as string,
|
|
TEST_PAYLOAD,
|
|
this.credIdHex as string,
|
|
);
|
|
logger.log("simple jwt4url", this.jwt);
|
|
}
|
|
|
|
/**
|
|
* Creates JWT using Navigator API for testing
|
|
*/
|
|
public async createJwtNavigator() {
|
|
const account: Account | undefined = await retrieveAccountMetadata(
|
|
this.activeDid || "",
|
|
);
|
|
if (!vcLib.isFromPasskey(account)) {
|
|
alert(`The DID ${this.activeDid} is not passkey-enabled.`);
|
|
return;
|
|
}
|
|
this.peerSetup = new PeerSetup();
|
|
this.jwt = await this.peerSetup.createJwtNavigator(
|
|
this.activeDid as string,
|
|
TEST_PAYLOAD,
|
|
this.credIdHex as string,
|
|
);
|
|
logger.log("lower jwt4url", this.jwt);
|
|
}
|
|
|
|
/**
|
|
* Verifies JWT using P256 algorithm for testing
|
|
*/
|
|
public async verifyP256() {
|
|
const decoded = await verifyJwtP256(
|
|
this.activeDid as string,
|
|
this.peerSetup?.authenticatorData as ArrayBuffer,
|
|
this.peerSetup?.challenge as Uint8Array,
|
|
this.peerSetup?.signature as Base64URLString,
|
|
);
|
|
logger.log("decoded", decoded);
|
|
}
|
|
|
|
/**
|
|
* Verifies JWT using SimpleWebAuthn for testing
|
|
*/
|
|
public async verifySimplewebauthn() {
|
|
const decoded = await verifyJwtSimplewebauthn(
|
|
this.credIdHex as string,
|
|
this.activeDid as string,
|
|
this.peerSetup?.authenticatorData as ArrayBuffer,
|
|
this.peerSetup?.challenge as Uint8Array,
|
|
this.peerSetup?.clientDataJsonBase64Url as Base64URLString,
|
|
this.peerSetup?.signature as Base64URLString,
|
|
);
|
|
logger.log("decoded", decoded);
|
|
}
|
|
|
|
/**
|
|
* Verifies JWT using WebCrypto for testing
|
|
*/
|
|
public async verifyWebCrypto() {
|
|
const decoded = await verifyJwtWebCrypto(
|
|
this.activeDid as string,
|
|
this.peerSetup?.authenticatorData as ArrayBuffer,
|
|
this.peerSetup?.challenge as Uint8Array,
|
|
this.peerSetup?.signature as Base64URLString,
|
|
);
|
|
logger.log("decoded", decoded);
|
|
}
|
|
|
|
/**
|
|
* Verifies hard-coded JWT for testing purposes
|
|
*/
|
|
public async verifyMyJwt() {
|
|
const did =
|
|
"did:peer:0zKMFjvUgYrM1hXwDciYHiA9MxXtJPXnRLJvqoMNAKoDLX9pKMWLb3VDsgua1p2zW1xXRsjZSTNsfvMnNyMS7dB4k7NAhFwL3pXBrBXgyYJ9ri";
|
|
const jwt =
|
|
"eyJ0eXAiOiJKV0FOVCIsImFsZyI6IkVTMjU2In0.eyJBdXRoZW50aWNhdGlvbkRhdGFCNjRVUkwiOiJTWllONVlnT2pHaDBOQmNQWkhaZ1c0X2tycm1paGpMSG1Wenp1b01kbDJNRkFBQUFBQSIsIkNsaWVudERhdGFKU09OQjY0VVJMIjoiZXlKMGVYQmxJam9pZDJWaVlYVjBhRzR1WjJWMElpd2lZMmhoYkd4bGJtZGxJam9pWlhsS01sbDVTVFpsZVVwcVkyMVdhMXBYTlRCaFYwWnpWVE5XYVdGdFZtcGtRMGsyWlhsS1FWa3lPWFZrUjFZMFpFTkpOa2x0YURCa1NFSjZUMms0ZG1NeVRtOWFWekZvVEcwNWVWcDVTWE5KYTBJd1pWaENiRWxxYjJsU01td3lXbFZHYW1SSGJIWmlhVWx6U1cxU2JHTXlUbmxoV0VJd1lWYzVkVWxxYjJsalIydzJaVzFGYVdaWU1ITkpiV3hvWkVOSk5rMVVZM2hQUkZVMFRtcHJOVTFEZDJsaFdFNTZTV3B2YVZwSGJHdFBia0pzV2xoSk5rMUljRXhVVlZweFpHeFdibGRZU2s1TlYyaFpaREJTYW1GV2JFbGhWVVUxVkZob1dXUkZjRkZYUnpWVFZFVndNbU5YT1U1VWEwWk1ZakJTVFZkRWJIZFRNREZZVkVkSmVsWnJVbnBhTTFab1RWaEJlV1ZzWTNobFJtaFRZekp3WVZVeFVrOWpNbG95VkZjMVQyVlZNVlJPTWxKRFRrZHpNMVJyUm05U2JtUk5UVE5DV1ZGdVNrTlhSMlExVjFWdk5XTnRhMmxtVVNJc0ltOXlhV2RwYmlJNkltaDBkSEE2THk5c2IyTmhiR2h2YzNRNk9EQTRNQ0lzSW1OeWIzTnpUM0pwWjJsdUlqcG1ZV3h6WlgwIiwiaWF0IjoxNzE4NTg2OTkyLCJpc3MiOiJkaWQ6cGVlcjowektNRmp2VWdZck0xaFh3RGNpWUhpQTlNeFh0SlBYblJMSnZxb01OQUtvRExYOXBLTVdMYjNWRHNndWExcDJ6VzF4WFJzalpTVE5zZnZNbk55TVM3ZEI0azdOQWhGd0wzcFhCckJYZ3lZSjlyaSJ9.MEUCIQDJyCTbMPIFnuBoW3FYnlgtDEIHZ2OrkCEvqVnHU7kJDQIgVxjBjfW1TwQfcSOYwK8Z7AdCWGJlyxtLEsrnPif7caE";
|
|
const pieces = jwt.split(".");
|
|
const payload = JSON.parse(Buffer.from(pieces[1], "base64").toString());
|
|
const authData = Buffer.from(payload["AuthenticationDataB64URL"], "base64");
|
|
const clientJSON = Buffer.from(
|
|
payload["ClientDataJSONB64URL"],
|
|
"base64",
|
|
).toString();
|
|
const clientData = JSON.parse(clientJSON);
|
|
const challenge = clientData.challenge;
|
|
const signatureB64URL = pieces[2];
|
|
const decoded = await verifyJwtWebCrypto(
|
|
did,
|
|
authData,
|
|
challenge,
|
|
signatureB64URL,
|
|
);
|
|
logger.log("decoded", decoded);
|
|
}
|
|
|
|
/**
|
|
* Executes SQL queries for testing database operations
|
|
*
|
|
* Supports both SELECT queries (dbQuery) and other SQL commands (dbExec)
|
|
* Provides interface for testing raw SQL operations
|
|
* Uses PlatformServiceMixin for database access and notification helpers for errors
|
|
*/
|
|
async executeSql() {
|
|
try {
|
|
const isSelect = this.sqlQuery.trim().toLowerCase().startsWith("select");
|
|
if (isSelect) {
|
|
this.sqlResult = await this.$query(this.sqlQuery);
|
|
} else {
|
|
this.sqlResult = await this.$exec(this.sqlQuery);
|
|
}
|
|
logger.log("Test SQL Result:", this.sqlResult);
|
|
} catch (error) {
|
|
logger.error("Test SQL Error:", error);
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "danger",
|
|
title: NOTIFY_SQL_ERROR.title,
|
|
text: createSqlErrorMessage(error),
|
|
},
|
|
5000,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests the URL flow from initialization to change propagation.
|
|
* This simulates the flow where a user's DID is set, and then the
|
|
* claim and partner server URLs are updated.
|
|
*/
|
|
public async testUrlFlow() {
|
|
this.isUrlTestRunning = true;
|
|
this.urlTestResults = [];
|
|
|
|
try {
|
|
logger.info("[TestView] 🔬 Starting comprehensive URL flow test");
|
|
this.addUrlTestResult("🚀 Starting URL flow test...");
|
|
|
|
// Test 1: Current state
|
|
this.addUrlTestResult(`📊 Current State:`);
|
|
this.addUrlTestResult(` - API Server: ${this.apiServer || "Not Set"}`);
|
|
this.addUrlTestResult(
|
|
` - Partner API Server: ${this.partnerApiServer || "Not Set"}`,
|
|
);
|
|
this.addUrlTestResult(` - Active DID: ${this.activeDid || "Not Set"}`);
|
|
this.addUrlTestResult(` - Platform: ${this.getCurrentPlatform()}`);
|
|
|
|
// Test 2: Load fresh settings
|
|
this.addUrlTestResult(`\n📥 Testing Settings Loading:`);
|
|
const startTime = Date.now();
|
|
const settings = await this.$accountSettings();
|
|
const loadTime = Date.now() - startTime;
|
|
|
|
this.addUrlTestResult(` - Settings loaded in ${loadTime}ms`);
|
|
this.addUrlTestResult(
|
|
` - API Server from settings: ${settings.apiServer || "Not Set"}`,
|
|
);
|
|
this.addUrlTestResult(
|
|
` - Partner API Server from settings: ${settings.partnerApiServer || "Not Set"}`,
|
|
);
|
|
|
|
// Test 3: Database query
|
|
this.addUrlTestResult(`\n💾 Testing Database Query:`);
|
|
const dbStartTime = Date.now();
|
|
const dbResult = await this.$dbQuery(
|
|
"SELECT apiServer, partnerApiServer, activeDid FROM settings WHERE id = ? OR accountDid = ?",
|
|
[1, this.activeDid || ""],
|
|
);
|
|
const dbTime = Date.now() - dbStartTime;
|
|
|
|
if (dbResult?.values) {
|
|
this.addUrlTestResult(` - Database query completed in ${dbTime}ms`);
|
|
this.addUrlTestResult(
|
|
` - Raw DB values: ${JSON.stringify(dbResult.values)}`,
|
|
);
|
|
} else {
|
|
this.addUrlTestResult(
|
|
` - Database query failed or returned no results`,
|
|
);
|
|
}
|
|
|
|
// Test 4: Environment variables
|
|
this.addUrlTestResult(`\n🌍 Testing Environment Variables:`);
|
|
this.addUrlTestResult(
|
|
` - VITE_PLATFORM: ${import.meta.env.VITE_PLATFORM || "Not Set"}`,
|
|
);
|
|
this.addUrlTestResult(
|
|
` - VITE_DEFAULT_ENDORSER_API_SERVER: ${import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER || "Not Set"}`,
|
|
);
|
|
this.addUrlTestResult(
|
|
` - VITE_DEFAULT_PARTNER_API_SERVER: ${import.meta.env.VITE_DEFAULT_PARTNER_API_SERVER || "Not Set"}`,
|
|
);
|
|
|
|
// Test 5: Constants
|
|
this.addUrlTestResult(`\n📋 Testing App Constants:`);
|
|
this.addUrlTestResult(
|
|
` - PROD_ENDORSER_API_SERVER: ${AppString.PROD_ENDORSER_API_SERVER}`,
|
|
);
|
|
this.addUrlTestResult(
|
|
` - PROD_PARTNER_API_SERVER: ${AppString.PROD_PARTNER_API_SERVER}`,
|
|
);
|
|
|
|
// Test 6: Change detection
|
|
this.addUrlTestResult(`\n🔄 Testing Change Detection:`);
|
|
const originalApiServer = this.apiServer;
|
|
const originalPartnerServer = this.partnerApiServer;
|
|
|
|
// Simulate a change
|
|
this.addUrlTestResult(` - Original API Server: ${originalApiServer}`);
|
|
this.addUrlTestResult(
|
|
` - Original Partner Server: ${originalPartnerServer}`,
|
|
);
|
|
|
|
// Test 7: Settings update
|
|
this.addUrlTestResult(`\n💾 Testing Settings Update:`);
|
|
const testChanges = {
|
|
apiServer:
|
|
originalApiServer === "https://api.endorser.ch"
|
|
? "https://test-api.endorser.ch"
|
|
: "https://api.endorser.ch",
|
|
};
|
|
|
|
this.addUrlTestResult(
|
|
` - Attempting to change API Server to: ${testChanges.apiServer}`,
|
|
);
|
|
const updateResult = await this.$saveSettings(testChanges);
|
|
this.addUrlTestResult(
|
|
` - Update result: ${updateResult ? "Success" : "Failed"}`,
|
|
);
|
|
|
|
// Test 8: Verify change propagation
|
|
this.addUrlTestResult(`\n✅ Testing Change Propagation:`);
|
|
const newSettings = await this.$accountSettings();
|
|
this.addUrlTestResult(
|
|
` - New API Server from settings: ${newSettings.apiServer || "Not Set"}`,
|
|
);
|
|
this.addUrlTestResult(
|
|
` - Component state API Server: ${this.apiServer || "Not Set"}`,
|
|
);
|
|
this.addUrlTestResult(
|
|
` - Change propagated: ${newSettings.apiServer === this.apiServer ? "Yes" : "No"}`,
|
|
);
|
|
|
|
// Test 9: Revert changes
|
|
this.addUrlTestResult(`\n🔄 Reverting Changes:`);
|
|
const revertResult = await this.$saveSettings({
|
|
apiServer: originalApiServer,
|
|
});
|
|
this.addUrlTestResult(
|
|
` - Revert result: ${revertResult ? "Success" : "Failed"}`,
|
|
);
|
|
|
|
// Test 10: Final verification
|
|
this.addUrlTestResult(`\n🎯 Final Verification:`);
|
|
const finalSettings = await this.$accountSettings();
|
|
this.addUrlTestResult(
|
|
` - Final API Server: ${finalSettings.apiServer || "Not Set"}`,
|
|
);
|
|
this.addUrlTestResult(
|
|
` - Matches original: ${finalSettings.apiServer === originalApiServer ? "Yes" : "No"}`,
|
|
);
|
|
|
|
this.addUrlTestResult(`\n✅ URL flow test completed successfully!`);
|
|
logger.info("[TestView] ✅ URL flow test completed successfully");
|
|
} catch (error) {
|
|
const errorMsg = `❌ URL flow test failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
this.addUrlTestResult(errorMsg);
|
|
logger.error("[TestView] ❌ URL flow test failed:", error);
|
|
} finally {
|
|
this.isUrlTestRunning = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a result to the URL test results array.
|
|
*/
|
|
private addUrlTestResult(message: string) {
|
|
this.urlTestResults.push(message);
|
|
}
|
|
|
|
/**
|
|
* Changes the API server to the production URL.
|
|
*/
|
|
public changeApiServer() {
|
|
const currentServer = this.apiServer;
|
|
const newServer =
|
|
currentServer === "https://api.endorser.ch"
|
|
? "https://test-api.endorser.ch"
|
|
: "https://api.endorser.ch";
|
|
|
|
logger.info("[TestView] 🔄 Changing API server:", {
|
|
from: currentServer,
|
|
to: newServer,
|
|
});
|
|
|
|
this.apiServer = newServer;
|
|
this.addUrlTestResult(
|
|
`API Server changed from ${currentServer} to ${newServer}`,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Changes the partner API server to the production URL.
|
|
*/
|
|
public changePartnerApiServer() {
|
|
const currentServer = this.partnerApiServer;
|
|
const newServer =
|
|
currentServer === "https://partner-api.endorser.ch"
|
|
? "https://test-partner-api.endorser.ch"
|
|
: "https://partner-api.endorser.ch";
|
|
|
|
logger.info("[TestView] 🔄 Changing partner API server:", {
|
|
from: currentServer,
|
|
to: newServer,
|
|
});
|
|
|
|
this.partnerApiServer = newServer;
|
|
this.addUrlTestResult(
|
|
`Partner API Server changed from ${currentServer} to ${newServer}`,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Resets all URL-related settings to their initial values.
|
|
*/
|
|
public resetToDefaults() {
|
|
this.apiServer = AppString.TEST_ENDORSER_API_SERVER;
|
|
this.partnerApiServer = AppString.TEST_PARTNER_API_SERVER;
|
|
this.activeDid = "";
|
|
this.addUrlTestResult("URL Flow Test Results Reset to Defaults.");
|
|
}
|
|
|
|
/**
|
|
* Refreshes settings from the database to verify changes.
|
|
*/
|
|
public async refreshSettings() {
|
|
try {
|
|
logger.info("[TestView] 🔄 Refreshing settings from database");
|
|
const settings = await this.$accountSettings();
|
|
|
|
// Update component state
|
|
this.apiServer = settings.apiServer || "";
|
|
this.partnerApiServer = settings.partnerApiServer || "";
|
|
|
|
logger.info("[TestView] ✅ Settings refreshed:", {
|
|
apiServer: this.apiServer,
|
|
partnerApiServer: this.partnerApiServer,
|
|
});
|
|
|
|
this.addUrlTestResult(
|
|
`Settings refreshed - API Server: ${this.apiServer}, Partner API Server: ${this.partnerApiServer}`,
|
|
);
|
|
} catch (error) {
|
|
logger.error("[TestView] ❌ Error refreshing settings:", error);
|
|
this.addUrlTestResult(
|
|
`Error refreshing settings: ${error instanceof Error ? error.message : String(error)}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Logs the current environment state to the console.
|
|
*/
|
|
public logEnvironmentState() {
|
|
logger.info("[TestView] 🌐 Current Environment State:", {
|
|
VITE_PLATFORM: import.meta.env.VITE_PLATFORM,
|
|
VITE_DEFAULT_ENDORSER_API_SERVER: import.meta.env
|
|
.VITE_DEFAULT_ENDORSER_API_SERVER,
|
|
VITE_DEFAULT_PARTNER_API_SERVER: import.meta.env
|
|
.VITE_DEFAULT_PARTNER_API_SERVER,
|
|
NODE_ENV: process.env.NODE_ENV,
|
|
activeDid: this.activeDid,
|
|
apiServer: this.apiServer,
|
|
partnerApiServer: this.partnerApiServer,
|
|
});
|
|
this.$notify({
|
|
group: "info",
|
|
type: "info",
|
|
title: "Environment State Logged",
|
|
text: "Current environment state logged to console.",
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Gets the current platform based on the API server.
|
|
*/
|
|
public getCurrentPlatform(): string {
|
|
if (this.apiServer?.includes(AppString.PROD_ENDORSER_API_SERVER)) {
|
|
return "Production";
|
|
} else if (this.apiServer?.includes(AppString.TEST_ENDORSER_API_SERVER)) {
|
|
return "Test";
|
|
} else {
|
|
return "Unknown";
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|