diff --git a/client/src/pages/config/users/containerPicker.jsx b/client/src/pages/config/users/containerPicker.jsx
index c76c721..6652acb 100644
--- a/client/src/pages/config/users/containerPicker.jsx
+++ b/client/src/pages/config/users/containerPicker.jsx
@@ -36,9 +36,9 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer}) {
const [containers, setContainers] = React.useState([]);
const [hasPublicPorts, setHasPublicPorts] = React.useState(false);
const [isOnBridge, setIsOnBridge] = React.useState(false);
- const [options, setOptions] = React.useState([]);
+ const [options, setOptions] = React.useState(null);
const [portsOptions, setPortsOptions] = React.useState([]);
- const loading = open && options.length === 0;
+ const loading = options === null;
const name = "Target"
const label = "Container Name"
@@ -50,27 +50,23 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer}) {
let preview = formik.values[name];
- if(preview && preview.includes("://") && preview.includes(":")) {
- let p1_ = preview.split("://")[1]
+ if(preview) {
+ let protocols = preview.split("://")
+ let p1_ = protocols.length > 1 ? protocols[1] : protocols[0]
+ console.log("p1_", p1_)
targetResult = {
container: '/' + p1_.split(":")[0],
port: p1_.split(":")[1],
protocol: preview.split("://")[0],
- containerObject: containers.find((container) => container.Names[0] === '/' + p1_.split(":")[0]),
+ containerObject: !loading && containers.find((container) => container.Names[0] === '/' + p1_.split(":")[0]),
}
}
function getTarget() {
- return targetResult.protocol + "://" + targetResult.container.replace("/", "") + ":" + targetResult.port
+ return targetResult.protocol + (targetResult.protocol != '' ? "://" : '') + targetResult.container.replace("/", "") + ":" + targetResult.port
}
- const onContainerChange = (newContainer) => {
- targetResult.container = newContainer.Names[0]
- targetResult.containerObject = newContainer
- targetResult.port = ''
- targetResult.protocol = 'http'
- formik.setFieldValue(name, getTarget())
-
+ const postContainerChange = (newContainer) => {
let portsTemp = []
newContainer.Ports.forEach((port) => {
portsTemp.push(port.PrivatePort)
@@ -85,6 +81,17 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer}) {
}
}
+ const onContainerChange = (newContainer) => {
+ if(loading) return;
+ targetResult.container = newContainer.Names[0]
+ targetResult.containerObject = newContainer
+ targetResult.port = ''
+ targetResult.protocol = 'http'
+ formik.setFieldValue(name, getTarget())
+
+ postContainerChange(newContainer)
+ }
+
React.useEffect(() => {
if(lockTarget) {
onContainerChange(TargetContainer)
@@ -93,7 +100,7 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer}) {
React.useEffect(() => {
let active = true;
-
+
if (!loading) {
return undefined;
}
@@ -101,13 +108,15 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer}) {
(async () => {
const res = await API.docker.list()
setContainers(res.data);
-
let names = res.data.map((container) => container.Names[0])
if (active) {
setOptions([...names]);
}
+ if (targetResult.container !== 'null') {
+ postContainerChange(res.data.find((container) => container.Names[0] === targetResult.container))
+ }
})();
return () => {
@@ -124,7 +133,7 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer}) {
return (
{label}
- (
)}
- />
-
+ />}
+
{(portsOptions.length > 0) ? (<>
Container Port
{
@@ -184,24 +193,24 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer}) {
{option}
))}
- >) : ''}
+
+ {targetResult.port == '' &&
+ Please select a port
+ }
+ >) : ''}
{(portsOptions.length > 0) ? (<>
-
-
- Container Protocol (use HTTP if unsure)
+ }
+ defaultValue={targetResult.protocol}
onChange={(event) => {
- targetResult.protocol = event.target.checked ? "https" : "http"
+ targetResult.protocol = event.target.value && event.target.value.toLowerCase()
formik.setFieldValue(name, getTarget())
}}
- label={"Container uses HTTPS internally (leave unchecked if not sure, they usually don't)"}
/>
-
-
>) : ''}
Result Target Preview
@@ -212,6 +221,12 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer}) {
value={formik.values[name]}
disabled={true}
/>
+
+ {formik.errors[name] && (
+
+ {formik.errors[name]}
+
+ )}
);
diff --git a/client/src/pages/config/users/formShortcuts.jsx b/client/src/pages/config/users/formShortcuts.jsx
index 1a95965..6a1ed9a 100644
--- a/client/src/pages/config/users/formShortcuts.jsx
+++ b/client/src/pages/config/users/formShortcuts.jsx
@@ -38,12 +38,7 @@ export const CosmosInputText = ({ name, style, multiline, type, placeholder, onC
name={name}
multiline={multiline}
onBlur={formik.handleBlur}
- onChange={(...e) => {
- if (onChange) {
- onChange(...e);
- }
- formik.handleChange(...e);
- }}
+ onChange={formik.handleChange}
placeholder={placeholder}
fullWidth
error={Boolean(formik.touched[name] && formik.errors[name])}
@@ -172,6 +167,11 @@ export const CosmosCheckbox = ({ name, label, formik, style }) => {
style={style}
/>
+ {formik.touched[name] && formik.errors[name] && (
+
+ {formik.errors[name]}
+
+ )}
}
diff --git a/client/src/pages/config/users/proxyman.jsx b/client/src/pages/config/users/proxyman.jsx
index fe4ccde..e857b74 100644
--- a/client/src/pages/config/users/proxyman.jsx
+++ b/client/src/pages/config/users/proxyman.jsx
@@ -28,7 +28,8 @@ import {
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
import AnimateButton from '../../../components/@extended/AnimateButton';
import RestartModal from './restart';
-import RouteManagement from './routeman';
+import RouteManagement, {ValidateRoute} from './routeman';
+import { map } from 'lodash';
const ProxyManagement = () => {
@@ -36,6 +37,7 @@ const ProxyManagement = () => {
const [config, setConfig] = React.useState(null);
const [openModal, setOpenModal] = React.useState(false);
const [error, setError] = React.useState(null);
+ const [submitErrors, setSubmitErrors] = React.useState([]);
function updateRoutes(routes) {
let con = {
@@ -52,6 +54,14 @@ const ProxyManagement = () => {
return con;
}
+ function cleanRoutes(config) {
+ config.HTTPConfig.ProxyConfig.Routes = config.HTTPConfig.ProxyConfig.Routes.map((r) => {
+ delete r._hasErrors;
+ return r;
+ });
+ return config;
+ }
+
function refresh() {
API.config.get().then((res) => {
setConfig(res.data);
@@ -85,6 +95,13 @@ const ProxyManagement = () => {
refresh();
}, []);
+ const testRoute = (route) => {
+ try {
+ ValidateRoute.validateSync(route);
+ } catch (e) {
+ return e.errors;
+ }
+ }
let routes = config && (config.HTTPConfig.ProxyConfig.Routes || []);
return
@@ -92,7 +109,7 @@ const ProxyManagement = () => {
refresh();
}}>Refresh
} onClick={() => {
- routes.push({
+ routes.unshift({
Name: 'New Route',
Description: 'New Route',
Mode: "SERVAPP",
@@ -114,7 +131,7 @@ const ProxyManagement = () => {
{config && <>
{routes && routes.map((route,key) => (<>
-
{
routes[key] = newRoute;
}}
@@ -133,13 +150,30 @@ const ProxyManagement = () => {
)}
+
+ {submitErrors.map((err) => {
+ return {err}
+ })}
+
}
diff --git a/client/src/pages/config/users/routeman.jsx b/client/src/pages/config/users/routeman.jsx
index 4f0e656..6d8fc3a 100644
--- a/client/src/pages/config/users/routeman.jsx
+++ b/client/src/pages/config/users/routeman.jsx
@@ -33,9 +33,35 @@ import { CosmosCheckbox, CosmosCollapse, CosmosFormDivider, CosmosInputText, Cos
import { DownOutlined, UpOutlined, CheckOutlined, DeleteOutlined } from '@ant-design/icons';
import { CosmosContainerPicker } from './containerPicker';
+export const ValidateRoute = Yup.object().shape({
+ Name: Yup.string().required('Name is required'),
+ Mode: Yup.string().required('Mode is required'),
+ Target: Yup.string().required('Target is required').when('Mode', {
+ is: 'SERVAPP',
+ then: Yup.string().matches(/:[0-9]+$/, 'Invalid Target, must have a port'),
+ }),
+
+ Host: Yup.string().when('UseHost', {
+ is: true,
+ then: Yup.string().required('Host is required')
+ .matches(/[\.|\:]/, 'Host must be full domain ([sub.]domain.com) or an IP')
+ }),
+
+ PathPrefix: Yup.string().when('UsePathPrefix', {
+ is: true,
+ then: Yup.string().required('Path Prefix is required').matches(/^\//, 'Path Prefix must start with / (e.g. /api). Do not include a domain/subdomain in it, use the Host for this.')
+ }),
+
+ UseHost: Yup.boolean().when('UsePathPrefix',
+ {
+ is: false,
+ then: Yup.boolean().oneOf([true], 'Source must at least be either Host or Path Prefix')
+ }),
+})
+
const RouteManagement = ({ routeConfig, TargetContainer, noControls=false, lockTarget=false, setRouteConfig, up, down, deleteRoute }) => {
const [confirmDelete, setConfirmDelete] = React.useState(false);
-
+
return
{routeConfig && <>
{
- setRouteConfig(values);
- }}
+ validateOnChange={false}
+ validationSchema={ValidateRoute}
onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => {
return false;
}}
+ validate={(values) => {
+ //setRouteConfig(values);
+ }}
>
{(formik) => (
}>
+ {formik.errors.submit && (
+
+ {formik.errors.submit}
+
+ )}
diff --git a/package.json b/package.json
index 7de3b9c..6e5fe8a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cosmos-server",
- "version": "0.1.8",
+ "version": "0.1.9",
"description": "",
"main": "test-server.js",
"bugs": {
diff --git a/readme.md b/readme.md
index f1be7e5..52dc28c 100644
--- a/readme.md
+++ b/readme.md
@@ -70,7 +70,7 @@ Authentication is very hard (how do you check the password match? What encryptio
Installation is simple using Docker:
```
-docker run -d -p 80:80 -p 443:443 --name cosmos-server --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/cosmos/config:/config azukaar/cosmos-server:latest
+docker run -d -p 80:80 -p 443:443 --name cosmos-server -h cosmos-server --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/cosmos/config:/config azukaar/cosmos-server:latest
```
Once installed, simply go to `http://your-ip` and follow the instructions of the setup wizard.
diff --git a/src/proxy/routerGen.go b/src/proxy/routerGen.go
index 26d4870..ad469fa 100644
--- a/src/proxy/routerGen.go
+++ b/src/proxy/routerGen.go
@@ -63,21 +63,6 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt
}
destination = http.StripPrefix(route.PathPrefix, destination)
}
- timeout := route.Timeout
-
- if(timeout == 0) {
- timeout = 10000
- }
-
- throttlePerMinute := route.ThrottlePerMinute
-
- throtthleTime := 1*time.Minute
-
- // lets do something better later to disable throttle
- if(throttlePerMinute == 0) {
- throttlePerMinute = 99999999
- throtthleTime = 1*time.Second
- }
originCORS := route.CORSOrigin
@@ -92,12 +77,18 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt
if(route.UsePathPrefix && !route.StripPathPrefix && (route.Mode == "STATIC" || route.Mode == "SPA")) {
utils.Warn("PathPrefix is used, but StripPathPrefix is false. The route mode is " + (string)(route.Mode) + ". This will likely cause issues with the route. Ignore this warning if you know what you are doing.")
}
+
+ timeout := route.Timeout
+
+ if(timeout > 0) {
+ destination = utils.MiddlewareTimeout(timeout * time.Millisecond)(destination)
+ }
- origin.Handler(
- tokenMiddleware(route.AuthEnabled)(
- utils.CORSHeader(originCORS)(
- utils.MiddlewareTimeout(timeout * time.Millisecond)(
- httprate.Limit(throttlePerMinute, throtthleTime,
+ throttlePerMinute := route.ThrottlePerMinute
+
+ if(throttlePerMinute > 0) {
+ throtthleTime := time.Minute
+ destination = httprate.Limit(throttlePerMinute, throtthleTime,
httprate.WithKeyFuncs(httprate.KeyByIP),
httprate.WithLimitHandler(func(w http.ResponseWriter, r *http.Request) {
utils.Error("Too many requests. Throttling", nil)
@@ -105,7 +96,10 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt
http.StatusTooManyRequests, "HTTP003")
return
}),
- )(destination)))))
+ )(destination)
+ }
+
+ origin.Handler(tokenMiddleware(route.AuthEnabled)(utils.CORSHeader(originCORS)((destination))))
utils.Log("Added route: ["+ (string)(route.Mode) + "] " + route.Host + route.PathPrefix + " to " + route.Target + "")
diff --git a/src/utils/middleware.go b/src/utils/middleware.go
index 551f7b0..ac3e614 100644
--- a/src/utils/middleware.go
+++ b/src/utils/middleware.go
@@ -22,6 +22,8 @@ func MiddlewareTimeout(timeout time.Duration) func(next http.Handler) http.Handl
}
}()
+ w.Header().Set("X-Timeout-Duration", timeout.String())
+
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
}