diff --git a/src/components/Dashboard/Dashboard.tsx b/src/components/Dashboard/Dashboard.tsx index f7e4167..2e78411 100644 --- a/src/components/Dashboard/Dashboard.tsx +++ b/src/components/Dashboard/Dashboard.tsx @@ -228,7 +228,7 @@ export default function Dashboard(props: DashboardProps) { - + diff --git a/src/interface.ts b/src/interface.ts index 329ee59..566cba9 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -218,6 +218,9 @@ export interface ConnectionDetailData { ipv4_route: string ipv6_route: string tunnel_endpoint_router_ip?: TunnelEndPointRouterIPTemplateData + ix?: string + ix_peer_type?: string + ix_vlan_id?: string } export interface BGPRouterDetailData { @@ -292,6 +295,12 @@ export interface ConnectionTemplateData { is_l3: boolean } +export interface IXTemplateData { + name: string + ipv4_address: string + ipv6_address: string +} + export interface GroupDetailData { ID: number CreatedAt: string @@ -349,6 +358,7 @@ export interface TemplateData { mail_template?: MailTemplateData[] member_type?: MemberTypeTemplateData[] payment_membership?: PaymentMembershipTemplate[] + ix?: IXTemplateData[] } export interface MemoAddData { @@ -435,6 +445,11 @@ export interface ConnectionAddData { noc_id: number term_ip: string monitor: boolean + ix?: string + ix_peer_type?: string + ix_vlan_id?: string + link_v4_your?: string + link_v6_your?: string } export interface ChatData { @@ -601,6 +616,7 @@ export const DefaultTemplateData: TemplateData = { user: undefined, group: undefined, member_type: undefined, + ix: undefined, } export const DefaultGroupDetailData: GroupDetailData = { @@ -781,6 +797,9 @@ export const DefaultConnectionDetailData: ConnectionDetailData = { comment: '', service: undefined, tunnel_endpoint_router_ip: undefined, + ix: '', + ix_peer_type: '', + ix_vlan_id: '', } export const DefaultConnectionDetailDataArray: ConnectionDetailData[] = [ diff --git a/src/pages/Add/ConnectionAdd.tsx b/src/pages/Add/ConnectionAdd.tsx index c8293a2..1990a53 100644 --- a/src/pages/Add/ConnectionAdd.tsx +++ b/src/pages/Add/ConnectionAdd.tsx @@ -57,6 +57,11 @@ export default function ConnectionAdd() { const isNeedInternet = (value: string) => template.connections?.find((ct) => ct.type === value)?.need_internet ?? false + const isIXConnection = (value: string) => value === 'IXP' + const isCrossConnect = (value: string) => + template.connections?.find((ct) => ct.type === value)?.need_cross_connect ?? + false + const isNeedBGP = () => template.services?.find( (serviceTemplate) => serviceTemplate.type === serviceType @@ -82,9 +87,13 @@ export default function ConnectionAdd() { connection_type: Yup.string() .required('接続情報を選択してください') .min(1, '正しく選択してください'), - preferred_ap: Yup.string() - .required('希望接続場所を選択してください') - .min(1, '正しく選択してください'), + preferred_ap: Yup.string().when('connection_type', { + is: (value: string) => !isIXConnection(value) && !isCrossConnect(value), + then: (value) => + value + .required('希望接続場所を選択してください') + .min(1, '正しく選択してください'), + }), monitor: Yup.bool(), comment: Yup.string(), @@ -136,6 +145,42 @@ export default function ConnectionAdd() { is: () => isNeedBGP() && isIPv6Route(), then: (value) => value.max(200), }), + + ix: Yup.string().when('connection_type', { + is: (value: string) => isIXConnection(value), + then: (value) => value.required('IXを選択してください'), + }), + ix_peer_type: Yup.string().when('connection_type', { + is: (value: string) => isIXConnection(value), + then: (value) => value.required('ピアリングタイプを選択してください'), + }), + ix_vlan_id: Yup.string().when(['connection_type', 'ix_peer_type'], { + is: (connectionType: string, peerType: string) => + isIXConnection(connectionType) && peerType === 'PI/CUG', + then: (value) => value.required('VLAN-IDを入力してください'), + }), + link_v4_your: Yup.string().when( + ['connection_type', 'ipv4_route', 'ix_peer_type'], + { + is: (connectionType: string, ipv4Route: string, peerType: string) => + isIXConnection(connectionType) && + ipv4Route && + ipv4Route !== '' && + peerType !== 'PI/CUG', + then: (value) => value.required('IPv4アドレスを入力してください'), + } + ), + link_v6_your: Yup.string().when( + ['connection_type', 'ipv6_route', 'ix_peer_type'], + { + is: (connectionType: string, ipv6Route: string, peerType: string) => + isIXConnection(connectionType) && + ipv6Route && + ipv6Route !== '' && + peerType !== 'PI/CUG', + then: (value) => value.required('IPv6アドレスを入力してください'), + } + ), }) const { @@ -160,6 +205,11 @@ export default function ConnectionAdd() { term_ip: '', monitor: false, comment: '', + ix: '', + ix_peer_type: '', + ix_vlan_id: '', + link_v4_your: '', + link_v6_your: '', }, }) @@ -167,14 +217,17 @@ export default function ConnectionAdd() { const ipv4Route = watch('ipv4_route') const ipv6Route = watch('ipv6_route') const ntt = watch('ntt') + const ixPeerType = watch('ix_peer_type') - const onSubmit = (data: any, e: any) => { + const onSubmit = (data: any) => { const request: any = { connection_type: data.connection_type, - preferred_ap: data.preferred_ap, monitor: data.monitor, comment: data.comment, } + if (data.preferred_ap) { + request.preferred_ap = data.preferred_ap + } if (data.comment_type) { request.comment_type = data.comment_type } @@ -193,11 +246,26 @@ export default function ConnectionAdd() { request.ipv4_route = data.ipv4_route_comment } if (data.ipv6_route) { - request.ipv6_route = data.ipv4_route + request.ipv6_route = data.ipv6_route } if (data.ipv6_route_comment) { request.ipv6_route = data.ipv6_route_comment } + if (data.ix) { + request.ix = data.ix + } + if (data.ix_peer_type) { + request.ix_peer_type = data.ix_peer_type + } + if (data.ix_vlan_id) { + request.ix_vlan_id = data.ix_vlan_id + } + if (data.link_v4_your) { + request.link_v4_your = data.link_v4_your + } + if (data.link_v6_your) { + request.link_v6_your = data.link_v6_your + } // check if (serviceID <= 0) { @@ -392,9 +460,7 @@ export default function ConnectionAdd() { > {template.connections?.map( (map) => - (map.is_l2 || - (isNeedRoute() && map.is_l2) || - (isNeedRoute() && map.is_l3)) && ( + (map.is_l2 || (isNeedRoute() && map.is_l3)) && ( 2.1. その他
{' '} - Cross - Connectを選択された方は以下のフォームに詳しい情報(ラック情報など)をご記入ください。 + 直接接続を選択された方は以下のフォームに詳しい情報(ラック情報など)をご記入ください。
{errors?.connection_comment && @@ -437,41 +502,169 @@ export default function ConnectionAdd() { )} - - - - 3.1. 希望接続場所をお選びください - - - {errors?.preferred_ap && errors.preferred_ap?.message} - - ( - - )} - /> - -
-
- (当団体のNOC一覧は https://www.homenoc.ad.jp/en/tech/backbone/ - をご覧ください) -
-
+ {isIXConnection(connectionType) && ( + + + + 2.2. 接続するIXを選択してください + + + {errors?.ix && errors.ix?.message} + + ( + + )} + /> + + + )} + {isIXConnection(connectionType) && ( + + + + 2.3. ピアリングタイプを選択してください + + + {errors?.ix_peer_type && errors.ix_peer_type?.message} + + ( + field.onChange(e.target.value)} + value={field.value} + > + } + label="パブリック(通常のピアリングLAN)" + /> + } + label="PI/CUG(プライベートピアリング)" + /> + + )} + /> + + + )} + {isIXConnection(connectionType) && ixPeerType === 'PI/CUG' && ( + + + 2.4. VLAN-ID +
PI/CUGのVLAN-IDを入力してください
+ + {errors?.ix_vlan_id && errors.ix_vlan_id?.message} + + +
+
+ )} + {isIXConnection(connectionType) && ixPeerType !== 'PI/CUG' && ( + + + + 2.5. IX接続アドレス(貴団体側) + +
IX接続で使用する貴団体側のIPアドレスを入力してください
+ + {errors?.link_v4_your && errors.link_v4_your?.message} + + + + {errors?.link_v6_your && errors.link_v6_your?.message} + + +
+
+ )} + {!isIXConnection(connectionType) && !isCrossConnect(connectionType) && ( + + + + 3.1. 希望接続場所をお選びください + + + {errors?.preferred_ap && errors.preferred_ap?.message} + + ( + + )} + /> + +
+
+ (当団体のNOC一覧は https://www.homenoc.ad.jp/en/tech/backbone/ + をご覧ください) +
+
+ )} {isNeedInternet(connectionType) && ( 3.4. 接続終端場所にNTTフレッツ光が利用可能かをお知らせください -
- 接続方式に構内接続をご希望の方は何も選択せず次の項目に進んでください -
当団体ではトンネル接続を利用する場合、フレッツのIPoE(IPv6)接続をご利用頂くことを推奨しております。
diff --git a/src/pages/Add/ServiceAdd.tsx b/src/pages/Add/ServiceAdd.tsx index 3fe9454..d88b976 100644 --- a/src/pages/Add/ServiceAdd.tsx +++ b/src/pages/Add/ServiceAdd.tsx @@ -275,15 +275,17 @@ export default function ServiceAdd() { .moreThan(0, '正しいAS番号を入力してください'), }), // is_ipv4 - route_v4: Yup.string().when({ - is: isIpv4, - then: (value) => - value - .required('ネットワーク名を入力してください') - .min(1, 'Network Name must be at least 1 characters') - .max(12, 'Network Name must not exceed 12 characters') - .matches(v4NetworkNameRegExp, '文字形式に誤りがあります。'), - }), + route_v4: Yup.string() + .test( + 'route_v4_required', + 'Network Name is required', + (value) => !isIpv4 || (value !== undefined && value !== '') + ) + .test( + 'route_v4_format', + 'Use only uppercase letters, numbers, and hyphens (max 12 characters)', + (value) => !isIpv4 || !value || (value.length <= 12 && v4NetworkNameRegExp.test(value)) + ), // L2, L3 Static, L3 BGP, CoLocation plan: Yup.array().when('service_type', { is: (value: string) => isIpv4 && isNeedJPNIC(value), @@ -299,15 +301,17 @@ export default function ServiceAdd() { }), // is_ipv6 - route_v6: Yup.string().when({ - is: isIpv6, - then: (value) => - value - .required('ネットワーク名を入力してください') - .min(1, 'Network Name must be at least 1 characters') - .max(12, 'Network Name must not exceed 12 characters') - .matches(v6NetworkNameRegExp, '文字形式に誤りがあります。'), - }), + route_v6: Yup.string() + .test( + 'route_v6_required', + 'Network Name is required', + (value) => !isIpv6 || (value !== undefined && value !== '') + ) + .test( + 'route_v6_format', + 'Use only uppercase letters, numbers, and hyphens (max 12 characters)', + (value) => !isIpv6 || !value || (value.length <= 12 && v6NetworkNameRegExp.test(value)) + ), }) const { @@ -680,7 +684,7 @@ export default function ServiceAdd() { {isIpv4 && getBool(isNeedJPNIC(serviceType)) && (

- (英大文字, 数字, "-" (ハイフン) のみを用いて12文字以上) + (英大文字, 数字, "-" (ハイフン) のみを用いて12文字以内)

)} diff --git a/src/pages/Add/reg.ts b/src/pages/Add/reg.ts index 9e92a97..d83496c 100644 --- a/src/pages/Add/reg.ts +++ b/src/pages/Add/reg.ts @@ -1,4 +1,4 @@ export const phoneRegExp = /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/ -export const v4NetworkNameRegExp = /^[\dA-Z\\-]+$/ -export const v6NetworkNameRegExp = /^[\dA-Z\\-]+$/ +export const v4NetworkNameRegExp = /^[A-Z0-9-]+$/ +export const v6NetworkNameRegExp = /^[A-Z0-9-]+$/ diff --git a/src/pages/Connection/ConnectionDetail/ConnectionDetail.tsx b/src/pages/Connection/ConnectionDetail/ConnectionDetail.tsx index 8e67c97..e64b393 100644 --- a/src/pages/Connection/ConnectionDetail/ConnectionDetail.tsx +++ b/src/pages/Connection/ConnectionDetail/ConnectionDetail.tsx @@ -432,6 +432,26 @@ export function ConnectionStatus(props: { connection: ConnectionDetailData }) { /> )}
+ {connection.ix && ( + +

IX接続

+ + {connection.ix_peer_type && ( + + )} + {connection.ix_peer_type === 'PI/CUG' && connection.ix_vlan_id && ( + + )} +
+ )}

Date

@@ -567,18 +587,42 @@ export function ConnectionUserDisplay(props: { ?.name ?? ''} - - 接続NOC - {getNOCName()} - - - トンネル終端アドレス(貴団体側) - {connection.term_ip} - - - トンネル終端アドレス(当団体側) - {connection.tunnel_endpoint_router_ip?.ip} - + {connection.connection_type !== 'IXP' && ( + + 接続NOC + {getNOCName()} + + )} + {connection.ix && ( + <> + + IX + {connection.ix} + + + ピアリングタイプ + {connection.ix_peer_type} + + {connection.ix_peer_type === 'PI/CUG' && connection.ix_vlan_id && ( + + VLAN-ID + {connection.ix_vlan_id} + + )} + + )} + {connection.connection_type !== 'IXP' && ( + <> + + トンネル終端アドレス(貴団体側) + {connection.term_ip} + + + トンネル終端アドレス(当団体側) + {connection.tunnel_endpoint_router_ip?.ip} + + + )} 当団体との間の境界アドレス @@ -858,6 +902,88 @@ export function ConnectionEtc2(props: {
+
+ +

IX接続情報

+
+ + + IX + + + + + + ピアリングタイプ + + + + + + { + setConnectionCopy({ + ...connectionCopy, + ix_vlan_id: event.target.value, + }) + }} + value={connectionCopy.ix_vlan_id ?? ''} + /> + +