mirror of
https://github.com/MAKS-IT-COM/maksit-certs-ui.git
synced 2025-12-31 04:00:03 +01:00
(refactor): fe layout review, be models review
This commit is contained in:
parent
7d7ebd298a
commit
322845d82d
6
src/ClientApp/.vscode/settings.json
vendored
6
src/ClientApp/.vscode/settings.json
vendored
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
// "editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
// "source.fixAll.eslint": true
|
"source.fixAll.eslint": true
|
||||||
// },
|
},
|
||||||
"[javascript]": {
|
"[javascript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,7 +2,7 @@ enum ApiRoutes {
|
|||||||
CACHE_ACCOUNTS = 'api/cache/accounts',
|
CACHE_ACCOUNTS = 'api/cache/accounts',
|
||||||
CACHE_ACCOUNT = 'api/cache/account/{accountId}',
|
CACHE_ACCOUNT = 'api/cache/account/{accountId}',
|
||||||
CACHE_ACCOUNT_CONTACTS = 'api/cache/account/{accountId}/contacts',
|
CACHE_ACCOUNT_CONTACTS = 'api/cache/account/{accountId}/contacts',
|
||||||
CACHE_ACCOUNT_CONTACT = 'api/cache/account/{accountId}/contacts/{index}',
|
CACHE_ACCOUNT_CONTACT = 'api/cache/account/{accountId}/contact/{index}',
|
||||||
CACHE_ACCOUNT_HOSTNAMES = 'api/cache/account/{accountId}/hostnames'
|
CACHE_ACCOUNT_HOSTNAMES = 'api/cache/account/{accountId}/hostnames'
|
||||||
|
|
||||||
// CERTS_FLOW_CONFIGURE_CLIENT = `api/CertsFlow/ConfigureClient`,
|
// CERTS_FLOW_CONFIGURE_CLIENT = `api/CertsFlow/ConfigureClient`,
|
||||||
|
|||||||
@ -21,17 +21,20 @@ export default function Page() {
|
|||||||
const {
|
const {
|
||||||
value: newContact,
|
value: newContact,
|
||||||
error: contactError,
|
error: contactError,
|
||||||
handleChange: handleContactChange
|
handleChange: handleContactChange,
|
||||||
} = useValidation({
|
reset: resetContact
|
||||||
|
} = useValidation<string>({
|
||||||
initialValue: '',
|
initialValue: '',
|
||||||
validateFn: isValidEmail,
|
validateFn: isValidEmail,
|
||||||
errorMessage: 'Invalid email format.'
|
errorMessage: 'Invalid email format.'
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
value: newHostname,
|
value: newHostname,
|
||||||
error: hostnameError,
|
error: hostnameError,
|
||||||
handleChange: handleHostnameChange
|
handleChange: handleHostnameChange,
|
||||||
} = useValidation({
|
reset: resetHostname
|
||||||
|
} = useValidation<string>({
|
||||||
initialValue: '',
|
initialValue: '',
|
||||||
validateFn: isValidHostname,
|
validateFn: isValidHostname,
|
||||||
errorMessage: 'Invalid hostname format.'
|
errorMessage: 'Invalid hostname format.'
|
||||||
@ -110,25 +113,25 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addContact = (accountId: string) => {
|
const addContact = (accountId: string) => {
|
||||||
if (newContact.trim() === '' || contactError) {
|
if (newContact === '' || contactError) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
accounts
|
accounts
|
||||||
.find((account) => account.accountId === accountId)
|
.find((account) => account.accountId === accountId)
|
||||||
?.contacts.includes(newContact.trim())
|
?.contacts.includes(newContact)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
setAccounts(
|
setAccounts(
|
||||||
accounts.map((account) =>
|
accounts.map((account) =>
|
||||||
account.accountId === accountId
|
account.accountId === accountId
|
||||||
? { ...account, contacts: [...account.contacts, newContact.trim()] }
|
? { ...account, contacts: [...account.contacts, newContact] }
|
||||||
: account
|
: account
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
handleContactChange('')
|
resetContact()
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteHostname = (accountId: string, hostname: string) => {
|
const deleteHostname = (accountId: string, hostname: string) => {
|
||||||
@ -153,14 +156,14 @@ export default function Page() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const addHostname = (accountId: string) => {
|
const addHostname = (accountId: string) => {
|
||||||
if (newHostname.trim() === '' || hostnameError) {
|
if (newHostname === '' || hostnameError) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
accounts
|
accounts
|
||||||
.find((account) => account.accountId === accountId)
|
.find((account) => account.accountId === accountId)
|
||||||
?.hostnames.some((h) => h.hostname === newHostname.trim())
|
?.hostnames.some((h) => h.hostname === newHostname)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -172,7 +175,7 @@ export default function Page() {
|
|||||||
hostnames: [
|
hostnames: [
|
||||||
...account.hostnames,
|
...account.hostnames,
|
||||||
{
|
{
|
||||||
hostname: newHostname.trim(),
|
hostname: newHostname,
|
||||||
expires: new Date(),
|
expires: new Date(),
|
||||||
isUpcomingExpire: false
|
isUpcomingExpire: false
|
||||||
}
|
}
|
||||||
@ -181,7 +184,7 @@ export default function Page() {
|
|||||||
: account
|
: account
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
handleHostnameChange('')
|
resetHostname()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = async (
|
const handleSubmit = async (
|
||||||
@ -279,14 +282,15 @@ export default function Page() {
|
|||||||
className="text-gray-700 flex justify-between items-center mb-2"
|
className="text-gray-700 flex justify-between items-center mb-2"
|
||||||
>
|
>
|
||||||
{contact}
|
{contact}
|
||||||
<button
|
<CustomButton
|
||||||
|
type="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
deleteContact(account.accountId, contact)
|
deleteContact(account.accountId, contact)
|
||||||
}
|
}
|
||||||
className="bg-red-500 text-white px-2 py-1 rounded ml-4 h-10"
|
className="bg-red-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
<TrashIcon className="h-5 w-5 text-white" />
|
<TrashIcon className="h-5 w-5 text-white" />
|
||||||
</button>
|
</CustomButton>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@ -301,13 +305,15 @@ export default function Page() {
|
|||||||
inputClassName="border p-2 rounded w-full"
|
inputClassName="border p-2 rounded w-full"
|
||||||
errorClassName="text-red-500 text-sm mt-1"
|
errorClassName="text-red-500 text-sm mt-1"
|
||||||
className="mr-2 flex-grow"
|
className="mr-2 flex-grow"
|
||||||
/>
|
>
|
||||||
<button
|
<CustomButton
|
||||||
|
type="button"
|
||||||
onClick={() => addContact(account.accountId)}
|
onClick={() => addContact(account.accountId)}
|
||||||
className="bg-green-500 text-white p-2 rounded ml-2 h-10 flex items-center"
|
className="bg-green-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
<PlusIcon className="h-5 w-5 text-white" />
|
<PlusIcon className="h-5 w-5 text-white" />
|
||||||
</button>
|
</CustomButton>
|
||||||
|
</CustomInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -329,14 +335,15 @@ export default function Page() {
|
|||||||
: 'Not Upcoming'}
|
: 'Not Upcoming'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<CustomButton
|
||||||
|
type="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
deleteHostname(account.accountId, hostname.hostname)
|
deleteHostname(account.accountId, hostname.hostname)
|
||||||
}
|
}
|
||||||
className="bg-red-500 text-white px-2 py-1 rounded ml-4 h-10"
|
className="bg-red-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
<TrashIcon className="h-5 w-5 text-white" />
|
<TrashIcon className="h-5 w-5 text-white" />
|
||||||
</button>
|
</CustomButton>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@ -351,25 +358,27 @@ export default function Page() {
|
|||||||
inputClassName="border p-2 rounded w-full"
|
inputClassName="border p-2 rounded w-full"
|
||||||
errorClassName="text-red-500 text-sm mt-1"
|
errorClassName="text-red-500 text-sm mt-1"
|
||||||
className="mr-2 flex-grow"
|
className="mr-2 flex-grow"
|
||||||
/>
|
>
|
||||||
<button
|
<CustomButton
|
||||||
|
type="button"
|
||||||
onClick={() => addHostname(account.accountId)}
|
onClick={() => addHostname(account.accountId)}
|
||||||
className="bg-green-500 text-white p-2 rounded ml-2 h-10 flex items-center"
|
className="bg-green-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
<PlusIcon className="h-5 w-5 text-white" />
|
<PlusIcon className="h-5 w-5 text-white" />
|
||||||
</button>
|
</CustomButton>
|
||||||
|
</CustomInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between mt-4">
|
<div className="flex justify-between mt-4">
|
||||||
<button
|
<CustomButton
|
||||||
onClick={() => deleteAccount(account.accountId)}
|
onClick={() => deleteAccount(account.accountId)}
|
||||||
className="bg-red-500 text-white px-3 py-1 rounded"
|
className="bg-red-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
<TrashIcon className="h-5 w-5 text-white" />
|
<TrashIcon className="h-5 w-5 text-white" />
|
||||||
</button>
|
</CustomButton>
|
||||||
<CustomButton
|
<CustomButton
|
||||||
type="submit"
|
type="submit"
|
||||||
className="bg-green-500 text-white px-3 py-1 rounded"
|
className="bg-green-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
|
|||||||
@ -10,24 +10,26 @@ import {
|
|||||||
} from '@/hooks/useValidation'
|
} from '@/hooks/useValidation'
|
||||||
import { CustomButton, CustomInput } from '@/controls'
|
import { CustomButton, CustomInput } from '@/controls'
|
||||||
import { FaTrash, FaPlus } from 'react-icons/fa'
|
import { FaTrash, FaPlus } from 'react-icons/fa'
|
||||||
import { GetAccountResponse } from '@/models/letsEncryptServer/cache/responses/GetAccountResponse'
|
|
||||||
import { deepCopy } from '../functions'
|
import { deepCopy } from '../functions'
|
||||||
|
import {
|
||||||
interface CacheAccount {
|
PostAccountRequest,
|
||||||
description?: string
|
validatePostAccountRequest
|
||||||
contacts: string[]
|
} from '@/models/letsEncryptServer/certsFlow/PostAccountRequest'
|
||||||
hostnames: string[]
|
import App from 'next/app'
|
||||||
}
|
import { useAppDispatch } from '@/redux/store'
|
||||||
|
import { showToast } from '@/redux/slices/toastSlice'
|
||||||
|
|
||||||
const RegisterPage = () => {
|
const RegisterPage = () => {
|
||||||
const [account, setAccount] = useState<CacheAccount | null>(null)
|
const [account, setAccount] = useState<PostAccountRequest | null>(null)
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
value: newContact,
|
value: newContact,
|
||||||
error: contactError,
|
error: contactError,
|
||||||
handleChange: handleContactChange,
|
handleChange: handleContactChange,
|
||||||
reset: resetContact
|
reset: resetContact
|
||||||
} = useValidation({
|
} = useValidation<string>({
|
||||||
initialValue: '',
|
initialValue: '',
|
||||||
validateFn: isValidContact,
|
validateFn: isValidContact,
|
||||||
errorMessage: 'Invalid contact. Must be a valid email or phone number.'
|
errorMessage: 'Invalid contact. Must be a valid email or phone number.'
|
||||||
@ -38,7 +40,7 @@ const RegisterPage = () => {
|
|||||||
error: hostnameError,
|
error: hostnameError,
|
||||||
handleChange: handleHostnameChange,
|
handleChange: handleHostnameChange,
|
||||||
reset: resetHostname
|
reset: resetHostname
|
||||||
} = useValidation({
|
} = useValidation<string>({
|
||||||
initialValue: '',
|
initialValue: '',
|
||||||
validateFn: isValidHostname,
|
validateFn: isValidHostname,
|
||||||
errorMessage: 'Invalid hostname format.'
|
errorMessage: 'Invalid hostname format.'
|
||||||
@ -55,10 +57,17 @@ const RegisterPage = () => {
|
|||||||
const handleDescription = (description: string) => {}
|
const handleDescription = (description: string) => {}
|
||||||
|
|
||||||
const handleAddContact = () => {
|
const handleAddContact = () => {
|
||||||
if (newContact !== '' || contactError) return
|
if (
|
||||||
|
newContact === '' ||
|
||||||
|
account?.contacts.includes(newContact) ||
|
||||||
|
contactError !== ''
|
||||||
|
) {
|
||||||
|
resetContact()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setAccount((prev) => {
|
setAccount((prev) => {
|
||||||
const newAccount: CacheAccount =
|
const newAccount: PostAccountRequest =
|
||||||
prev !== null
|
prev !== null
|
||||||
? deepCopy(prev)
|
? deepCopy(prev)
|
||||||
: {
|
: {
|
||||||
@ -75,10 +84,17 @@ const RegisterPage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleAddHostname = () => {
|
const handleAddHostname = () => {
|
||||||
if (newHostname !== '' || hostnameError) return
|
if (
|
||||||
|
newHostname === '' ||
|
||||||
|
account?.hostnames.includes(newHostname) ||
|
||||||
|
hostnameError !== ''
|
||||||
|
) {
|
||||||
|
resetHostname()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setAccount((prev) => {
|
setAccount((prev) => {
|
||||||
const newAccount: CacheAccount =
|
const newAccount: PostAccountRequest =
|
||||||
prev !== null
|
prev !== null
|
||||||
? deepCopy(prev)
|
? deepCopy(prev)
|
||||||
: {
|
: {
|
||||||
@ -119,6 +135,17 @@ const RegisterPage = () => {
|
|||||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
|
const error = validatePostAccountRequest(account)
|
||||||
|
if (error) {
|
||||||
|
console.error(`Validation failed: ${error}`)
|
||||||
|
// dipatch toasterror
|
||||||
|
dispatch(showToast({ message: error, type: 'error' }))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpService.post<PostAccountRequest, GetAccountResponse>('', account)
|
||||||
|
|
||||||
console.log(account)
|
console.log(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,13 +175,13 @@ const RegisterPage = () => {
|
|||||||
className="text-gray-700 flex justify-between items-center mb-2"
|
className="text-gray-700 flex justify-between items-center mb-2"
|
||||||
>
|
>
|
||||||
{contact}
|
{contact}
|
||||||
<button
|
<CustomButton
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleDeleteContact(contact)}
|
onClick={() => handleDeleteContact(contact)}
|
||||||
className="bg-red-500 text-white px-2 py-1 rounded ml-4"
|
className="bg-red-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
<FaTrash />
|
<FaTrash />
|
||||||
</button>
|
</CustomButton>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@ -169,14 +196,15 @@ const RegisterPage = () => {
|
|||||||
inputClassName="border p-2 rounded w-full"
|
inputClassName="border p-2 rounded w-full"
|
||||||
errorClassName="text-red-500 text-sm mt-1"
|
errorClassName="text-red-500 text-sm mt-1"
|
||||||
className="mr-2 flex-grow"
|
className="mr-2 flex-grow"
|
||||||
/>
|
>
|
||||||
<button
|
<CustomButton
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAddContact}
|
onClick={handleAddContact}
|
||||||
className="bg-green-500 text-white p-2 rounded ml-2 h-10 flex items-center"
|
className="bg-green-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
<FaPlus />
|
<FaPlus />
|
||||||
</button>
|
</CustomButton>
|
||||||
|
</CustomInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
@ -188,13 +216,13 @@ const RegisterPage = () => {
|
|||||||
className="text-gray-700 flex justify-between items-center mb-2"
|
className="text-gray-700 flex justify-between items-center mb-2"
|
||||||
>
|
>
|
||||||
{hostname}
|
{hostname}
|
||||||
<button
|
<CustomButton
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleDeleteHostname(hostname)}
|
onClick={() => handleDeleteHostname(hostname)}
|
||||||
className="bg-red-500 text-white px-2 py-1 rounded ml-4"
|
className="bg-red-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
<FaTrash />
|
<FaTrash />
|
||||||
</button>
|
</CustomButton>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@ -209,14 +237,15 @@ const RegisterPage = () => {
|
|||||||
inputClassName="border p-2 rounded w-full"
|
inputClassName="border p-2 rounded w-full"
|
||||||
errorClassName="text-red-500 text-sm mt-1"
|
errorClassName="text-red-500 text-sm mt-1"
|
||||||
className="mr-2 flex-grow"
|
className="mr-2 flex-grow"
|
||||||
/>
|
>
|
||||||
<button
|
<CustomButton
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleAddHostname}
|
onClick={handleAddHostname}
|
||||||
className="bg-green-500 text-white p-2 rounded ml-2 h-10 flex items-center"
|
className="bg-green-500 text-white p-2 rounded ml-2"
|
||||||
>
|
>
|
||||||
<FaPlus />
|
<FaPlus />
|
||||||
</button>
|
</CustomButton>
|
||||||
|
</CustomInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CustomButton
|
<CustomButton
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// components/CustomInput.tsx
|
// components/CustomInput.tsx
|
||||||
'use client'
|
'use client'
|
||||||
import React from 'react'
|
import React, { FC } from 'react'
|
||||||
|
|
||||||
interface CustomInputProps {
|
interface CustomInputProps {
|
||||||
value: string
|
value: string
|
||||||
@ -12,9 +12,10 @@ interface CustomInputProps {
|
|||||||
inputClassName?: string
|
inputClassName?: string
|
||||||
errorClassName?: string
|
errorClassName?: string
|
||||||
className?: string
|
className?: string
|
||||||
|
children?: React.ReactNode // Added for additional elements
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomInput: React.FC<CustomInputProps> = ({
|
const CustomInput: FC<CustomInputProps> = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
placeholder = '',
|
placeholder = '',
|
||||||
@ -23,23 +24,29 @@ const CustomInput: React.FC<CustomInputProps> = ({
|
|||||||
title,
|
title,
|
||||||
inputClassName = '',
|
inputClassName = '',
|
||||||
errorClassName = '',
|
errorClassName = '',
|
||||||
className = ''
|
className = '',
|
||||||
|
children // Added for additional elements
|
||||||
}) => {
|
}) => {
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
onChange?.(e.target.value)
|
onChange?.(e.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={`flex flex-col ${className}`}>
|
||||||
{title && <label>{title}</label>}
|
{title && <label className="mb-1">{title}</label>}
|
||||||
|
<div className="flex items-center">
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
className={inputClassName}
|
className={`flex-grow ${inputClassName}`}
|
||||||
/>
|
/>
|
||||||
{error && <p className={errorClassName}>{error}</p>}
|
{children && <div className="ml-2">{children}</div>}
|
||||||
|
</div>
|
||||||
|
{error && (
|
||||||
|
<p className={`text-red-500 mt-1 ${errorClassName}`}>{error}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
|
|
||||||
// Helper functions for validation
|
// Helper functions for validation
|
||||||
const isValidEmail = (email: string) => {
|
const isValidEmail = (email: string) => {
|
||||||
@ -21,38 +21,68 @@ const isValidHostname = (hostname: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Props interface for useValidation hook
|
// Props interface for useValidation hook
|
||||||
interface UseValidationProps {
|
interface UseValidationProps<T> {
|
||||||
initialValue: string
|
initialValue: T
|
||||||
validateFn: (value: string) => boolean
|
validateFn: (value: T) => boolean
|
||||||
errorMessage: string
|
errorMessage: string
|
||||||
|
emptyFieldMessage?: string // Optional custom message for empty fields
|
||||||
|
defaultResetValue?: T // Optional default reset value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom hook for input validation
|
// Custom hook for input validation
|
||||||
const useValidation = ({
|
const useValidation = <T extends string | number | Date>(
|
||||||
|
props: UseValidationProps<T>
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
initialValue,
|
initialValue,
|
||||||
validateFn,
|
validateFn,
|
||||||
errorMessage
|
errorMessage,
|
||||||
}: UseValidationProps) => {
|
emptyFieldMessage = 'This field cannot be empty.', // Default message
|
||||||
const [value, setValue] = useState(initialValue)
|
defaultResetValue
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const [value, setValue] = useState<T>(initialValue)
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
|
|
||||||
const handleChange = (newValue: string) => {
|
const handleChange = useCallback(
|
||||||
console.log(newValue)
|
(newValue: T) => {
|
||||||
setValue(newValue)
|
setValue(newValue)
|
||||||
if (newValue.trim() === '') {
|
const stringValue =
|
||||||
setError('This field cannot be empty.')
|
newValue instanceof Date
|
||||||
} else if (!validateFn(newValue.trim())) {
|
? newValue.toISOString()
|
||||||
|
: newValue.toString().trim()
|
||||||
|
if (stringValue === '') {
|
||||||
|
setError(emptyFieldMessage)
|
||||||
|
} else if (!validateFn(newValue)) {
|
||||||
setError(errorMessage)
|
setError(errorMessage)
|
||||||
} else {
|
} else {
|
||||||
setError('')
|
setError('')
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
[emptyFieldMessage, errorMessage, validateFn]
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleChange(initialValue)
|
handleChange(initialValue)
|
||||||
}, [initialValue])
|
}, [initialValue, handleChange])
|
||||||
|
|
||||||
return { value, error, handleChange, reset: () => setValue('') }
|
const reset = useCallback(() => {
|
||||||
|
const resetValue =
|
||||||
|
defaultResetValue !== undefined ? defaultResetValue : initialValue
|
||||||
|
|
||||||
|
setValue(resetValue)
|
||||||
|
const stringValue =
|
||||||
|
resetValue instanceof Date
|
||||||
|
? resetValue.toISOString()
|
||||||
|
: resetValue.toString().trim()
|
||||||
|
if (stringValue === '') {
|
||||||
|
setError(emptyFieldMessage)
|
||||||
|
} else {
|
||||||
|
setError('')
|
||||||
|
}
|
||||||
|
}, [defaultResetValue, initialValue, emptyFieldMessage])
|
||||||
|
|
||||||
|
return { value, error, handleChange, reset }
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
5
src/ClientApp/models/PatchAction.ts
Normal file
5
src/ClientApp/models/PatchAction.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export interface PatchAction<T> {
|
||||||
|
op: PatchOperation // Enum for operation type
|
||||||
|
index?: number // Index for the operation (for arrays/lists)
|
||||||
|
value?: T // Value for the operation
|
||||||
|
}
|
||||||
5
src/ClientApp/models/PatchOperation.ts
Normal file
5
src/ClientApp/models/PatchOperation.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
enum PatchOperation {
|
||||||
|
Add,
|
||||||
|
Remove,
|
||||||
|
Replace
|
||||||
|
}
|
||||||
6
src/ClientApp/models/letsEncryptServer/cache/requests/PatchAccountRequest.ts
vendored
Normal file
6
src/ClientApp/models/letsEncryptServer/cache/requests/PatchAccountRequest.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { PatchAction } from '@/models/PatchAction'
|
||||||
|
|
||||||
|
export interface PatchAccountRequest {
|
||||||
|
description?: PatchAction<string>
|
||||||
|
contacts?: PatchAction<string>[]
|
||||||
|
}
|
||||||
5
src/ClientApp/models/letsEncryptServer/cache/requests/PatchContactsRequest.ts
vendored
Normal file
5
src/ClientApp/models/letsEncryptServer/cache/requests/PatchContactsRequest.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { PatchAction } from '@/models/PatchAction'
|
||||||
|
|
||||||
|
export interface PatchContactsRequest {
|
||||||
|
contacts: PatchAction<string>[]
|
||||||
|
}
|
||||||
3
src/ClientApp/models/letsEncryptServer/cache/requests/PostContactsRequest.ts
vendored
Normal file
3
src/ClientApp/models/letsEncryptServer/cache/requests/PostContactsRequest.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface PostContactsRequest {
|
||||||
|
contacts: string[]
|
||||||
|
}
|
||||||
4
src/ClientApp/models/letsEncryptServer/cache/requests/PutAccountRequest.ts
vendored
Normal file
4
src/ClientApp/models/letsEncryptServer/cache/requests/PutAccountRequest.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface PutAccountRequest {
|
||||||
|
description: string
|
||||||
|
contacts: string[]
|
||||||
|
}
|
||||||
3
src/ClientApp/models/letsEncryptServer/cache/requests/PutContactsRequest.ts
vendored
Normal file
3
src/ClientApp/models/letsEncryptServer/cache/requests/PutContactsRequest.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface PutContactsRequest {
|
||||||
|
contacts: string[]
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { isValidContact, isValidHostname } from '@/hooks/useValidation'
|
||||||
|
|
||||||
|
export interface PostAccountRequest {
|
||||||
|
description?: string
|
||||||
|
contacts: string[]
|
||||||
|
hostnames: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatePostAccountRequest = (
|
||||||
|
request: PostAccountRequest | null
|
||||||
|
): string | null => {
|
||||||
|
if (request === null) return 'Request is null'
|
||||||
|
|
||||||
|
// Validate contacts
|
||||||
|
for (const contact of request.contacts) {
|
||||||
|
if (!isValidContact(contact)) {
|
||||||
|
return `Invalid contact: ${contact}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate hostnames
|
||||||
|
for (const hostname of request.hostnames) {
|
||||||
|
if (!isValidHostname(hostname)) {
|
||||||
|
return `Invalid hostname: ${hostname}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all validations pass, return null
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export { validatePostAccountRequest }
|
||||||
@ -1,5 +1,11 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit'
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
import loaderReducer from '@/redux/slices//loaderSlice'
|
import {
|
||||||
|
TypedUseSelectorHook,
|
||||||
|
useDispatch,
|
||||||
|
useSelector,
|
||||||
|
useStore
|
||||||
|
} from 'react-redux'
|
||||||
|
import loaderReducer from '@/redux/slices/loaderSlice'
|
||||||
import toastReducer from '@/redux/slices/toastSlice'
|
import toastReducer from '@/redux/slices/toastSlice'
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
@ -11,3 +17,9 @@ export const store = configureStore({
|
|||||||
|
|
||||||
export type RootState = ReturnType<typeof store.getState>
|
export type RootState = ReturnType<typeof store.getState>
|
||||||
export type AppDispatch = typeof store.dispatch
|
export type AppDispatch = typeof store.dispatch
|
||||||
|
export type AppStore = typeof store
|
||||||
|
|
||||||
|
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||||
|
export const useAppDispatch = () => useDispatch<AppDispatch>()
|
||||||
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
||||||
|
export const useAppStore = () => useStore<AppStore>()
|
||||||
|
|||||||
@ -4,9 +4,8 @@ using DomainResults.Common;
|
|||||||
|
|
||||||
|
|
||||||
using MaksIT.LetsEncryptServer.Services;
|
using MaksIT.LetsEncryptServer.Services;
|
||||||
using Models.LetsEncryptServer.CertsFlow.Requests;
|
|
||||||
using Models.LetsEncryptServer.Cache.Responses;
|
|
||||||
using MaksIT.LetsEncrypt.Entities;
|
using MaksIT.LetsEncrypt.Entities;
|
||||||
|
using MaksIT.Models.LetsEncryptServer.CertsFlow.Requests;
|
||||||
|
|
||||||
namespace MaksIT.LetsEncryptServer.BackgroundServices {
|
namespace MaksIT.LetsEncryptServer.BackgroundServices {
|
||||||
public class AutoRenewal : BackgroundService {
|
public class AutoRenewal : BackgroundService {
|
||||||
@ -62,7 +61,7 @@ namespace MaksIT.LetsEncryptServer.BackgroundServices {
|
|||||||
return IDomainResult.Success();
|
return IDomainResult.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
var renewResult = await RenewCertificatesForHostnames(cache.AccountId, cache.Contacts, hostnames);
|
var renewResult = await RenewCertificatesForHostnames(cache.AccountId, cache.Description, cache.Contacts, hostnames);
|
||||||
if (!renewResult.IsSuccess)
|
if (!renewResult.IsSuccess)
|
||||||
return renewResult;
|
return renewResult;
|
||||||
|
|
||||||
@ -71,7 +70,7 @@ namespace MaksIT.LetsEncryptServer.BackgroundServices {
|
|||||||
return IDomainResult.Success();
|
return IDomainResult.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IDomainResult> RenewCertificatesForHostnames(Guid accountId, string[] contacts, string[] hostnames) {
|
private async Task<IDomainResult> RenewCertificatesForHostnames(Guid accountId, string description, string[] contacts, string[] hostnames) {
|
||||||
var (sessionId, configureClientResult) = await _certsFlowService.ConfigureClientAsync();
|
var (sessionId, configureClientResult) = await _certsFlowService.ConfigureClientAsync();
|
||||||
if (!configureClientResult.IsSuccess || sessionId == null) {
|
if (!configureClientResult.IsSuccess || sessionId == null) {
|
||||||
LogErrors(configureClientResult.Errors);
|
LogErrors(configureClientResult.Errors);
|
||||||
@ -81,6 +80,7 @@ namespace MaksIT.LetsEncryptServer.BackgroundServices {
|
|||||||
var sessionIdValue = sessionId.Value;
|
var sessionIdValue = sessionId.Value;
|
||||||
|
|
||||||
var (_, initResult) = await _certsFlowService.InitAsync(sessionIdValue, accountId, new InitRequest {
|
var (_, initResult) = await _certsFlowService.InitAsync(sessionIdValue, accountId, new InitRequest {
|
||||||
|
Description = description,
|
||||||
Contacts = contacts
|
Contacts = contacts
|
||||||
});
|
});
|
||||||
if (!initResult.IsSuccess) {
|
if (!initResult.IsSuccess) {
|
||||||
|
|||||||
@ -57,7 +57,7 @@ public class CacheController : ControllerBase {
|
|||||||
return result.ToActionResult();
|
return result.ToActionResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("account/{accountId:guid}/contacts/{index:int}")]
|
[HttpDelete("account/{accountId:guid}/contact/{index:int}")]
|
||||||
public async Task<IActionResult> DeleteContact(Guid accountId, int index) {
|
public async Task<IActionResult> DeleteContact(Guid accountId, int index) {
|
||||||
var result = await _cacheService.DeleteContactAsync(accountId, index);
|
var result = await _cacheService.DeleteContactAsync(accountId, index);
|
||||||
return result.ToActionResult();
|
return result.ToActionResult();
|
||||||
|
|||||||
@ -2,11 +2,11 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using DomainResults.Mvc;
|
using DomainResults.Mvc;
|
||||||
using MaksIT.LetsEncryptServer.Services;
|
using MaksIT.LetsEncryptServer.Services;
|
||||||
using Models.LetsEncryptServer.CertsFlow.Requests;
|
using MaksIT.Models.LetsEncryptServer.CertsFlow.Requests;
|
||||||
|
|
||||||
namespace MaksIT.LetsEncryptServer.Controllers {
|
namespace MaksIT.LetsEncryptServer.Controllers {
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/certs")]
|
||||||
public class CertsFlowController : ControllerBase {
|
public class CertsFlowController : ControllerBase {
|
||||||
private readonly Configuration _appSettings;
|
private readonly Configuration _appSettings;
|
||||||
private readonly ICertsFlowService _certsFlowService;
|
private readonly ICertsFlowService _certsFlowService;
|
||||||
@ -34,7 +34,7 @@ namespace MaksIT.LetsEncryptServer.Controllers {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sessionId">Session ID</param>
|
/// <param name="sessionId">Session ID</param>
|
||||||
/// <returns>Terms of service</returns>
|
/// <returns>Terms of service</returns>
|
||||||
[HttpGet("terms-of-service/{sessionId}")]
|
[HttpGet("{sessionId}/terms-of-service")]
|
||||||
public IActionResult TermsOfService(Guid sessionId) {
|
public IActionResult TermsOfService(Guid sessionId) {
|
||||||
var result = _certsFlowService.GetTermsOfService(sessionId);
|
var result = _certsFlowService.GetTermsOfService(sessionId);
|
||||||
return result.ToActionResult();
|
return result.ToActionResult();
|
||||||
|
|||||||
@ -13,7 +13,7 @@ namespace MaksIT.LetsEncryptServer.Controllers;
|
|||||||
public class WellKnownController : ControllerBase {
|
public class WellKnownController : ControllerBase {
|
||||||
|
|
||||||
private readonly Configuration _appSettings;
|
private readonly Configuration _appSettings;
|
||||||
private readonly ICertsFlowServiceBase _certsFlowService;
|
private readonly ICertsRestChallengeService _certsFlowService;
|
||||||
|
|
||||||
private readonly string _acmePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "acme");
|
private readonly string _acmePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "acme");
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ using MaksIT.Core.Extensions;
|
|||||||
using MaksIT.LetsEncrypt.Entities;
|
using MaksIT.LetsEncrypt.Entities;
|
||||||
using MaksIT.Models;
|
using MaksIT.Models;
|
||||||
using MaksIT.Models.LetsEncryptServer.Cache.Requests;
|
using MaksIT.Models.LetsEncryptServer.Cache.Requests;
|
||||||
using Models.LetsEncryptServer.Cache.Responses;
|
using MaksIT.Models.LetsEncryptServer.Cache.Responses;
|
||||||
|
|
||||||
namespace MaksIT.LetsEncryptServer.Services;
|
namespace MaksIT.LetsEncryptServer.Services;
|
||||||
|
|
||||||
|
|||||||
@ -6,16 +6,21 @@ using DomainResults.Common;
|
|||||||
|
|
||||||
using MaksIT.LetsEncrypt.Entities;
|
using MaksIT.LetsEncrypt.Entities;
|
||||||
using MaksIT.LetsEncrypt.Services;
|
using MaksIT.LetsEncrypt.Services;
|
||||||
using Models.LetsEncryptServer.CertsFlow.Requests;
|
using MaksIT.Models.LetsEncryptServer.CertsFlow.Requests;
|
||||||
|
|
||||||
|
|
||||||
namespace MaksIT.LetsEncryptServer.Services;
|
namespace MaksIT.LetsEncryptServer.Services;
|
||||||
|
|
||||||
public interface ICertsFlowServiceBase {
|
public interface ICertsInternalService {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface ICertsRestChallengeService {
|
||||||
(string?, IDomainResult) AcmeChallenge(string fileName);
|
(string?, IDomainResult) AcmeChallenge(string fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ICertsFlowService : ICertsFlowServiceBase {
|
public interface ICertsRestService {
|
||||||
Task<(Guid?, IDomainResult)> ConfigureClientAsync();
|
Task<(Guid?, IDomainResult)> ConfigureClientAsync();
|
||||||
(string?, IDomainResult) GetTermsOfService(Guid sessionId);
|
(string?, IDomainResult) GetTermsOfService(Guid sessionId);
|
||||||
Task<(Guid?, IDomainResult)> InitAsync(Guid sessionId, Guid? accountId, InitRequest requestData);
|
Task<(Guid?, IDomainResult)> InitAsync(Guid sessionId, Guid? accountId, InitRequest requestData);
|
||||||
@ -26,6 +31,11 @@ public interface ICertsFlowService : ICertsFlowServiceBase {
|
|||||||
Task<(Dictionary<string, string>?, IDomainResult)> ApplyCertificatesAsync(Guid sessionId, GetCertificatesRequest requestData);
|
Task<(Dictionary<string, string>?, IDomainResult)> ApplyCertificatesAsync(Guid sessionId, GetCertificatesRequest requestData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface ICertsFlowService
|
||||||
|
: ICertsInternalService,
|
||||||
|
ICertsRestService,
|
||||||
|
ICertsRestChallengeService {}
|
||||||
|
|
||||||
public class CertsFlowService : ICertsFlowService {
|
public class CertsFlowService : ICertsFlowService {
|
||||||
|
|
||||||
private readonly Configuration _appSettings;
|
private readonly Configuration _appSettings;
|
||||||
|
|||||||
@ -1,12 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MaksIT.Models.LetsEncryptServer.Cache.Requests {
|
namespace MaksIT.Models.LetsEncryptServer.Cache.Requests {
|
||||||
public class PutAccountRequest {
|
public class PutAccountRequest : IValidatableObject {
|
||||||
public string Description { get; set; }
|
public required string Description { get; set; }
|
||||||
public string[] Contacts { get; set; }
|
public required string[] Contacts { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||||
|
if (string.IsNullOrWhiteSpace(Description))
|
||||||
|
yield return new ValidationResult("Description is required", new[] { nameof(Description) });
|
||||||
|
|
||||||
|
if (Contacts == null || Contacts.Length == 0)
|
||||||
|
yield return new ValidationResult("Contacts is required", new[] { nameof(Contacts) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Models.LetsEncryptServer.Cache.Responses {
|
namespace MaksIT.Models.LetsEncryptServer.Cache.Responses {
|
||||||
public class GetAccountResponse {
|
public class GetAccountResponse {
|
||||||
public Guid AccountId { get; set; }
|
public Guid AccountId { get; set; }
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Models.LetsEncryptServer.Cache.Responses {
|
namespace MaksIT.Models.LetsEncryptServer.Cache.Responses {
|
||||||
public class GetContactsResponse {
|
public class GetContactsResponse {
|
||||||
public string[] Contacts { get; set; }
|
public string[] Contacts { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Models.LetsEncryptServer.Cache.Responses {
|
namespace MaksIT.Models.LetsEncryptServer.Cache.Responses {
|
||||||
|
|
||||||
public class GetHostnamesResponse {
|
public class GetHostnamesResponse {
|
||||||
public List<HostnameResponse> Hostnames { get; set; }
|
public List<HostnameResponse> Hostnames { get; set; }
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Models.LetsEncryptServer.Cache.Responses {
|
namespace MaksIT.Models.LetsEncryptServer.Cache.Responses {
|
||||||
public class HostnameResponse {
|
public class HostnameResponse {
|
||||||
public string Hostname { get; set; }
|
public string Hostname { get; set; }
|
||||||
public DateTime Expires { get; set; }
|
public DateTime Expires { get; set; }
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
namespace Models.LetsEncryptServer.CertsFlow.Requests
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace MaksIT.Models.LetsEncryptServer.CertsFlow.Requests
|
||||||
{
|
{
|
||||||
public class GetCertificatesRequest
|
public class GetCertificatesRequest : IValidatableObject {
|
||||||
{
|
public required string[] Hostnames { get; set; }
|
||||||
public string[] Hostnames { get; set; }
|
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||||
|
if (Hostnames == null || Hostnames.Length == 0)
|
||||||
|
yield return new ValidationResult("Hostnames is required", new[] { nameof(Hostnames) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
namespace Models.LetsEncryptServer.CertsFlow.Requests
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace MaksIT.Models.LetsEncryptServer.CertsFlow.Requests
|
||||||
{
|
{
|
||||||
public class GetOrderRequest
|
public class GetOrderRequest : IValidatableObject {
|
||||||
{
|
public required string[] Hostnames { get; set; }
|
||||||
public string[] Hostnames { get; set; }
|
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||||
|
if (Hostnames == null || Hostnames.Length == 0)
|
||||||
|
yield return new ValidationResult("Hostnames is required", new[] { nameof(Hostnames) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Models.LetsEncryptServer.CertsFlow.Requests
|
namespace MaksIT.Models.LetsEncryptServer.CertsFlow.Requests {
|
||||||
{
|
public class InitRequest: IValidatableObject {
|
||||||
public class InitRequest
|
public required string Description { get; set; }
|
||||||
{
|
public required string[] Contacts { get; set; }
|
||||||
public string[] Contacts { get; set; }
|
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||||
|
if (string.IsNullOrWhiteSpace(Description))
|
||||||
|
yield return new ValidationResult("Description is required", new[] { nameof(Description) });
|
||||||
|
|
||||||
|
if (Contacts == null || Contacts.Length == 0)
|
||||||
|
yield return new ValidationResult("Contacts is required", new[] { nameof(Contacts) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,18 @@
|
|||||||
namespace Models.LetsEncryptServer.CertsFlow.Requests
|
using System.ComponentModel.DataAnnotations;
|
||||||
{
|
|
||||||
public class NewOrderRequest
|
|
||||||
{
|
|
||||||
public string[] Hostnames { get; set; }
|
|
||||||
|
|
||||||
public string ChallengeType { get; set; }
|
namespace MaksIT.Models.LetsEncryptServer.CertsFlow.Requests
|
||||||
|
{
|
||||||
|
public class NewOrderRequest : IValidatableObject {
|
||||||
|
public required string[] Hostnames { get; set; }
|
||||||
|
|
||||||
|
public required string ChallengeType { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||||
|
if (Hostnames == null || Hostnames.Length == 0)
|
||||||
|
yield return new ValidationResult("Hostnames is required", new[] { nameof(Hostnames) });
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(ChallengeType) && ChallengeType != "http-01")
|
||||||
|
yield return new ValidationResult("ChallengeType is required", new[] { nameof(ChallengeType) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MaksIT.Models.LetsEncryptServer.CertsFlow.Requests {
|
||||||
|
public class PostAccountRequest : IValidatableObject {
|
||||||
|
public required string Description { get; set; }
|
||||||
|
public required string[] Contacts { get; set; }
|
||||||
|
public required string [] Hostnames { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
|
||||||
|
if (Description == null || Description.Length == 0)
|
||||||
|
yield return new ValidationResult("Description is required", new[] { nameof(Description) });
|
||||||
|
|
||||||
|
if (Contacts == null || Contacts.Length == 0)
|
||||||
|
yield return new ValidationResult("Contacts is required", new[] { nameof(Contacts) });
|
||||||
|
|
||||||
|
if (Hostnames == null || Hostnames.Length == 0)
|
||||||
|
yield return new ValidationResult("Hostnames is required", new[] { nameof(Hostnames) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user