aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-08-26 12:59:00 -0300
committerSebastian <sebasjm@gmail.com>2022-08-26 12:59:09 -0300
commitcf894f1dd309e48d8be380c56175219027c84fb7 (patch)
tree8aa434c47d4ed366f4b3d5f0bd89fc696a388b9d
parenteef2d4702019b9de64efc01fff097b437e65ce39 (diff)
fix #7065
-rwxr-xr-xpackages/anastasis-webui/clean_and_build.sh5
-rw-r--r--packages/anastasis-webui/src/components/menu/SideBar.tsx7
-rw-r--r--packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts6
-rw-r--r--packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts4
-rw-r--r--packages/anastasis-webui/src/pages/home/AddingProviderScreen/state.ts5
-rw-r--r--packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx4
-rw-r--r--packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx5
-rw-r--r--packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx61
-rw-r--r--packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx27
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx56
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx325
-rw-r--r--packages/anastasis-webui/src/pages/home/SolveScreen.tsx73
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx251
-rw-r--r--packages/anastasis-webui/src/pages/home/index.storiesNo.tsx2
-rw-r--r--packages/anastasis-webui/src/pages/home/index.tsx4
-rw-r--r--packages/anastasis-webui/src/stories.tsx6
-rw-r--r--packages/anastasis-webui/src/utils/index.tsx5
-rwxr-xr-xpackages/anastasis-webui/watch/serve.sh3
18 files changed, 521 insertions, 328 deletions
diff --git a/packages/anastasis-webui/clean_and_build.sh b/packages/anastasis-webui/clean_and_build.sh
index a88dc9c37..9486848fe 100755
--- a/packages/anastasis-webui/clean_and_build.sh
+++ b/packages/anastasis-webui/clean_and_build.sh
@@ -40,17 +40,20 @@ set -e
echo compile
build_css &
build_js src/main.ts &
+build_js src/stories.tsx &
build_js src/main.test.ts &
for file in $(find src/ -name test.ts); do build_js $file; done &
wait -n
wait -n
wait -n
wait -n
+wait -n
pnpm run --silent test -- -R dot
echo html
build_html ui
build_html ui-dev
+build_html stories
if [ "WATCH" == "$1" ]; then
@@ -62,6 +65,8 @@ if [ "WATCH" == "$1" ]; then
echo $(date) $line
build_js src/main.ts
build_html ui-dev
+ build_js src/stories.tsx
+ build_html stories
./watch/send.sh '{"type":"RELOAD"}'
done;
fi
diff --git a/packages/anastasis-webui/src/components/menu/SideBar.tsx b/packages/anastasis-webui/src/components/menu/SideBar.tsx
index f83131ae1..ee394da83 100644
--- a/packages/anastasis-webui/src/components/menu/SideBar.tsx
+++ b/packages/anastasis-webui/src/components/menu/SideBar.tsx
@@ -28,11 +28,8 @@ interface Props {
mobile?: boolean;
}
-// @ts-ignore
-const maybeEnv = process?.env || {};
-
-const VERSION: string = maybeEnv.__VERSION__ || "dev";
-const GIT_HASH: string | undefined = maybeEnv.__GIT_HASH__;
+const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "dev";
+const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
const VERSION_WITH_HASH = GIT_HASH ? `${VERSION}-${GIT_HASH}` : VERSION;
export function Sidebar({ mobile }: Props): VNode {
diff --git a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts
index f78576118..3ad563ee6 100644
--- a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts
+++ b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts
@@ -180,7 +180,7 @@ function getStateFromStorage(): any {
state = JSON.parse(s);
}
} catch (e) {
- console.log(e);
+ console.log("ERROR: getStateFromStorage ", e);
}
return state ?? undefined;
}
@@ -203,7 +203,7 @@ export function useAnastasisReducer(): AnastasisReducerApi {
JSON.stringify(newState.reducerState),
);
} catch (e) {
- console.log(e);
+ console.log("ERROR setAnastasisState", e);
}
setAnastasisStateInternal(newState);
@@ -239,7 +239,7 @@ export function useAnastasisReducer(): AnastasisReducerApi {
},
});
};
- doUpdate().catch((e) => console.log(e));
+ doUpdate().catch((e) => console.log("ERROR doUpdate", e));
};
tryUpdateProviders();
diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts
index 5d5913ffc..0ab275f54 100644
--- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts
@@ -16,6 +16,7 @@
import { AuthenticationProviderStatus } from "@gnu-taler/anastasis-core";
import InvalidState from "../../../components/InvalidState.js";
import NoReducer from "../../../components/NoReducer.js";
+import { Notification } from "../../../components/Notifications.js";
import { compose, StateViewMap } from "../../../utils/index.js";
import useComponentState from "./state.js";
import { WithoutProviderType, WithProviderType } from "./views.js";
@@ -44,6 +45,7 @@ interface CommonProps {
setProviderURL: (url: string) => Promise<void>;
providerURL: string;
errors: string | undefined;
+ notifications: Notification[];
}
export interface WithType extends CommonProps {
@@ -90,7 +92,7 @@ export async function testProvider(
}
return;
} catch (e) {
- console.log("error", e);
+ console.log("ERROR testProvider", e);
const error =
e instanceof Error
? Error(
diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/state.ts b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/state.ts
index a04c7957b..009ab20a2 100644
--- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/state.ts
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/state.ts
@@ -14,6 +14,7 @@
GNU Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { useEffect, useRef, useState } from "preact/hooks";
+import { Notification } from "../../../components/Notifications.js";
import { useAnastasisContext } from "../../../context/anastasis.js";
import { authMethods, KnownAuthMethods } from "../authMethod/index.jsx";
import { AuthProvByStatusMap, State, testProvider } from "./index.js";
@@ -21,9 +22,10 @@ import { AuthProvByStatusMap, State, testProvider } from "./index.js";
interface Props {
providerType?: KnownAuthMethods;
onCancel: () => Promise<void>;
+ notifications?: Notification[];
}
-export default function useComponentState({ providerType, onCancel }: Props): State {
+export default function useComponentState({ providerType, onCancel, notifications = [] }: Props): State {
const reducer = useAnastasisContext();
const [providerURL, setProviderURL] = useState("");
@@ -128,6 +130,7 @@ export default function useComponentState({ providerType, onCancel }: Props): St
setProviderURL: async (s: string) => setProviderURL(s),
errors,
error,
+ notifications
}
if (!providerLabel) {
diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx
index 7faaae4d8..dc41d9c1a 100644
--- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx
@@ -53,6 +53,7 @@ export const NewProvider = createExampleWithoutAnastasis(WithoutProviderType, {
disabled: [],
error: [],
},
+ notifications: [],
});
export const NewProviderWithoutProviderList = createExampleWithoutAnastasis(
@@ -64,6 +65,7 @@ export const NewProviderWithoutProviderList = createExampleWithoutAnastasis(
disabled: [],
error: [],
},
+ notifications: [],
},
);
@@ -75,6 +77,7 @@ export const NewSmsProvider = createExampleWithoutAnastasis(WithProviderType, {
error: [],
},
providerLabel: "sms",
+ notifications: [],
});
export const NewIBANProvider = createExampleWithoutAnastasis(WithProviderType, {
@@ -85,4 +88,5 @@ export const NewIBANProvider = createExampleWithoutAnastasis(WithProviderType, {
error: [],
},
providerLabel: "IBAN",
+ notifications: [],
});
diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx
index bb1283a82..e397e0b65 100644
--- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx
@@ -20,6 +20,7 @@ import {
import { h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { TextInput } from "../../../components/fields/TextInput.js";
+import { Notifications } from "../../../components/Notifications.js";
import { AnastasisClientFrame } from "../index.js";
import { testProvider, WithoutType, WithType } from "./index.js";
@@ -31,6 +32,7 @@ export function WithProviderType(props: WithType): VNode {
hideNext={props.errors}
>
<div>
+ <Notifications notifications={props.notifications} />
<p>Add a provider url for a {props.providerLabel} service</p>
<div class="container">
<TextInput
@@ -108,10 +110,11 @@ export function WithoutProviderType(props: WithoutType): VNode {
return (
<AnastasisClientFrame
hideNav
- title="Backup: Manage providers2"
+ title="Backup: Manage providers"
hideNext={props.errors}
>
<div>
+ <Notifications notifications={props.notifications} />
<p>Add a provider url</p>
<div class="container">
<TextInput
diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
index e3d92c4df..552cb069f 100644
--- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
@@ -174,85 +174,88 @@ export const OnePolicyWithAllTheChallengesInDifferentState = createExample(
{ uuid: "uuid-7" },
{ uuid: "uuid-8" },
{ uuid: "uuid-9" },
+ { uuid: "uuid-10" },
],
],
challenges: [
{
- instructions: 'in state "solved"',
+ instructions: 'this challenge is in state "solved"',
type: "question",
uuid: "uuid-1",
},
{
- instructions: 'in state "message"',
+ instructions: 'this challenge is in state "code-in-file"',
type: "question",
uuid: "uuid-2",
},
{
- instructions: 'in state "auth iban"',
+ instructions: 'this challenge is in state "code-sent"',
type: "question",
uuid: "uuid-3",
},
{
- instructions: 'in state "payment "',
+ instructions: 'this challenge is in state "server-failure "',
type: "question",
uuid: "uuid-4",
},
{
- instructions: 'in state "rate limit"',
+ instructions: 'this challenge is in state "truth-unknown"',
type: "question",
uuid: "uuid-5",
},
{
- instructions: 'in state "redirect"',
+ instructions: 'this challenge is in state "taler-payment"',
type: "question",
uuid: "uuid-6",
},
{
- instructions: 'in state "server failure"',
+ instructions: 'this challenge is in state "unsupported"',
type: "question",
uuid: "uuid-7",
},
{
- instructions: 'in state "truth unknown"',
+ instructions: 'this challenge is in state "rate-limit-exceeded"',
type: "question",
uuid: "uuid-8",
},
{
- instructions: 'in state "unsupported"',
+ instructions: 'this challenge is in state "iban-instructions"',
type: "question",
uuid: "uuid-9",
},
+ {
+ instructions: 'this challenge is in state "incorrect-answer"',
+ type: "question",
+ uuid: "uuid-10",
+ },
],
},
challenge_feedback: {
"uuid-1": { state: ChallengeFeedbackStatus.Solved.toString() },
- "uuid-3": {
- state: ChallengeFeedbackStatus.IbanInstructions.toString(),
- challenge_amount: "EUR:1",
- target_iban: "DE12345789000",
- target_business_name: "Data Loss Incorporated",
- wire_transfer_subject: "Anastasis 987654321",
- },
+ "uuid-2": { state: ChallengeFeedbackStatus.CodeInFile.toString() },
+ "uuid-3": { state: ChallengeFeedbackStatus.CodeSent.toString() },
"uuid-4": {
+ state: ChallengeFeedbackStatus.ServerFailure.toString(),
+ http_status: 500,
+ error_response: "some error message or error object",
+ },
+ "uuid-5": { state: ChallengeFeedbackStatus.TruthUnknown.toString() },
+ "uuid-6": {
state: ChallengeFeedbackStatus.TalerPayment.toString(),
taler_pay_uri: "taler://pay/...",
provider: "https://localhost:8080/",
payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG",
},
- "uuid-5": {
- state: ChallengeFeedbackStatus.RateLimitExceeded.toString(),
- // "error_code": 8121
- },
- "uuid-7": {
- state: ChallengeFeedbackStatus.ServerFailure.toString(),
- http_status: 500,
- error_response: "some error message or error object",
- },
- "uuid-8": {
- state: ChallengeFeedbackStatus.TruthUnknown.toString(),
- // "error_code": 8108
+ "uuid-7": { state: ChallengeFeedbackStatus.Unsupported.toString() },
+ "uuid-8": { state: ChallengeFeedbackStatus.RateLimitExceeded.toString() },
+ "uuid-9": {
+ state: ChallengeFeedbackStatus.IbanInstructions.toString(),
+ challenge_amount: "EUR:1",
+ target_iban: "DE12345789000",
+ target_business_name: "Data Loss Incorporated",
+ wire_transfer_subject: "Anastasis 987654321",
},
- "uuid-9": { state: ChallengeFeedbackStatus.Unsupported.toString() },
+ "uuid-10": { state: ChallengeFeedbackStatus.IncorrectAnswer.toString() },
},
} as ReducerState,
);
diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx
index 8de0ceb08..5b9c11bab 100644
--- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx
@@ -17,22 +17,25 @@ import {
ChallengeFeedback,
ChallengeFeedbackStatus,
} from "@gnu-taler/anastasis-core";
-import { h, VNode } from "preact";
+import { Fragment, h, VNode } from "preact";
import { AsyncButton } from "../../components/AsyncButton.js";
import { useAnastasisContext } from "../../context/anastasis.js";
import { authMethods, KnownAuthMethods } from "./authMethod/index.js";
import { AnastasisClientFrame } from "./index.js";
-function OverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }) {
+function OverviewFeedbackDisplay(props: {
+ feedback?: ChallengeFeedback;
+}): VNode {
const { feedback } = props;
if (!feedback) {
- return null;
+ return <Fragment />;
}
+
switch (feedback.state) {
case ChallengeFeedbackStatus.Solved:
return <div />;
case ChallengeFeedbackStatus.IbanInstructions:
- return null;
+ return <div class="block has-text-info">Payment required.</div>;
case ChallengeFeedbackStatus.ServerFailure:
return <div class="block has-text-danger">Server error.</div>;
case ChallengeFeedbackStatus.RateLimitExceeded:
@@ -51,12 +54,20 @@ function OverviewFeedbackDisplay(props: { feedback?: ChallengeFeedback }) {
case ChallengeFeedbackStatus.TruthUnknown:
return (
<div class="block has-text-danger">
- Provider doesn&apos;t recognize the challenge of the policy. Contact
- the provider for further information.
+ Provider doesn&apos;t recognize the type of challenge. Use another
+ version or contact the provider.
</div>
);
- default:
- return <div />;
+ case ChallengeFeedbackStatus.IncorrectAnswer:
+ return (
+ <div class="block has-text-danger">The answer was not correct.</div>
+ );
+ case ChallengeFeedbackStatus.CodeInFile:
+ return <div class="block has-text-info">code in file</div>;
+ case ChallengeFeedbackStatus.CodeSent:
+ return <div class="block has-text-info">Code sent</div>;
+ case ChallengeFeedbackStatus.TalerPayment:
+ return <div class="block has-text-info">Payment required</div>;
}
}
diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx
index fec1e0113..b457937f8 100644
--- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx
@@ -21,29 +21,57 @@
import { ReducerState } from "@gnu-taler/anastasis-core";
import { createExample, reducerStatesExample } from "../../utils/index.js";
-import { SecretSelectionScreen as TestedComponent } from "./SecretSelectionScreen.js";
+import {
+ SecretSelectionScreen,
+ SecretSelectionScreenFound,
+} from "./SecretSelectionScreen.js";
export default {
- component: TestedComponent,
+ component: SecretSelectionScreen,
args: {
order: 4,
},
- argTypes: {
- onUpdate: { action: "onUpdate" },
- onBack: { action: "onBack" },
- },
};
-export const Example = createExample(TestedComponent, {
- ...reducerStatesExample.secretSelection,
- recovery_document: {
- provider_url: "https://kudos.demo.anastasis.lu/",
- secret_name: "secretName",
- version: 1,
+export const Example = createExample(
+ SecretSelectionScreenFound,
+ {
+ ...reducerStatesExample.secretSelection,
+ recovery_document: {
+ provider_url: "https://kudos.demo.anastasis.lu/",
+ secret_name: "secretName",
+ version: 1,
+ },
+ } as ReducerState,
+ {
+ policies: [
+ {
+ secret_name: "The secret name 1",
+ attribute_mask: 1,
+ policy_hash: "abcdefghijklmnopqrstuvwxyz",
+ providers: [
+ {
+ url: "http://someurl",
+ version: 1,
+ },
+ ],
+ },
+ {
+ secret_name: "The secret name 2",
+ attribute_mask: 1,
+ policy_hash: "abcdefghijklmnopqrstuvwxyz",
+ providers: [
+ {
+ url: "http://someurl",
+ version: 1,
+ },
+ ],
+ },
+ ],
},
-} as ReducerState);
+);
-export const NoRecoveryDocumentFound = createExample(TestedComponent, {
+export const NoRecoveryDocumentFound = createExample(SecretSelectionScreen, {
...reducerStatesExample.secretSelection,
recovery_document: undefined,
} as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
index 11271aaa5..ce44b0884 100644
--- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
@@ -14,6 +14,7 @@
GNU Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import {
+ AggregatedPolicyMetaInfo,
AuthenticationProviderStatus,
AuthenticationProviderStatusOk,
} from "@gnu-taler/anastasis-core";
@@ -25,20 +26,16 @@ import { useAnastasisContext } from "../../context/anastasis.js";
import AddingProviderScreen from "./AddingProviderScreen/index.js";
import { AnastasisClientFrame } from "./index.js";
-export function SecretSelectionScreen(): VNode {
- const [selectingVersion, setSelectingVersion] = useState<boolean>(false);
+export function SecretSelectionScreenFound({
+ policies,
+ onManageProvider,
+ onNext,
+}: {
+ policies: AggregatedPolicyMetaInfo[];
+ onManageProvider: () => void;
+ onNext: (version: AggregatedPolicyMetaInfo) => void;
+}): VNode {
const reducer = useAnastasisContext();
- const [manageProvider, setManageProvider] = useState(false);
-
- useEffect(() => {
- async function f() {
- if (reducer) {
- await reducer.discoverStart();
- }
- }
- f().catch((e) => console.log(e));
- }, []);
-
if (!reducer) {
return <div>no reducer in context</div>;
}
@@ -49,45 +46,6 @@ export function SecretSelectionScreen(): VNode {
) {
return <div>invalid state</div>;
}
-
- const provs = reducer.currentReducerState.authentication_providers ?? {};
- const recoveryDocument = reducer.currentReducerState.recovery_document;
-
- if (manageProvider) {
- return (
- <AddingProviderScreen onCancel={async () => setManageProvider(false)} />
- );
- }
-
- if (reducer.discoveryState.state === "none") {
- // Can this even happen?
- return (
- <AnastasisClientFrame title="Recovery: Select secret">
- <div>waiting to start discovery</div>
- </AnastasisClientFrame>
- );
- }
-
- if (reducer.discoveryState.state === "active") {
- return (
- <AnastasisClientFrame title="Recovery: Select secret">
- <div>loading secret versions</div>
- </AnastasisClientFrame>
- );
- }
-
- const policies = reducer.discoveryState.aggregatedPolicies ?? [];
-
- if (policies.length === 0) {
- return (
- <ChooseAnotherProviderScreen
- providers={provs}
- selected=""
- onChange={() => null}
- ></ChooseAnotherProviderScreen>
- );
- }
-
return (
<AnastasisClientFrame
title="Recovery: Select secret"
@@ -115,9 +73,7 @@ export function SecretSelectionScreen(): VNode {
<b>Id:</b>&nbsp;
<span
class="icon has-tooltip-top"
- data-tooltip={version.policy_hash
- .match(/(.{22})/g)
- ?.join("\n")}
+ data-tooltip={version.policy_hash}
>
<i class="mdi mdi-information" />
</span>
@@ -128,9 +84,7 @@ export function SecretSelectionScreen(): VNode {
<div>
<AsyncButton
class="button"
- onClick={() =>
- reducer.transition("select_version", version)
- }
+ onClick={async () => onNext(version)}
>
Recover
</AsyncButton>
@@ -145,9 +99,7 @@ export function SecretSelectionScreen(): VNode {
challenges solving
</p>
<p class="block">
- <a onClick={() => setManageProvider(true)}>
- Manage recovery providers
- </a>
+ <a onClick={onManageProvider}>Manage recovery providers</a>
</p>
</div>
</div>
@@ -155,8 +107,7 @@ export function SecretSelectionScreen(): VNode {
);
}
-export function OldSecretSelectionScreen(): VNode {
- const [selectingVersion, setSelectingVersion] = useState<boolean>(false);
+export function SecretSelectionScreen(): VNode {
const reducer = useAnastasisContext();
const [manageProvider, setManageProvider] = useState(false);
@@ -169,15 +120,10 @@ export function OldSecretSelectionScreen(): VNode {
f().catch((e) => console.log(e));
}, []);
- const currentVersion =
- (reducer?.currentReducerState &&
- "recovery_document" in reducer.currentReducerState &&
- reducer.currentReducerState.recovery_document?.version) ||
- 0;
-
if (!reducer) {
return <div>no reducer in context</div>;
}
+
if (
!reducer.currentReducerState ||
reducer.currentReducerState.reducer_type !== "recovery"
@@ -185,97 +131,178 @@ export function OldSecretSelectionScreen(): VNode {
return <div>invalid state</div>;
}
- async function doSelectVersion(p: string, n: number): Promise<void> {
- if (!reducer) return Promise.resolve();
- return reducer.runTransaction(async (tx) => {
- await tx.transition("select_version", {
- version: n,
- provider_url: p,
- });
- setSelectingVersion(false);
- });
- }
-
- const provs = reducer.currentReducerState.authentication_providers ?? {};
- const recoveryDocument = reducer.currentReducerState.recovery_document;
-
- if (!recoveryDocument) {
+ if (manageProvider) {
return (
- <ChooseAnotherProviderScreen
- providers={provs}
- selected=""
- onChange={(newProv) => doSelectVersion(newProv, 0)}
- />
+ <AddingProviderScreen onCancel={async () => setManageProvider(false)} />
);
}
- if (selectingVersion) {
- return (
- <SelectOtherVersionProviderScreen
- providers={provs}
- provider={recoveryDocument.provider_url}
- version={recoveryDocument.version}
- onCancel={() => setSelectingVersion(false)}
- onConfirm={doSelectVersion}
- />
- );
+ if (
+ reducer.discoveryState.state === "none" ||
+ reducer.discoveryState.state === "active"
+ ) {
+ // Can this even happen?
+ return <SecretSelectionScreenWaiting />;
}
- if (manageProvider) {
+ const policies = reducer.discoveryState.aggregatedPolicies ?? [];
+
+ if (policies.length === 0) {
return (
- <AddingProviderScreen onCancel={async () => setManageProvider(false)} />
+ <AddingProviderScreen
+ onCancel={async () => setManageProvider(false)}
+ notifications={[
+ {
+ message: "Secret not found",
+ type: "ERROR",
+ description:
+ "With the information you provided we could not found secret in any of the providers. You can try adding more providers if you think the data is correct.",
+ },
+ ]}
+ />
);
}
- const providerInfo = provs[
- recoveryDocument.provider_url
- ] as AuthenticationProviderStatusOk;
-
return (
- <AnastasisClientFrame title="Recovery: Select secret">
- <div class="columns">
- <div class="column">
- <div class="box" style={{ border: "2px solid green" }}>
- <h1 class="subtitle">{providerInfo.business_name}</h1>
- <div class="block">
- {currentVersion === 0 ? (
- <p>Set to recover the latest version</p>
- ) : (
- <p>Set to recover the version number {currentVersion}</p>
- )}
- </div>
- <div class="buttons is-right">
- <button class="button" onClick={(e) => setSelectingVersion(true)}>
- Change secret&apos;s version
- </button>
- </div>
- </div>
- </div>
- <div class="column">
- <p>
- Secret found, you can select another version or continue to the
- challenges solving
- </p>
- <p class="block">
- <a onClick={() => setManageProvider(true)}>
- Manage recovery providers
- </a>
- </p>
- </div>
- </div>
- </AnastasisClientFrame>
+ <SecretSelectionScreenFound
+ policies={policies}
+ onNext={(version) => reducer.transition("select_version", version)}
+ onManageProvider={async () => setManageProvider(false)}
+ />
);
}
+// export function OldSecretSelectionScreen(): VNode {
+// const [selectingVersion, setSelectingVersion] = useState<boolean>(false);
+// const reducer = useAnastasisContext();
+// const [manageProvider, setManageProvider] = useState(false);
+
+// useEffect(() => {
+// async function f() {
+// if (reducer) {
+// await reducer.discoverStart();
+// }
+// }
+// f().catch((e) => console.log(e));
+// }, []);
+
+// const currentVersion =
+// (reducer?.currentReducerState &&
+// "recovery_document" in reducer.currentReducerState &&
+// reducer.currentReducerState.recovery_document?.version) ||
+// 0;
+
+// if (!reducer) {
+// return <div>no reducer in context</div>;
+// }
+// if (
+// !reducer.currentReducerState ||
+// reducer.currentReducerState.reducer_type !== "recovery"
+// ) {
+// return <div>invalid state</div>;
+// }
+
+// async function doSelectVersion(p: string, n: number): Promise<void> {
+// if (!reducer) return Promise.resolve();
+// return reducer.runTransaction(async (tx) => {
+// await tx.transition("select_version", {
+// version: n,
+// provider_url: p,
+// });
+// setSelectingVersion(false);
+// });
+// }
+
+// const provs = reducer.currentReducerState.authentication_providers ?? {};
+// const recoveryDocument = reducer.currentReducerState.recovery_document;
+
+// if (!recoveryDocument) {
+// return (
+// <ChooseAnotherProviderScreen
+// providers={provs}
+// selected=""
+// onChange={(newProv) => doSelectVersion(newProv, 0)}
+// />
+// );
+// }
+
+// if (selectingVersion) {
+// return (
+// <SelectOtherVersionProviderScreen
+// providers={provs}
+// provider={recoveryDocument.provider_url}
+// version={recoveryDocument.version}
+// onCancel={() => setSelectingVersion(false)}
+// onConfirm={doSelectVersion}
+// />
+// );
+// }
+
+// if (manageProvider) {
+// return (
+// <AddingProviderScreen onCancel={async () => setManageProvider(false)} />
+// );
+// }
+
+// const providerInfo = provs[
+// recoveryDocument.provider_url
+// ] as AuthenticationProviderStatusOk;
+
+// return (
+// <AnastasisClientFrame title="Recovery: Select secret">
+// <div class="columns">
+// <div class="column">
+// <div class="box" style={{ border: "2px solid green" }}>
+// <h1 class="subtitle">{providerInfo.business_name}</h1>
+// <div class="block">
+// {currentVersion === 0 ? (
+// <p>Set to recover the latest version</p>
+// ) : (
+// <p>Set to recover the version number {currentVersion}</p>
+// )}
+// </div>
+// <div class="buttons is-right">
+// <button class="button" onClick={(e) => setSelectingVersion(true)}>
+// Change secret&apos;s version
+// </button>
+// </div>
+// </div>
+// </div>
+// <div class="column">
+// <p>
+// Secret found, you can select another version or continue to the
+// challenges solving
+// </p>
+// <p class="block">
+// <a onClick={() => setManageProvider(true)}>
+// Manage recovery providers
+// </a>
+// </p>
+// </div>
+// </div>
+// </AnastasisClientFrame>
+// );
+// }
+
function ChooseAnotherProviderScreen({
- providers,
- selected,
onChange,
}: {
- selected: string;
- providers: { [url: string]: AuthenticationProviderStatus };
onChange: (prov: string) => void;
}): VNode {
+ const reducer = useAnastasisContext();
+
+ if (!reducer) {
+ return <div>no reducer in context</div>;
+ }
+
+ if (
+ !reducer.currentReducerState ||
+ reducer.currentReducerState.reducer_type !== "recovery"
+ ) {
+ return <div>invalid state</div>;
+ }
+ const providers = reducer.currentReducerState.authentication_providers ?? {};
+
return (
<AnastasisClientFrame
hideNext="Recovery document not found"
@@ -286,13 +313,9 @@ function ChooseAnotherProviderScreen({
<label class="label">Provider</label>
<div class="control is-expanded has-icons-left">
<div class="select is-fullwidth">
- <select
- onChange={(e) => onChange(e.currentTarget.value)}
- value={selected}
- >
+ <select onChange={(e) => onChange(e.currentTarget.value)} value="">
<option key="none" disabled selected value="">
- {" "}
- Choose a provider{" "}
+ Choose a provider
</option>
{Object.keys(providers).map((url) => {
const p = providers[url];
@@ -419,3 +442,11 @@ function SelectOtherVersionProviderScreen({
</AnastasisClientFrame>
);
}
+
+function SecretSelectionScreenWaiting(): VNode {
+ return (
+ <AnastasisClientFrame title="Recovery: Select secret">
+ <div>loading secret versions</div>
+ </AnastasisClientFrame>
+ );
+}
diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx
index 23910565e..7f4d5aa18 100644
--- a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx
@@ -40,7 +40,14 @@ export function SolveOverviewFeedbackDisplay(props: {
message: `Message from provider`,
description: (
<span>
- To pay you can <a href={feedback.taler_pay_uri}>click here</a>
+ To pay you can{" "}
+ <a
+ href={feedback.taler_pay_uri}
+ target="_blank"
+ rel="noreferrer"
+ >
+ click here
+ </a>
</span>
),
},
@@ -65,8 +72,12 @@ export function SolveOverviewFeedbackDisplay(props: {
notifications={[
{
type: "ERROR",
- message: `Server error: Code ${feedback.http_status}`,
- description: feedback.error_response,
+ message: `Server error: response code ${feedback.http_status}`,
+ description: !feedback.error_response
+ ? undefined
+ : `More information: ${JSON.stringify(
+ feedback.error_response,
+ )}`,
},
]}
/>
@@ -77,8 +88,7 @@ export function SolveOverviewFeedbackDisplay(props: {
notifications={[
{
type: "ERROR",
- message: `Message from provider`,
- description: "There were to many failed attempts.",
+ message: "There were to many failed attempts.",
},
]}
/>
@@ -107,11 +117,56 @@ export function SolveOverviewFeedbackDisplay(props: {
]}
/>
);
- default:
- console.warn(
- `unknown challenge feedback status ${JSON.stringify(feedback)}`,
+ case ChallengeFeedbackStatus.CodeInFile:
+ return (
+ <Notifications
+ notifications={[
+ {
+ type: "INFO",
+ message: `Required TAN can be found in file "${feedback.filename}"`,
+ description: feedback.display_hint
+ ? `HINT: ${feedback.display_hint}`
+ : undefined,
+ },
+ ]}
+ />
+ );
+ case ChallengeFeedbackStatus.CodeSent:
+ return (
+ <Notifications
+ notifications={[
+ {
+ type: "INFO",
+ message: `Code sent to address "${feedback.address_hint}"`,
+ description: feedback.display_hint
+ ? `HINT: ${feedback.display_hint}`
+ : undefined,
+ },
+ ]}
+ />
+ );
+ case ChallengeFeedbackStatus.IncorrectAnswer:
+ return (
+ <Notifications
+ notifications={[
+ {
+ type: "ERROR",
+ message: `The answer is wrong.`,
+ },
+ ]}
+ />
+ );
+ case ChallengeFeedbackStatus.Solved:
+ return (
+ <Notifications
+ notifications={[
+ {
+ type: "SUCCESS",
+ message: `This challenge is solved`,
+ },
+ ]}
+ />
);
- return <div />;
}
}
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx
index 038d5e897..182538775 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx
@@ -20,7 +20,6 @@
*/
import {
- ChallengeFeedbackBankTransferRequired,
ChallengeFeedbackStatus,
ReducerState,
} from "@gnu-taler/anastasis-core";
@@ -61,142 +60,190 @@ export const WithoutFeedback = createExample(
},
);
-export const ServerFailureFeedback = createExample(
- TestedComponent[type].solve,
- {
- ...reducerStatesExample.challengeSolving,
- recovery_information: {
- challenges: [
- {
- instructions: "does P equal NP?",
- type: "question",
- uuid: "ASDASDSAD!1",
- },
- ],
- policies: [],
+const recovery_information = {
+ challenges: [
+ {
+ instructions: "does P equal NP?",
+ type: "question",
+ uuid: "ASDASDSAD!1",
},
- selected_challenge_uuid: "ASDASDSAD!1",
- challenge_feedback: {
- "ASDASDSAD!1": {
- state: ChallengeFeedbackStatus.ServerFailure,
- http_status: 500,
- error_response: "Couldn't connect to mysql",
- },
+ ],
+ policies: [],
+};
+
+export const CodeInFileFeedback = createExample(TestedComponent[type].solve, {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information,
+ selected_challenge_uuid: "ASDASDSAD!1",
+ challenge_feedback: {
+ "ASDASDSAD!1": {
+ state: ChallengeFeedbackStatus.CodeInFile,
+ filename: "asd",
+ display_hint: "hint",
},
- } as ReducerState,
-);
+ },
+} as ReducerState);
-export const MessageRateLimitExceededFeedback = createExample(
+export const CodeSentFeedback = createExample(TestedComponent[type].solve, {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information,
+ selected_challenge_uuid: "ASDASDSAD!1",
+ challenge_feedback: {
+ "ASDASDSAD!1": {
+ state: ChallengeFeedbackStatus.CodeSent,
+ address_hint: "asdasd",
+ display_hint: "qweqweqw",
+ },
+ },
+} as ReducerState);
+
+export const SolvedFeedback = createExample(TestedComponent[type].solve, {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information,
+ selected_challenge_uuid: "ASDASDSAD!1",
+ challenge_feedback: {
+ "ASDASDSAD!1": {
+ state: ChallengeFeedbackStatus.Solved,
+ },
+ },
+} as ReducerState);
+
+export const ServerFailureFeedback = createExample(
TestedComponent[type].solve,
{
...reducerStatesExample.challengeSolving,
- recovery_information: {
- challenges: [
- {
- instructions: "does P equals NP?",
- type: "question",
- uuid: "ASDASDSAD!1",
- },
- ],
- policies: [],
- },
+ recovery_information,
selected_challenge_uuid: "ASDASDSAD!1",
challenge_feedback: {
"ASDASDSAD!1": {
- state: ChallengeFeedbackStatus.RateLimitExceeded,
+ state: ChallengeFeedbackStatus.ServerFailure,
+ http_status: 500,
},
},
} as ReducerState,
);
-export const UnsupportedFeedback = createExample(TestedComponent[type].solve, {
+export const TruthUnknownFeedback = createExample(TestedComponent[type].solve, {
...reducerStatesExample.challengeSolving,
- recovery_information: {
- challenges: [
- {
- instructions: "does P equals NP?",
- type: "question",
- uuid: "ASDASDSAD!1",
- },
- ],
- policies: [],
- },
+ recovery_information,
selected_challenge_uuid: "ASDASDSAD!1",
challenge_feedback: {
"ASDASDSAD!1": {
- state: ChallengeFeedbackStatus.Unsupported,
- http_status: 500,
- unsupported_method: "Question",
+ state: ChallengeFeedbackStatus.TruthUnknown,
},
},
} as ReducerState);
-export const TruthUnknownFeedback = createExample(TestedComponent[type].solve, {
+export const TalerPaymentFeedback = createExample(TestedComponent[type].solve, {
...reducerStatesExample.challengeSolving,
- recovery_information: {
- challenges: [
- {
- instructions: "does P equals NP?",
- type: "question",
- uuid: "ASDASDSAD!1",
- },
- ],
- policies: [],
- },
+ recovery_information,
selected_challenge_uuid: "ASDASDSAD!1",
challenge_feedback: {
"ASDASDSAD!1": {
- state: ChallengeFeedbackStatus.TruthUnknown,
+ state: ChallengeFeedbackStatus.TalerPayment,
+ payment_secret: "secret",
+ provider: "asdasdas",
+ taler_pay_uri: "taler://pay/...",
},
},
} as ReducerState);
-const ibanFeedback: ChallengeFeedbackBankTransferRequired = {
- state: ChallengeFeedbackStatus.IbanInstructions,
- challenge_amount: "EUR:1",
- target_iban: "DE12345789000",
- target_business_name: "Data Loss Incorporated",
- wire_transfer_subject: "Anastasis 987654321",
- answer_code: 987654321,
-};
-
-export const AuthIbanFeedback = createExample(TestedComponent[type].solve, {
+export const UnsupportedFeedback = createExample(TestedComponent[type].solve, {
...reducerStatesExample.challengeSolving,
- recovery_information: {
- challenges: [
- {
- instructions: "does P equals NP?",
- type: "question",
- uuid: "ASDASDSAD!1",
- },
- ],
- policies: [],
- },
+ recovery_information,
selected_challenge_uuid: "ASDASDSAD!1",
challenge_feedback: {
- "ASDASDSAD!1": ibanFeedback,
+ "ASDASDSAD!1": {
+ state: ChallengeFeedbackStatus.Unsupported,
+ unsupported_method: "method",
+ },
},
} as ReducerState);
-export const PaymentFeedback = createExample(TestedComponent[type].solve, {
- ...reducerStatesExample.challengeSolving,
- recovery_information: {
- challenges: [
- {
- instructions: "does P equals NP?",
- type: "question",
- uuid: "ASDASDSAD!1",
+export const RateLimitExceededFeedback = createExample(
+ TestedComponent[type].solve,
+ {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information,
+ selected_challenge_uuid: "ASDASDSAD!1",
+ challenge_feedback: {
+ "ASDASDSAD!1": {
+ state: ChallengeFeedbackStatus.RateLimitExceeded,
},
- ],
- policies: [],
- },
- selected_challenge_uuid: "ASDASDSAD!1",
- challenge_feedback: {
- "ASDASDSAD!1": {
- state: ChallengeFeedbackStatus.TalerPayment,
- taler_pay_uri: "taler://pay/...",
- provider: "https://localhost:8080/",
- payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG",
},
- },
-} as ReducerState);
+ } as ReducerState,
+);
+
+export const IbanInstructionsFeedback = createExample(
+ TestedComponent[type].solve,
+ {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information,
+ selected_challenge_uuid: "ASDASDSAD!1",
+ challenge_feedback: {
+ "ASDASDSAD!1": {
+ state: ChallengeFeedbackStatus.IbanInstructions,
+ challenge_amount: "EUR:1",
+ target_iban: "DE12345789000",
+ target_business_name: "Data Loss Incorporated",
+ wire_transfer_subject: "Anastasis 987654321",
+ answer_code: 987654321,
+ },
+ },
+ } as ReducerState,
+);
+
+export const IncorrectAnswerFeedback = createExample(
+ TestedComponent[type].solve,
+ {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information,
+ selected_challenge_uuid: "ASDASDSAD!1",
+ challenge_feedback: {
+ "ASDASDSAD!1": {
+ state: ChallengeFeedbackStatus.IncorrectAnswer,
+ },
+ },
+ } as ReducerState,
+);
+
+// export const AuthIbanFeedback = createExample(TestedComponent[type].solve, {
+// ...reducerStatesExample.challengeSolving,
+// recovery_information: {
+// challenges: [
+// {
+// instructions: "does P equals NP?",
+// type: "question",
+// uuid: "ASDASDSAD!1",
+// },
+// ],
+// policies: [],
+// },
+// selected_challenge_uuid: "ASDASDSAD!1",
+// challenge_feedback: {
+// "ASDASDSAD!1": ibanFeedback,
+// },
+// } as ReducerState);
+
+// export const PaymentFeedback = createExample(TestedComponent[type].solve, {
+// ...reducerStatesExample.challengeSolving,
+// recovery_information: {
+// challenges: [
+// {
+// instructions: "does P equals NP?",
+// type: "question",
+// uuid: "ASDASDSAD!1",
+// },
+// ],
+// policies: [],
+// },
+// selected_challenge_uuid: "ASDASDSAD!1",
+// challenge_feedback: {
+// "ASDASDSAD!1": {
+// state: ChallengeFeedbackStatus.TalerPayment,
+// taler_pay_uri: "taler://pay/...",
+// provider: "https://localhost:8080/",
+// payment_secret: "3P4561HAMHRRYEYD6CM6J7TS5VTD5SR2K2EXJDZEFSX92XKHR4KG",
+// },
+// },
+// } as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/index.storiesNo.tsx b/packages/anastasis-webui/src/pages/home/index.storiesNo.tsx
index 31595111a..0dad73724 100644
--- a/packages/anastasis-webui/src/pages/home/index.storiesNo.tsx
+++ b/packages/anastasis-webui/src/pages/home/index.storiesNo.tsx
@@ -20,7 +20,7 @@
*/
export * as AddingProviderScreen from "./AddingProviderScreen/stories.js";
-export * as algo from "./AttributeEntryScreen.stories.js";
+export * as AttributeEntryScreen from "./AttributeEntryScreen.stories.js";
export * as AuthenticationEditorScreen from "./AuthenticationEditorScreen.stories.js";
export * as authMethod_AuthMethodEmailSetup from "./authMethod/AuthMethodEmailSetup.stories.js";
diff --git a/packages/anastasis-webui/src/pages/home/index.tsx b/packages/anastasis-webui/src/pages/home/index.tsx
index 25d9c63d9..44e065807 100644
--- a/packages/anastasis-webui/src/pages/home/index.tsx
+++ b/packages/anastasis-webui/src/pages/home/index.tsx
@@ -85,7 +85,7 @@ function ErrorBoundary(props: {
children: ComponentChildren;
}): VNode {
const [error, resetError] = useErrorBoundary((error) =>
- console.log("got error", error),
+ console.log("ErrorBoundary got error", error),
);
if (error) {
return (
@@ -132,7 +132,7 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode {
history.pushState({ id: nextId }, "unused", `#${nextId}`);
} catch (e) {
- console.log(e);
+ console.log("ERROR doNext ", e);
}
}
diff --git a/packages/anastasis-webui/src/stories.tsx b/packages/anastasis-webui/src/stories.tsx
index fb37cdfa5..7d22deece 100644
--- a/packages/anastasis-webui/src/stories.tsx
+++ b/packages/anastasis-webui/src/stories.tsx
@@ -38,7 +38,6 @@ function parseExampleImport(
im: any,
name?: string,
): ComponentItem {
- console.log(im);
const component = name || im.default.title;
const order: number = im.default.args?.order || 0;
return {
@@ -372,9 +371,8 @@ function LiveReload({ port = 8002 }: { port?: number }): VNode {
}
function setupLiveReload(port: number, onReload: () => void): void {
- const protocol = location.protocol === "https:" ? "wss:" : "ws:";
- const host = location.hostname;
- const socketPath = `${protocol}//${host}:${port}/socket`;
+ const socketPath = `ws://localhost:8003/socket`;
+ // const socketPath = `${protocol}//${host}:${port}/socket`;
const ws = new WebSocket(socketPath);
ws.onmessage = (message) => {
diff --git a/packages/anastasis-webui/src/utils/index.tsx b/packages/anastasis-webui/src/utils/index.tsx
index 63bed9392..f87dd7f13 100644
--- a/packages/anastasis-webui/src/utils/index.tsx
+++ b/packages/anastasis-webui/src/utils/index.tsx
@@ -54,7 +54,7 @@ export function createExample<Props>(
discoverMore: noop,
discoverStart: noop,
discoveryState: {
- state: "none",
+ state: "finished",
},
currentError: undefined,
back: noop,
@@ -204,14 +204,17 @@ const base = {
} as AuthenticationProviderStatusOk,
"http://localhost:8087/": {
+ status: "error",
code: 8414,
hint: "request to provider failed",
} as AuthenticationProviderStatusError,
"http://localhost:8088/": {
+ status: "error",
code: 8414,
hint: "request to provider failed",
} as AuthenticationProviderStatusError,
"http://localhost:8089/": {
+ status: "error",
code: 8414,
hint: "request to provider failed",
} as AuthenticationProviderStatusError,
diff --git a/packages/anastasis-webui/watch/serve.sh b/packages/anastasis-webui/watch/serve.sh
index c395c6a64..f4e9595d5 100755
--- a/packages/anastasis-webui/watch/serve.sh
+++ b/packages/anastasis-webui/watch/serve.sh
@@ -1,4 +1,7 @@
#!/bin/bash
+#clean up
+rm /tmp/send_signal
+
socat TCP-LISTEN:8003,fork,reuseaddr,keepalive EXEC:"./watch/reply.sh"