v0.1.15 improve Port selection, hostnames and ports now have default, other UI improvements
This commit is contained in:
parent
d8870191c6
commit
a82a436187
|
@ -16,24 +16,24 @@ const pages = {
|
||||||
type: 'group',
|
type: 'group',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 'proxy',
|
id: 'url',
|
||||||
title: 'Proxy Routes',
|
title: 'URLs',
|
||||||
type: 'item',
|
type: 'item',
|
||||||
url: '/ui/config/proxy',
|
url: '/ui/config-url',
|
||||||
icon: icons.NodeExpandOutlined,
|
icon: icons.NodeExpandOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'users',
|
id: 'users',
|
||||||
title: 'Manage Users',
|
title: 'Manage Users',
|
||||||
type: 'item',
|
type: 'item',
|
||||||
url: '/ui/config/users',
|
url: '/ui/config-users',
|
||||||
icon: icons.ProfileOutlined,
|
icon: icons.ProfileOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'config',
|
id: 'config',
|
||||||
title: 'Configuration',
|
title: 'Configuration',
|
||||||
type: 'item',
|
type: 'item',
|
||||||
url: '/ui/config/general',
|
url: '/ui/config-general',
|
||||||
icon: icons.SettingOutlined,
|
icon: icons.SettingOutlined,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -29,6 +29,8 @@ import RestartModal from './restart';
|
||||||
import Autocomplete from '@mui/material/Autocomplete';
|
import Autocomplete from '@mui/material/Autocomplete';
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
|
|
||||||
|
import defaultport from '../../servapps/defaultport.json';
|
||||||
|
|
||||||
import * as API from '../../../api';
|
import * as API from '../../../api';
|
||||||
|
|
||||||
export function CosmosContainerPicker({formik, lockTarget, TargetContainer, onTargetChange}) {
|
export function CosmosContainerPicker({formik, lockTarget, TargetContainer, onTargetChange}) {
|
||||||
|
@ -53,7 +55,7 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer, onTa
|
||||||
if(preview) {
|
if(preview) {
|
||||||
let protocols = preview.split("://")
|
let protocols = preview.split("://")
|
||||||
let p1_ = protocols.length > 1 ? protocols[1] : protocols[0]
|
let p1_ = protocols.length > 1 ? protocols[1] : protocols[0]
|
||||||
console.log("p1_", p1_)
|
|
||||||
targetResult = {
|
targetResult = {
|
||||||
container: '/' + p1_.split(":")[0],
|
container: '/' + p1_.split(":")[0],
|
||||||
port: p1_.split(":")[1],
|
port: p1_.split(":")[1],
|
||||||
|
@ -76,6 +78,37 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer, onTa
|
||||||
})
|
})
|
||||||
setPortsOptions(portsTemp)
|
setPortsOptions(portsTemp)
|
||||||
|
|
||||||
|
targetResult.port = '80'
|
||||||
|
|
||||||
|
if(portsTemp.length > 0) {
|
||||||
|
// pick best default port
|
||||||
|
// set default to first port
|
||||||
|
targetResult.port = portsTemp[0]
|
||||||
|
// first, check if a manual override exists
|
||||||
|
let override = Object.keys(defaultport.overrides).find((key) => {
|
||||||
|
let keyMatch = new RegExp(key, "i");
|
||||||
|
return newContainer.Image.match(keyMatch) && portsTemp.includes(defaultport.overrides[key])
|
||||||
|
});
|
||||||
|
|
||||||
|
if(override) {
|
||||||
|
targetResult.port = defaultport.overrides[override]
|
||||||
|
} else {
|
||||||
|
// if not, check the default list of common ports
|
||||||
|
let priorityList = defaultport.priority;
|
||||||
|
priorityList.find((_portReg) => {
|
||||||
|
return portsTemp.find((portb) => {
|
||||||
|
let portReg = new RegExp(_portReg, "i");
|
||||||
|
if(portb.toString().match(portReg)) {
|
||||||
|
targetResult.port = portb
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formik.setFieldValue(name, getTarget());
|
||||||
|
|
||||||
if(newContainer.NetworkSettings.Networks["bridge"]) {
|
if(newContainer.NetworkSettings.Networks["bridge"]) {
|
||||||
setIsOnBridge(true);
|
setIsOnBridge(true);
|
||||||
}
|
}
|
||||||
|
@ -186,26 +219,27 @@ export function CosmosContainerPicker({formik, lockTarget, TargetContainer, onTa
|
||||||
|
|
||||||
{(portsOptions.length > 0) ? (<>
|
{(portsOptions.length > 0) ? (<>
|
||||||
<InputLabel htmlFor={name + "-port"}>Container Port</InputLabel>
|
<InputLabel htmlFor={name + "-port"}>Container Port</InputLabel>
|
||||||
<TextField
|
<Autocomplete
|
||||||
className="px-2 my-2"
|
className="px-2 my-2"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
name={name + "-port"}
|
name={name + "-port"}
|
||||||
id={name + "-port"}
|
id={name + "-port"}
|
||||||
value={targetResult.port}
|
value={targetResult.port}
|
||||||
select
|
options={portsOptions.map((option) => (option))}
|
||||||
placeholder='Select a port'
|
placeholder='Select a port'
|
||||||
onChange={(event) => {
|
freeSolo
|
||||||
targetResult.port = event.target.value
|
filterOptions={(x) => x} // disable filtering
|
||||||
|
getOptionLabel={(option) => '' + option}
|
||||||
|
isOptionEqualToValue={(option, value) => {
|
||||||
|
return ('' + option) === value
|
||||||
|
}}
|
||||||
|
onChange={(event, newValue) => {
|
||||||
|
targetResult.port = newValue
|
||||||
formik.setFieldValue(name, getTarget())
|
formik.setFieldValue(name, getTarget())
|
||||||
}}
|
}}
|
||||||
>
|
renderInput={(params) => <TextField {...params} />}
|
||||||
{portsOptions.map((option) => (
|
/>
|
||||||
<MenuItem key={option} value={option}>
|
{targetResult.port == '' && targetResult.port == 0 && <FormHelperText error id="standard-weight-helper-text-name-login">
|
||||||
{option}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</TextField>
|
|
||||||
{targetResult.port == '' && <FormHelperText error id="standard-weight-helper-text-name-login">
|
|
||||||
Please select a port
|
Please select a port
|
||||||
</FormHelperText>}
|
</FormHelperText>}
|
||||||
</>) : ''}
|
</>) : ''}
|
||||||
|
|
|
@ -122,8 +122,8 @@ const ProxyManagement = () => {
|
||||||
}}>Refresh</Button>
|
}}>Refresh</Button>
|
||||||
<Button variant="contained" color="primary" startIcon={<PlusCircleOutlined />} onClick={() => {
|
<Button variant="contained" color="primary" startIcon={<PlusCircleOutlined />} onClick={() => {
|
||||||
routes.unshift({
|
routes.unshift({
|
||||||
Name: 'New Route',
|
Name: 'New URL',
|
||||||
Description: 'New Route',
|
Description: 'New URL',
|
||||||
Mode: "SERVAPP",
|
Mode: "SERVAPP",
|
||||||
UseHost: false,
|
UseHost: false,
|
||||||
Host: '',
|
Host: '',
|
||||||
|
|
|
@ -100,7 +100,7 @@ const RouteManagement = ({ routeConfig, TargetContainer, noControls=false, lockT
|
||||||
{(formik) => (
|
{(formik) => (
|
||||||
<form ref={myRef} noValidate onSubmit={formik.handleSubmit}>
|
<form ref={myRef} noValidate onSubmit={formik.handleSubmit}>
|
||||||
<MainCard name={routeConfig.Name} title={
|
<MainCard name={routeConfig.Name} title={
|
||||||
noControls ? 'New Route' :
|
noControls ? 'New URL' :
|
||||||
<div>{routeConfig.Name}
|
<div>{routeConfig.Name}
|
||||||
<Chip label={<UpOutlined />} onClick={() => up()}/>
|
<Chip label={<UpOutlined />} onClick={() => up()}/>
|
||||||
<Chip label={<DownOutlined />} onClick={() => down()}/>
|
<Chip label={<DownOutlined />} onClick={() => down()}/>
|
||||||
|
|
59
client/src/pages/servapps/defaultport.json
Normal file
59
client/src/pages/servapps/defaultport.json
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
{
|
||||||
|
"priority": [
|
||||||
|
"80",
|
||||||
|
"3000",
|
||||||
|
"8080",
|
||||||
|
"8*",
|
||||||
|
"3*"
|
||||||
|
],
|
||||||
|
"overrides": {
|
||||||
|
"adguard": 80,
|
||||||
|
"airsonic": 4040,
|
||||||
|
"bazarr": 6767,
|
||||||
|
"bitwarden": 80,
|
||||||
|
"bookstack": 80,
|
||||||
|
"calibre(-web)?" : 8083,
|
||||||
|
"dokuwiki": 80,
|
||||||
|
"duplicati": 8200,
|
||||||
|
"emby": 8096,
|
||||||
|
"filebrowser": 80,
|
||||||
|
"ghost": 2368,
|
||||||
|
"git(lab|server)" : 80,
|
||||||
|
"gitea": 3000,
|
||||||
|
"grafana": 3000,
|
||||||
|
"heimdall": 80,
|
||||||
|
"homeassistant": 8123,
|
||||||
|
"homebridge": 8581,
|
||||||
|
"jackett": 9117,
|
||||||
|
"jdownloader": 5800,
|
||||||
|
"jellyfin": 8096,
|
||||||
|
"jitsi": 80,
|
||||||
|
"lidarr": 8686,
|
||||||
|
"letsencrypt": 80,
|
||||||
|
"ombi4k": 3579,
|
||||||
|
"moodle": 80,
|
||||||
|
"nzb360": 6789,
|
||||||
|
"nzbget": 6789,
|
||||||
|
"nzbhydra": 5076,
|
||||||
|
"ombi": 3579,
|
||||||
|
"openproject": 80,
|
||||||
|
"organizr": 80,
|
||||||
|
"pi-hole": 80,
|
||||||
|
"plex": 32400,
|
||||||
|
"portainer": 9000,
|
||||||
|
"prometheus": 9090,
|
||||||
|
"qbittorrent(vpn)?" : 8080,
|
||||||
|
"radarr": 7878,
|
||||||
|
"rocket.chat": 3000,
|
||||||
|
"sabnzbd": 8080,
|
||||||
|
"sick(chill|rage)": 8081,
|
||||||
|
"sonarr": 8989,
|
||||||
|
"synclounge": 8088,
|
||||||
|
"tautulli": 8181,
|
||||||
|
"transmission(-openvpn)?" : 9091,
|
||||||
|
"ubooquity": 2202,
|
||||||
|
"unifi-controller": 8443,
|
||||||
|
"watchtower": 8080,
|
||||||
|
"wordpress": 80
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// material-ui
|
// material-ui
|
||||||
import { AppstoreAddOutlined, ReloadOutlined, SearchOutlined, SettingOutlined } from '@ant-design/icons';
|
import { AppstoreAddOutlined, PlusCircleOutlined, ReloadOutlined, SearchOutlined, SettingOutlined } from '@ant-design/icons';
|
||||||
import { Alert, Badge, Button, Card, Checkbox, Chip, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, Input, InputAdornment, TextField, Tooltip, Typography } from '@mui/material';
|
import { Alert, Badge, Button, Card, Checkbox, Chip, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, Input, InputAdornment, TextField, Tooltip, Typography } from '@mui/material';
|
||||||
import Grid2 from '@mui/material/Unstable_Grid2/Grid2';
|
import Grid2 from '@mui/material/Unstable_Grid2/Grid2';
|
||||||
import { Stack } from '@mui/system';
|
import { Stack } from '@mui/system';
|
||||||
|
@ -116,13 +116,13 @@ const ServeApps = () => {
|
||||||
return <div>
|
return <div>
|
||||||
<RestartModal openModal={openRestartModal} setOpenModal={setOpenRestartModal} />
|
<RestartModal openModal={openRestartModal} setOpenModal={setOpenRestartModal} />
|
||||||
<Dialog open={openModal} onClose={() => setOpenModal(false)}>
|
<Dialog open={openModal} onClose={() => setOpenModal(false)}>
|
||||||
<DialogTitle>Connect ServApp</DialogTitle>
|
<DialogTitle>Expose ServApp</DialogTitle>
|
||||||
{openModal && <>
|
{openModal && <>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText>
|
<DialogContentText>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<div>
|
<div>
|
||||||
Welcome to the Connect Wizard. This interface will help you expose your ServApp securely to the internet.
|
Welcome to the URL Wizard. This interface will help you expose your ServApp securely to the internet by creating a new URL.
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{openModal && !hasCosmosNetwork(openModal.Names[0]) && <Alert severity="warning">This ServApp does not appear to be connected to a Cosmos Network, so the hostname might not be accessible. The easiest way to fix this is to check the box "Force Secure Network" or manually create a sub-network in Docker.</Alert>}
|
{openModal && !hasCosmosNetwork(openModal.Names[0]) && <Alert severity="warning">This ServApp does not appear to be connected to a Cosmos Network, so the hostname might not be accessible. The easiest way to fix this is to check the box "Force Secure Network" or manually create a sub-network in Docker.</Alert>}
|
||||||
|
@ -134,8 +134,8 @@ const ServeApps = () => {
|
||||||
Mode: "SERVAPP",
|
Mode: "SERVAPP",
|
||||||
Name: openModal.Names[0].replace('/', ''),
|
Name: openModal.Names[0].replace('/', ''),
|
||||||
Description: "Expose " + openModal.Names[0].replace('/', '') + " to the internet",
|
Description: "Expose " + openModal.Names[0].replace('/', '') + " to the internet",
|
||||||
UseHost: false,
|
UseHost: true,
|
||||||
Host: '',
|
Host: openModal.Names[0].replace('/', '') + '.' + window.location.origin.split('://')[1],
|
||||||
UsePathPrefix: false,
|
UsePathPrefix: false,
|
||||||
PathPrefix: '',
|
PathPrefix: '',
|
||||||
Timeout: 30000,
|
Timeout: 30000,
|
||||||
|
@ -177,7 +177,7 @@ const ServeApps = () => {
|
||||||
updateRoutes();
|
updateRoutes();
|
||||||
}
|
}
|
||||||
|
|
||||||
}}>Connect</Button>
|
}}>Confirm</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</>}
|
</>}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
@ -278,13 +278,13 @@ const ServeApps = () => {
|
||||||
</Stack></Stack>}
|
</Stack></Stack>}
|
||||||
<Stack margin={1} direction="column" spacing={1} alignItems="flex-start">
|
<Stack margin={1} direction="column" spacing={1} alignItems="flex-start">
|
||||||
<Typography variant="h6" color="text.secondary">
|
<Typography variant="h6" color="text.secondary">
|
||||||
Proxies
|
URLs
|
||||||
</Typography>
|
</Typography>
|
||||||
<Stack spacing={2} direction="row">
|
<Stack spacing={2} direction="row">
|
||||||
{getContainersRoutes(app.Names[0].replace('/', '')).map((route) => {
|
{getContainersRoutes(app.Names[0].replace('/', '')).map((route) => {
|
||||||
return <><Chip
|
return <><Chip
|
||||||
label={route.Host + route.PathPrefix}
|
label={route.Host + route.PathPrefix}
|
||||||
color="primary"
|
color="secondary"
|
||||||
style={{paddingRight: '4px'}}
|
style={{paddingRight: '4px'}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if(route.UseHost)
|
if(route.UseHost)
|
||||||
|
@ -293,21 +293,33 @@ const ServeApps = () => {
|
||||||
window.open(window.location.origin + route.PathPrefix, '_blank');
|
window.open(window.location.origin + route.PathPrefix, '_blank');
|
||||||
}}
|
}}
|
||||||
onDelete={() => {
|
onDelete={() => {
|
||||||
window.open('/ui/config/proxy#'+route.Name, '_blank');
|
window.open('/ui/config-url#'+route.Name, '_blank');
|
||||||
}}
|
}}
|
||||||
deleteIcon={<SettingOutlined />}
|
deleteIcon={<SettingOutlined />}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
})}
|
})}
|
||||||
{getContainersRoutes(app.Names[0].replace('/', '')).length == 0 &&
|
{/* {getContainersRoutes(app.Names[0].replace('/', '')).length == 0 && */}
|
||||||
<Chip label="No Proxy Setup" />}
|
<Chip
|
||||||
|
label="New"
|
||||||
|
color="primary"
|
||||||
|
style={{paddingRight: '4px'}}
|
||||||
|
deleteIcon={<PlusCircleOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
setOpenModal(app);
|
||||||
|
}}
|
||||||
|
onDelete={() => {
|
||||||
|
setOpenModal(app);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* } */}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack>
|
{/* <Stack>
|
||||||
<Button variant="contained" color="primary" onClick={() => {
|
<Button variant="contained" color="primary" onClick={() => {
|
||||||
setOpenModal(app);
|
setOpenModal(app);
|
||||||
}}>Connect</Button>
|
}}>Connect</Button>
|
||||||
</Stack>
|
</Stack> */}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Item></Grid2>
|
</Item></Grid2>
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,15 +41,15 @@ const MainRoutes = {
|
||||||
element: <ServeApps />
|
element: <ServeApps />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/ui/config/users',
|
path: '/ui/config-users',
|
||||||
element: <UserManagement />
|
element: <UserManagement />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/ui/config/general',
|
path: '/ui/config-general',
|
||||||
element: <ConfigManagement />
|
element: <ConfigManagement />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/ui/config/proxy',
|
path: '/ui/config-url',
|
||||||
element: <ProxyManagement />
|
element: <ProxyManagement />
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
// ==============================|| PRESET THEME - THEME SELECTOR ||============================== //
|
// ==============================|| PRESET THEME - THEME SELECTOR ||============================== //
|
||||||
|
|
||||||
|
import { purple, pink, deepPurple } from '@mui/material/colors';
|
||||||
|
|
||||||
const Theme = (colors) => {
|
const Theme = (colors) => {
|
||||||
|
console.log(colors)
|
||||||
const { blue, red, gold, cyan, green, grey } = colors;
|
const { blue, red, gold, cyan, green, grey } = colors;
|
||||||
const greyColors = {
|
const greyColors = {
|
||||||
0: grey[0],
|
0: grey[0],
|
||||||
|
@ -25,33 +28,10 @@ const Theme = (colors) => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
primary: {
|
primary: {
|
||||||
lighter: '#C8A2C8',
|
main: purple[400],
|
||||||
100: '#B785B7',
|
|
||||||
200: '#B785B7',
|
|
||||||
light: '#A668A6',
|
|
||||||
400: '#A668A6',
|
|
||||||
main: '#8D538D',
|
|
||||||
dark: '#704270',
|
|
||||||
700: '#533153',
|
|
||||||
darker:'#362036',
|
|
||||||
900: '#1A0E1A',
|
|
||||||
contrastText
|
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
lighter: greyColors[100],
|
main: deepPurple[100]
|
||||||
100: greyColors[100],
|
|
||||||
200: greyColors[200],
|
|
||||||
light: greyColors[300],
|
|
||||||
400: greyColors[400],
|
|
||||||
main: greyColors[500],
|
|
||||||
600: greyColors[600],
|
|
||||||
dark: greyColors[700],
|
|
||||||
800: greyColors[800],
|
|
||||||
darker: greyColors[900],
|
|
||||||
A100: greyColors[0],
|
|
||||||
A200: greyColors.A400,
|
|
||||||
A300: greyColors.A700,
|
|
||||||
contrastText: greyColors[0]
|
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
lighter: red[0],
|
lighter: red[0],
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cosmos-server",
|
"name": "cosmos-server",
|
||||||
"version": "0.1.14",
|
"version": "0.1.15",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "test-server.js",
|
"main": "test-server.js",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|
Loading…
Reference in a new issue