Skip to content

Commit 2c468de

Browse files
committed
Add temporary legacy request page
1 parent 977b022 commit 2c468de

File tree

6 files changed

+258
-2
lines changed

6 files changed

+258
-2
lines changed

src/lib/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Chain } from 'viem';
44
import { base, baseSepolia } from 'wagmi/chains';
55

66
type Config = {
7+
deeployUrl: string;
78
backendUrl: string;
89
oraclesUrl: string;
910
r1ContractAddress: EthAddress;
@@ -27,6 +28,7 @@ const configs: {
2728
[key in 'mainnet' | 'testnet' | 'devnet']: Config;
2829
} = {
2930
mainnet: {
31+
deeployUrl: 'https://deeploy-api.ratio1.ai',
3032
backendUrl: 'https://dapp-api.ratio1.ai',
3133
oraclesUrl: 'https://oracle.ratio1.ai',
3234
r1ContractAddress: '0x6444C6c2D527D85EA97032da9A7504d6d1448ecF',
@@ -46,6 +48,7 @@ const configs: {
4648
ND_LICENSE_CAP: 1575_188843457943924200n,
4749
},
4850
testnet: {
51+
deeployUrl: 'https://testnet-deeploy-api.ratio1.ai',
4952
backendUrl: 'https://testnet-dapp-api.ratio1.ai',
5053
oraclesUrl: 'https://testnet-oracle.ratio1.ai',
5154
r1ContractAddress: '0xCC96f389F45Fc08b4fa8e2bC4C7DA9920292ec64',
@@ -65,6 +68,7 @@ const configs: {
6568
ND_LICENSE_CAP: 1575_188843457943924200n,
6669
},
6770
devnet: {
71+
deeployUrl: 'https://devnet-deeploy-api.ratio1.ai',
6872
backendUrl: 'https://devnet-dapp-api.ratio1.ai',
6973
oraclesUrl: 'https://devnet-oracle.ratio1.ai',
7074
r1ContractAddress: '0x07C5678F0f4aC347496eAA8D6031b37FF3402CE5',

src/lib/routes/route-paths.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const routePath = {
55
// Children of /deeploys
66
dashboard: 'dashboard',
77
deeployApp: 'deeploy-app',
8+
legacyRequester: 'legacy-requester',
89
//
910
account: '/account',
1011
docs: '/docs',

src/lib/routes/routes.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import PrivacyPolicy from '@pages/compliance/PrivacyPolicy';
44
import TermsAndConditions from '@pages/compliance/T&C';
55
import Dashboard from '@pages/deeploys/Dashboard';
66
import DeeployApp from '@pages/deeploys/DeeployApp';
7+
import LegacyRequester from '@pages/deeploys/LegacyRequester';
78
import Docs from '@pages/Docs';
89
import Home from '@pages/Home';
910
import Support from '@pages/Support';
@@ -58,6 +59,11 @@ export const routeInfo = {
5859
description: 'Create and configure a new app for deployment',
5960
routeTitle: 'Deeploy App',
6061
},
62+
[`${routePath.deeploys}/${routePath.legacyRequester}`]: {
63+
title: 'Legacy Requester',
64+
description: 'Legacy interface for requesting deployments',
65+
routeTitle: 'Legacy Requester',
66+
},
6167
[routePath.account]: {
6268
title: 'Account',
6369
description: 'Manage your account & billing settings',
@@ -109,6 +115,10 @@ export const routes: AppRoute[] = [
109115
</DeploymentProvider>
110116
),
111117
},
118+
{
119+
path: routePath.legacyRequester,
120+
page: LegacyRequester,
121+
},
112122
],
113123
},
114124
{

src/lib/utils.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,20 @@ export const arrayAverage = (numbers: number[]): number => {
7474
if (numbers.length === 0) return 0;
7575
return numbers.reduce((sum, num) => sum + num, 0) / numbers.length;
7676
};
77+
78+
export function deepSort(obj: any): any {
79+
if (Array.isArray(obj)) {
80+
return obj.map(deepSort);
81+
} else if (obj && typeof obj === 'object' && !Array.isArray(obj)) {
82+
return Object.keys(obj)
83+
.sort()
84+
.reduce(
85+
(acc, key) => {
86+
acc[key] = deepSort(obj[key]);
87+
return acc;
88+
},
89+
{} as Record<string, any>,
90+
);
91+
}
92+
return obj;
93+
}
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
import { config, environment } from '@lib/config';
2+
import { deepSort } from '@lib/utils';
3+
import axios from 'axios';
4+
import { useEffect, useState } from 'react';
5+
import { useAccount, useSignMessage } from 'wagmi';
6+
7+
function buildMessage(data: Record<string, any>): string {
8+
const cleaned = structuredClone(data);
9+
delete cleaned.address;
10+
delete cleaned.signature;
11+
12+
const sorted = deepSort(cleaned);
13+
const json = JSON.stringify(sorted, null, 1).replaceAll('": ', '":');
14+
return `Please sign this message for Deeploy: ${json}`;
15+
}
16+
17+
async function _doPost(endpoint: string, body: any) {
18+
const { data } = await axios.post(endpoint, body);
19+
return data.result;
20+
}
21+
22+
function LegacyRequester() {
23+
const { address } = useAccount();
24+
const [userInput, setUserInput] = useState<string>('');
25+
const [responseInput, setResponseInput] = useState<string>('');
26+
const [endpoint, setEndpoint] = useState<string>('/create_pipeline');
27+
const { signMessageAsync } = useSignMessage();
28+
29+
useEffect(() => {
30+
const nonce = `0x${Date.now().toString(16)}`;
31+
32+
if (endpoint === '/get_apps') {
33+
setUserInput(
34+
JSON.stringify(
35+
{
36+
request: {
37+
nonce,
38+
},
39+
},
40+
null,
41+
2,
42+
),
43+
);
44+
}
45+
46+
if (endpoint === '/create_pipeline') {
47+
setUserInput(
48+
JSON.stringify(
49+
{
50+
request: {
51+
app_alias: 'some_app_name',
52+
plugin_signature: 'SOME_PLUGIN_01',
53+
nonce,
54+
target_nodes: ['0xai_node_1', '0xETH_ADDR_NODE_2'],
55+
target_nodes_count: 0,
56+
app_params: {
57+
CONTAINER_RESOURCES: {
58+
cpu: 1,
59+
memory: '512m',
60+
},
61+
62+
CR: 'docker.io',
63+
CR_PASSWORD: 'password',
64+
CR_USER: 'user',
65+
RESTART_POLICY: 'always',
66+
IMAGE_PULL_POLICY: 'always',
67+
68+
NGROK_EDGE_LABEL: null,
69+
NGROK_USE_API: false,
70+
71+
ENV: {
72+
ENV1: 'value1',
73+
ENV2: 'value2',
74+
ENV3: 'value3',
75+
ENV4: 'value4',
76+
},
77+
DYNAMIC_ENV: {
78+
ENV5: [
79+
{
80+
type: 'static',
81+
value: 'http://',
82+
},
83+
{
84+
type: 'host_ip',
85+
value: null,
86+
},
87+
{
88+
type: 'static',
89+
value: ':5080/test_api_endpoint',
90+
},
91+
],
92+
ENV6: [
93+
{
94+
type: 'host_ip',
95+
value: 'http://',
96+
},
97+
],
98+
},
99+
IMAGE: 'repo/image:tag',
100+
OTHER_PARAM1: 'value1',
101+
OTHER_PARAM2: 'value2',
102+
OTHER_PARAM3: 'value3',
103+
OTHER_PARAM4: 'value4',
104+
OTHER_PARAM5: 'value5',
105+
PORT: null,
106+
},
107+
pipeline_input_type: 'void',
108+
pipeline_input_uri: null,
109+
chainstore_response: false,
110+
},
111+
},
112+
null,
113+
2,
114+
),
115+
);
116+
}
117+
118+
if (endpoint === '/delete_pipeline') {
119+
setUserInput(
120+
JSON.stringify(
121+
{
122+
request: {
123+
app_id: 'target_app_name_id_returned_by_get_apps_or_create_pipeline',
124+
target_nodes: ['0xai_target_node_1', '0xai_target_node_2'],
125+
nonce,
126+
},
127+
},
128+
null,
129+
2,
130+
),
131+
);
132+
}
133+
}, [endpoint]);
134+
135+
return (
136+
<div>
137+
<h3>Environment: {environment}</h3>
138+
<h3>Endpoint:</h3>
139+
<select
140+
value={endpoint}
141+
onChange={(e) => setEndpoint(e.target.value)}
142+
className="rounded-md border border-gray-300 p-2"
143+
style={{
144+
width: '100%',
145+
height: '50px',
146+
marginBottom: '1rem',
147+
}}
148+
>
149+
<option value="/create_pipeline">/create_pipeline</option>
150+
<option value="/delete_pipeline">/delete_pipeline</option>
151+
<option value="/get_apps">/get_apps</option>
152+
</select>
153+
<div
154+
style={{
155+
display: 'flex',
156+
flexDirection: 'row',
157+
gap: '1rem',
158+
}}
159+
>
160+
<textarea
161+
placeholder="Enter JSON"
162+
value={userInput}
163+
onChange={(e) => setUserInput(e.target.value)}
164+
className="rounded-md border border-gray-300 p-2"
165+
style={{
166+
width: '100%',
167+
height: '60vh',
168+
marginBottom: '1rem',
169+
}}
170+
/>
171+
<textarea
172+
placeholder="Enter JSON"
173+
value={responseInput}
174+
onChange={(e) => setResponseInput(e.target.value)}
175+
className="rounded-md border border-gray-300 p-2"
176+
style={{
177+
width: '100%',
178+
height: '60vh',
179+
marginBottom: '1rem',
180+
}}
181+
/>
182+
</div>
183+
<button
184+
onClick={async () => {
185+
try {
186+
const parsed = JSON.parse(userInput).request;
187+
const message = buildMessage(parsed);
188+
console.log(JSON.stringify(message));
189+
const signature = await signMessageAsync({
190+
account: address,
191+
message,
192+
});
193+
console.log('Signature:', signature);
194+
195+
const request = {
196+
...parsed,
197+
EE_ETH_SIGN: signature,
198+
EE_ETH_SENDER: address,
199+
};
200+
console.log('Data to send:', request);
201+
202+
const res = await _doPost(`${config.deeployUrl}${endpoint}`, {
203+
request,
204+
});
205+
console.log('Response:', res);
206+
setResponseInput(JSON.stringify(res, null, 2));
207+
} catch (error) {
208+
console.error('Error:', error);
209+
alert(`Error: ${error}`);
210+
}
211+
}}
212+
className="rounded-md bg-blue-500 p-2 text-white transition-colors hover:bg-blue-600"
213+
style={{
214+
width: '100%',
215+
height: '50px',
216+
}}
217+
>
218+
Sign and send
219+
</button>
220+
</div>
221+
);
222+
}
223+
224+
export default LegacyRequester;

tsconfig.app.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
"@schemas/*": ["schemas/*"]
1616
},
1717

18-
"target": "ES2020",
18+
"target": "ES2021",
1919
"useDefineForClassFields": true,
20-
"lib": ["ES2020", "DOM", "DOM.Iterable"],
20+
"lib": ["ES2021", "DOM", "DOM.Iterable"],
2121
"module": "ESNext",
2222
"skipLibCheck": true,
2323

0 commit comments

Comments
 (0)