1diff --git a/src/deploy/vpn-tunnel/index.ts b/src/deploy/vpn-tunnel/index.ts
2index 32ef92bb..3e3ad310 100644
3--- a/src/deploy/vpn-tunnel/index.ts
4+++ b/src/deploy/vpn-tunnel/index.ts
5@@ -2,9 +2,9 @@ import { api } from "@app/api";
6 import { createSelector } from "@app/fx";
7 import { defaultEntity, extractIdFromLink } from "@app/hal";
8 import { WebState, schema } from "@app/schema";
9-import { camelizeKeys } from "@app/string-utils";
10 import {
11 DeployVpnTunnel,
12+ DeployVpnTunnelAttributes,
13 DeployVpnTunnelState,
14 LinkResponse,
15 } from "@app/types";
16@@ -24,7 +24,7 @@ export interface DeployVpnTunnelResponse {
17 peer_gateway: string;
18 peer_networks: string[];
19 state: string;
20- tunnel_attributes: { [key: string]: any };
21+ tunnel_attributes?: DeployVpnTunnelAttributes;
22 created_at: string;
23 updated_at: string;
24 _links: {
25@@ -54,8 +54,8 @@ export const defaultVpnTunnelResponse = (
26 state: "unknown",
27 tunnel_attributes: {
28 connections: {},
29- routedConnections: {},
30- securityAssociations: {},
31+ routed_connections: {},
32+ security_associations: {},
33 },
34 created_at: now,
35 updated_at: now,
36@@ -65,6 +65,19 @@ export const defaultVpnTunnelResponse = (
37 };
38 };
39
40+const deserializeVpnTunnelAttributes = (
41+ tun: DeployVpnTunnelResponse["tunnel_attributes"],
42+): DeployVpnTunnelAttributes => {
43+ if (!tun) {
44+ return {
45+ connections: {},
46+ routed_connections: {},
47+ security_associations: {},
48+ };
49+ }
50+ return tun;
51+};
52+
53 export const deserializeDeployVpnTunnel = (
54 payload: DeployVpnTunnelResponse,
55 ): DeployVpnTunnel => {
56@@ -86,7 +99,7 @@ export const deserializeDeployVpnTunnel = (
57 updatedAt: payload.updated_at,
58 stackId: extractIdFromLink(payload._links.stack),
59 state: payload.state as DeployVpnTunnelState,
60- tunnelAttributes: camelizeKeys(payload.tunnel_attributes),
61+ tunnelAttributes: deserializeVpnTunnelAttributes(payload.tunnel_attributes),
62 };
63 };
64
65diff --git a/src/schema/factory.ts b/src/schema/factory.ts
66index 2a27cb44..53e48365 100644
67--- a/src/schema/factory.ts
68+++ b/src/schema/factory.ts
69@@ -577,8 +577,8 @@ export const defaultDeployVpnTunnel = (
70 state: "unknown",
71 tunnelAttributes: {
72 connections: {},
73- routedConnections: {},
74- securityAssociations: {},
75+ routed_connections: {},
76+ security_associations: {},
77 },
78 createdAt: now,
79 updatedAt: now,
80diff --git a/src/string-utils/index.ts b/src/string-utils/index.ts
81index 4733964d..17123530 100644
82--- a/src/string-utils/index.ts
83+++ b/src/string-utils/index.ts
84@@ -48,19 +48,6 @@ export const camelToSnakeCase = (str: string) =>
85 export const snakeToCamelCase = (str: string) =>
86 str.replace(/_(\w)/g, (_substr, letter) => letter.toUpperCase());
87
88-export const camelizeKeys: any = (obj: any) => {
89- if (obj != null && obj.constructor === Object) {
90- return Object.keys(obj).reduce(
91- (result, key) => ({
92- ...result,
93- [snakeToCamelCase(key)]: camelizeKeys(obj[key]),
94- }),
95- {},
96- );
97- }
98- return obj;
99-};
100-
101 export interface TextVal<
102 M extends { [key: string]: unknown } = {
103 [key: string]: unknown;
104diff --git a/src/types/deploy.ts b/src/types/deploy.ts
105index 578abeb1..03c9f20a 100644
106--- a/src/types/deploy.ts
107+++ b/src/types/deploy.ts
108@@ -502,32 +502,32 @@ export interface DeployVpnTunnel extends Timestamps {
109 export type DeployVpnTunnelState = "up" | "down" | "partial" | "unknown";
110
111 export interface DeployVpnTunnelAttributes {
112- connections: { [key: string]: DeployVpnTunnelConnection };
113- routedConnections: { [key: string]: DeployVpnTunnelRoutedConnection };
114- securityAssociations: { [key: string]: DeployVpnTunnelSecurityAssociation };
115-}
116-
117-export interface DeployVpnTunnelConnection {
118- name: string;
119- localAddress: string;
120- remoteAddress: string;
121- state: DeployVpnTunnelState;
122-}
123-
124-export interface DeployVpnTunnelRoutedConnection {
125- name: string;
126- localAddress: string;
127- remoteAddress: string;
128- reqId: string;
129-}
130-
131-export interface DeployVpnTunnelSecurityAssociation {
132- id: string;
133- local: string;
134- remote: string;
135- status: string;
136- statusTime: string;
137- linkedRoutes: string[];
138+ connections: {
139+ [key: string]: {
140+ name: string;
141+ local_address: string;
142+ remote_address: string;
143+ state: string;
144+ };
145+ } | null;
146+ routed_connections: {
147+ [key: string]: {
148+ name: string;
149+ local_address: string;
150+ remote_address: string;
151+ req_id: string;
152+ };
153+ } | null;
154+ security_associations: {
155+ [key: string]: {
156+ id: string;
157+ local: string;
158+ remote: string;
159+ status: string;
160+ status_time: string;
161+ linked_routes: string[];
162+ };
163+ } | null;
164 }
165
166 export interface DeployBackup {
167diff --git a/src/ui/pages/stack-detail-vpn-tunnels.tsx b/src/ui/pages/stack-detail-vpn-tunnels.tsx
168index 7f3791d6..0053e76e 100644
169--- a/src/ui/pages/stack-detail-vpn-tunnels.tsx
170+++ b/src/ui/pages/stack-detail-vpn-tunnels.tsx
171@@ -163,11 +163,11 @@ export const StackDetailVpnTunnelsPage = () => {
172 </THead>
173
174 <TBody>
175- {Object.values(vpnTunnel.tunnelAttributes.connections).map(
176+ {Object.values(vpnTunnel.tunnelAttributes.connections || {}).map(
177 (connection) => (
178 <Tr key={connection.name}>
179- <Td>{connection.localAddress}</Td>
180- <Td>{connection.remoteAddress}</Td>
181+ <Td>{connection.local_address}</Td>
182+ <Td>{connection.remote_address}</Td>
183 <Td>{connection.state}</Td>
184 </Tr>
185 ),