[release] v0.5.11

This commit is contained in:
Yann Stepienik 2023-05-27 13:58:33 +01:00
parent caddef01d1
commit c97ebed936
12 changed files with 356 additions and 182 deletions

View file

@ -1,4 +1,9 @@
## version 0.5.1 -> 0.5.7
## versiom 0.5.11
- Improve docker-compose import support for alternative syntaxes
- Improve docker service creation when using force secure label (fixes few containers not liking restarting too fast when created)
- Add toggle for using insecure HTTPS targets (fixes Unifi controller)
## version 0.5.1 -> 0.5.10
- Add Wilcard certificates support
- Auto switch to Mongo 4 if CPU has no ADX
- Improve setup for certificates on new install

View file

@ -46,6 +46,7 @@ const RouteManagement = ({ routeConfig, routeNames, TargetContainer, noControls
Target: routeConfig.Target,
UseHost: routeConfig.UseHost,
Host: routeConfig.Host,
AcceptInsecureHTTPSTarget: routeConfig.AcceptInsecureHTTPSTarget === true,
UsePathPrefix: routeConfig.UsePathPrefix,
PathPrefix: routeConfig.PathPrefix,
StripPathPrefix: routeConfig.StripPathPrefix,
@ -167,6 +168,12 @@ const RouteManagement = ({ routeConfig, routeNames, TargetContainer, noControls
/>
}
{formik.values.Target.startsWith('https://') && <CosmosCheckbox
name="AcceptInsecureHTTPSTarget"
label="Accept Insecure HTTPS Target (not recommended)"
formik={formik}
/>}
<CosmosFormDivider title={'Source'} />
<Grid item xs={12}>

View file

@ -1,7 +1,7 @@
// material-ui
import * as React from 'react';
import { Alert, Button, Stack, Typography } from '@mui/material';
import { WarningOutlined, PlusCircleOutlined, CopyOutlined, ExclamationCircleOutlined , SyncOutlined, UserOutlined, KeyOutlined, ArrowUpOutlined, FileZipOutlined } from '@ant-design/icons';
import { WarningOutlined, PlusCircleOutlined, CopyOutlined, ExclamationCircleOutlined, SyncOutlined, UserOutlined, KeyOutlined, ArrowUpOutlined, FileZipOutlined } from '@ant-design/icons';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
@ -28,13 +28,13 @@ import NewDockerService from './newService';
import yaml from 'js-yaml';
function checkIsOnline() {
API.isOnline().then((res) => {
window.location.reload();
}).catch((err) => {
setTimeout(() => {
checkIsOnline();
}, 1000);
});
API.isOnline().then((res) => {
window.location.reload();
}).catch((err) => {
setTimeout(() => {
checkIsOnline();
}, 1000);
});
}
const preStyle = {
@ -63,30 +63,25 @@ const preStyle = {
marginRight: '0',
}
const DockerComposeImport = ({refresh}) => {
const [step, setStep] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [openModal, setOpenModal] = useState(false);
const [dockerCompose, setDockerCompose] = useState('');
const [service, setService] = useState({});
const [ymlError, setYmlError] = useState('');
const DockerComposeImport = ({ refresh }) => {
const [step, setStep] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [openModal, setOpenModal] = useState(false);
const [dockerCompose, setDockerCompose] = useState('');
const [service, setService] = useState({});
const [ymlError, setYmlError] = useState('');
useEffect(() => {
if(dockerCompose === '') {
return;
}
useEffect(() => {
if (dockerCompose === '') {
return;
}
setYmlError('');
let doc;
let newService = {};
try {
doc = yaml.load(dockerCompose);
setYmlError('');
let doc;
let newService = {};
try {
doc = yaml.load(dockerCompose);
} catch (e) {
console.log(e);
setYmlError(e.message);
return;
}
if (typeof doc === 'object' && doc !== null && Object.keys(doc).length > 0 &&
!doc.services && !doc.networks && !doc.volumes) {
doc = {
@ -94,52 +89,66 @@ const DockerComposeImport = ({refresh}) => {
}
}
// convert to the proper format
if(doc.services) {
if (doc.services) {
Object.keys(doc.services).forEach((key) => {
// convert volumes
if(doc.services[key].volumes) {
let volumes = [];
doc.services[key].volumes.forEach((volume) => {
let volumeSplit = volume.split(':');
let volumeObj = {
Source: volumeSplit[0],
Target: volumeSplit[1],
Type: volume[0] === '/' ? 'bind' : 'volume',
};
volumes.push(volumeObj);
});
doc.services[key].volumes = volumes;
if (doc.services[key].volumes) {
if(Array.isArray(doc.services[key].volumes)) {
let volumes = [];
doc.services[key].volumes.forEach((volume) => {
if (typeof volume === 'object') {
volumes.push(volume);
} else {
let volumeSplit = volume.split(':');
let volumeObj = {
Source: volumeSplit[0],
Target: volumeSplit[1],
Type: volume[0] === '/' ? 'bind' : 'volume',
};
volumes.push(volumeObj);
}
});
doc.services[key].volumes = volumes;
}
}
// convert expose
if(doc.services[key].expose) {
if (doc.services[key].expose) {
doc.services[key].expose = doc.services[key].expose.map((port) => {
return ''+port;
return '' + port;
})
}
//convert user
if(doc.services[key].user) {
if (doc.services[key].user) {
doc.services[key].user = '' + doc.services[key].user;
}
// convert labels:
if(doc.services[key].labels) {
if(Array.isArray(doc.services[key].labels)) {
if (doc.services[key].labels) {
if (Array.isArray(doc.services[key].labels)) {
let labels = {};
doc.services[key].labels.forEach((label) => {
const [key, value] = label.split('=');
labels[''+key] = ''+value;
labels['' + key] = '' + value;
});
doc.services[key].labels = labels;
}
if (typeof doc.services[key].labels == 'object') {
let labels = {};
Object.keys(doc.services[key].labels).forEach((keylabel) => {
labels['' + keylabel] = '' + doc.services[key].labels[keylabel];
});
doc.services[key].labels = labels;
}
}
// convert environment
if(doc.services[key].environment) {
if(!Array.isArray(doc.services[key].environment)) {
if (doc.services[key].environment) {
if (!Array.isArray(doc.services[key].environment)) {
let environment = [];
Object.keys(doc.services[key].environment).forEach((keyenv) => {
environment.push(keyenv + '=' + doc.services[key].environment[keyenv]);
@ -149,100 +158,155 @@ const DockerComposeImport = ({refresh}) => {
}
// convert network
if(doc.services[key].networks) {
if(Array.isArray(doc.services[key].networks)) {
if (doc.services[key].networks) {
if (Array.isArray(doc.services[key].networks)) {
let networks = {};
doc.services[key].networks.forEach((network) => {
networks[''+network] = {};
if (typeof network === 'object') {
networks['' + network.name] = network;
}
else
networks['' + network] = {};
});
doc.services[key].networks = networks;
}
}
// ensure container_name
if(!doc.services[key].container_name) {
if (!doc.services[key].container_name) {
doc.services[key].container_name = key;
}
});
}
setService(doc);
}, [dockerCompose]);
// convert networks
if (doc.networks) {
if (Array.isArray(doc.networks)) {
let networks = {};
doc.networks.forEach((network) => {
if (typeof network === 'object') {
networks['' + network.name] = network;
}
else
networks['' + network] = {};
});
doc.networks = networks;
} else {
let networks = {};
Object.keys(doc.networks).forEach((key) => {
networks['' + key] = doc.networks[key] || {};
});
doc.networks = networks;
}
}
return <>
<Dialog open={openModal} onClose={() => setOpenModal(false)}>
<DialogTitle>Import Docker Compose</DialogTitle>
<DialogContent>
<DialogContentText>
{step === 0 && <Stack spacing={2}>
<Alert severity="warning" icon={<WarningOutlined />}>
This is a highly experimental feature. It is recommended to use with caution.
</Alert>
// convert volumes
if (doc.volumes) {
if (Array.isArray(doc.volumes)) {
let volumes = {};
doc.volumes.forEach((volume) => {
if(!volume) {
volume = {};
}
if (typeof volume === 'object') {
volumes['' + volume.name] = volume;
}
else
volumes['' + volume] = {};
});
doc.volumes = volumes;
} else {
let volumes = {};
Object.keys(doc.volumes).forEach((key) => {
volumes['' + key] = doc.volumes[key] || {};
});
doc.volumes = volumes;
}
}
<UploadButtons
accept='.yml,.yaml'
OnChange={(e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (e) => {
setDockerCompose(e.target.result);
};
reader.readAsText(file);
}}
/>
} catch (e) {
console.log(e);
setYmlError(e.message);
return;
}
<div style={{color: 'red'}}>
{ymlError}
</div>
<TextField
multiline
placeholder='Paste your docker-compose.yml here or use the file upload button.'
fullWidth
value={dockerCompose}
onChange={(e) => setDockerCompose(e.target.value)}
sx={preStyle}
InputProps={{
sx: {
color: '#EEE',
}
}}
rows={20}></TextField>
</Stack>}
{step === 1 && <Stack spacing={2}>
<NewDockerService service={service} refresh={refresh}/>
</Stack>}
</DialogContentText>
</DialogContent>
{!isLoading && <DialogActions>
<Button onClick={() => {
setOpenModal(false);
setStep(0);
setDockerCompose('');
setYmlError('');
}}>Close</Button>
<Button onClick={() => {
if(step === 0) {
setStep(1);
} else {
setStep(0);
}
}}>
{step === 0 && 'Next'}
{step === 1 && 'Back'}
</Button>
</DialogActions>}
</Dialog>
setService(doc);
}, [dockerCompose]);
<ResponsiveButton
color="primary"
onClick={() => setOpenModal(true)}
variant="outlined"
startIcon={<ArrowUpOutlined />}
>
Import Docker Compose
</ResponsiveButton>
</>;
return <>
<Dialog open={openModal} onClose={() => setOpenModal(false)}>
<DialogTitle>Import Docker Compose</DialogTitle>
<DialogContent>
<DialogContentText>
{step === 0 && <Stack spacing={2}>
<Alert severity="warning" icon={<WarningOutlined />}>
This is a highly experimental feature. It is recommended to use with caution.
</Alert>
<UploadButtons
accept='.yml,.yaml'
OnChange={(e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (e) => {
setDockerCompose(e.target.result);
};
reader.readAsText(file);
}}
/>
<div style={{ color: 'red' }}>
{ymlError}
</div>
<TextField
multiline
placeholder='Paste your docker-compose.yml here or use the file upload button.'
fullWidth
value={dockerCompose}
onChange={(e) => setDockerCompose(e.target.value)}
sx={preStyle}
InputProps={{
sx: {
color: '#EEE',
}
}}
rows={20}></TextField>
</Stack>}
{step === 1 && <Stack spacing={2}>
<NewDockerService service={service} refresh={refresh} />
</Stack>}
</DialogContentText>
</DialogContent>
{!isLoading && <DialogActions>
<Button onClick={() => {
setOpenModal(false);
setStep(0);
setDockerCompose('');
setYmlError('');
}}>Close</Button>
<Button onClick={() => {
if (step === 0) {
setStep(1);
} else {
setStep(0);
}
}}>
{step === 0 && 'Next'}
{step === 1 && 'Back'}
</Button>
</DialogActions>}
</Dialog>
<ResponsiveButton
color="primary"
onClick={() => setOpenModal(true)}
variant="outlined"
startIcon={<ArrowUpOutlined />}
>
Import Docker Compose
</ResponsiveButton>
</>;
};
export default DockerComposeImport;

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "cosmos-server",
"version": "0.5.0-unstable",
"version": "0.5.10",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "cosmos-server",
"version": "0.5.0-unstable",
"version": "0.5.10",
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons": "^4.7.0",

View file

@ -1,6 +1,6 @@
{
"name": "cosmos-server",
"version": "0.5.10",
"version": "0.5.11",
"description": "",
"main": "test-server.js",
"bugs": {
@ -56,10 +56,10 @@
"client": "vite",
"client-build": "vite build --base=/ui/",
"start": "env COSMOS_HOSTNAME=localhost CONFIG_FILE=./config_dev.json EZ=UTC build/cosmos",
"build": " sh build.sh",
"build": "sh build.sh",
"dev": "npm run build && npm run start",
"dockerdevbuild": "sh build.sh && npm run client-build && docker build --tag cosmos-dev .",
"dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run -d -p 80:80 -p 443:443 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev",
"dockerdevbuild": "sh build.sh && docker build --tag cosmos-dev .",
"dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run -d -p 80:80 -p 443:443 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG -v /:/mnt/host --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev",
"dockerdev": "npm run dockerdevbuild && npm run dockerdevrun",
"demo": "vite build --base=/ui/ --mode demo",
"devdemo": "vite --mode demo"

View file

@ -110,7 +110,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 -h cosmos-server --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/cosmos:/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 /:/mnt/host -v /var/lib/cosmos:/config azukaar/cosmos-server:latest
```
Once installed, simply go to `http://your-server-ip` and follow the instructions of the setup wizard.

View file

@ -22,6 +22,12 @@ import (
"github.com/azukaar/cosmos-server/src/utils"
)
type ContainerCreateRequestServiceNetwork struct {
Aliases []string `json:"aliases,omitempty"`
IPV4Address string `json:"ipv4_address,omitempty"`
IPV6Address string `json:"ipv6_address,omitempty"`
}
type ContainerCreateRequestContainer struct {
Name string `json:"container_name"`
Image string `json:"image"`
@ -29,12 +35,8 @@ type ContainerCreateRequestContainer struct {
Labels map[string]string `json:"labels"`
Ports []string `json:"ports"`
Volumes []mount.Mount `json:"volumes"`
Networks map[string]struct {
Aliases []string `json:"aliases,omitempty"`
IPV4Address string `json:"ipv4_address,omitempty"`
IPV6Address string `json:"ipv6_address,omitempty"`
} `json:"networks"`
Routes []utils.ProxyRouteConfig `json:"routes"`
Networks map[string]ContainerCreateRequestServiceNetwork `json:"networks"`
Routes []utils.ProxyRouteConfig `json:"routes"`
RestartPolicy string `json:"restart,omitempty"`
Devices []string `json:"devices"`
@ -100,7 +102,7 @@ type ContainerCreateRequestNetwork struct {
type DockerServiceCreateRequest struct {
Services map[string]ContainerCreateRequestContainer `json:"services"`
Volumes []ContainerCreateRequestVolume `json:"volumes"`
Volumes map[string]ContainerCreateRequestVolume `json:"volumes"`
Networks map[string]ContainerCreateRequestNetwork `json:"networks"`
}
@ -137,6 +139,9 @@ func Rollback(actions []DockerServiceCreateRollback , w http.ResponseWriter, flu
flusher.Flush()
}
case "network":
if os.Getenv("HOSTNAME") != "" {
DockerClient.NetworkDisconnect(DockerContext, action.Name, os.Getenv("HOSTNAME"), true)
}
err := DockerClient.NetworkRemove(DockerContext, action.Name)
if err != nil {
utils.Error("Rollback: Network", err)
@ -220,6 +225,48 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
var rollbackActions []DockerServiceCreateRollback
var err error
// check if services have the cosmos-force-network-secured label
for serviceName, service := range serviceRequest.Services {
utils.Log(fmt.Sprintf("Checking service %s...", serviceName))
fmt.Fprintf(w, "Checking service %s...\n", serviceName)
flusher.Flush()
if service.Labels["cosmos-force-network-secured"] == "true" {
utils.Log(fmt.Sprintf("Forcing secure %s...", serviceName))
fmt.Fprintf(w, "Forcing secure %s...\n", serviceName)
flusher.Flush()
newNetwork, errNC := CreateCosmosNetwork()
if errNC != nil {
utils.Error("CreateService: Network", err)
fmt.Fprintf(w, "[ERROR] Network %s cant be created\n", newNetwork)
flusher.Flush()
Rollback(rollbackActions, w, flusher)
return err
}
service.Labels["cosmos-network-name"] = newNetwork
AttachNetworkToCosmos(newNetwork)
if service.Networks == nil {
service.Networks = make(map[string]ContainerCreateRequestServiceNetwork)
}
service.Networks[newNetwork] = ContainerCreateRequestServiceNetwork{}
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
Action: "remove",
Type: "network",
Name: newNetwork,
})
utils.Log(fmt.Sprintf("Created secure network %s", newNetwork))
fmt.Fprintf(w, "Created secure network %s\n", newNetwork)
flusher.Flush()
}
}
// Create networks
for networkToCreateName, networkToCreate := range serviceRequest.Networks {
utils.Log(fmt.Sprintf("Creating network %s...", networkToCreateName))
@ -391,11 +438,32 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
// Create missing folders for bind mounts
for _, newmount := range container.Volumes {
if newmount.Type == mount.TypeBind {
if _, err := os.Stat(newmount.Source); os.IsNotExist(err) {
err := os.MkdirAll(newmount.Source, 0755)
newSource := newmount.Source
if os.Getenv("HOSTNAME") != "" {
if _, err := os.Stat("/mnt/host"); os.IsNotExist(err) {
utils.Error("CreateService: Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself", err)
fmt.Fprintf(w, "[ERROR] Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
return err
}
newSource = "/mnt/host" + newSource
}
utils.Log(fmt.Sprintf("Checking directory %s for bind mount", newSource))
fmt.Fprintf(w, "Checking directory %s for bind mount\n", newSource)
flusher.Flush()
if _, err := os.Stat(newSource); os.IsNotExist(err) {
utils.Log(fmt.Sprintf("Not found. Creating directory %s for bind mount", newSource))
fmt.Fprintf(w, "Not found. Creating directory %s for bind mount\n", newSource)
flusher.Flush()
err := os.MkdirAll(newSource, 0755)
if err != nil {
utils.Error("CreateService: Unable to create directory for bind mount", err)
fmt.Fprintf(w, "[ERROR] Unable to create directory for bind mount: "+err.Error())
utils.Error("CreateService: Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory", err)
fmt.Fprintf(w, "[ERROR] Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory for bind mount: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
return err
@ -411,7 +479,7 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
} else {
uid, _ := strconv.Atoi(userInfo.Uid)
gid, _ := strconv.Atoi(userInfo.Gid)
err = os.Chown(newmount.Source, uid, gid)
err = os.Chown(newSource, uid, gid)
if err != nil {
utils.Error("CreateService: Unable to change ownership of directory", err)
fmt.Fprintf(w, "[ERROR] Unable to change ownership of directory: "+err.Error())

View file

@ -4,8 +4,6 @@ import (
"encoding/json"
"net/http"
"os"
"os/user"
"strconv"
"github.com/azukaar/cosmos-server/src/utils"
containerType "github.com/docker/docker/api/types/container"
@ -86,33 +84,6 @@ func UpdateContainerRoute(w http.ResponseWriter, req *http.Request) {
}
}
if(form.Volumes != nil) {
// Create missing folders for bind mounts
for _, newmount := range form.Volumes {
if newmount.Type == mount.TypeBind {
if _, err := os.Stat(newmount.Source); os.IsNotExist(err) {
err := os.MkdirAll(newmount.Source, 0755)
if err != nil {
utils.Error("UpdateService: Unable to create directory for bind mount", err)
utils.HTTPError(w, "Unable to create directory for bind mount: "+err.Error(), http.StatusInternalServerError, "DS004")
return
}
// Change the ownership of the directory to the container.User
userInfo, err := user.Lookup(container.Config.User)
if err != nil {
utils.Error("UpdateService: Unable to lookup user", err)
} else {
uid, _ := strconv.Atoi(userInfo.Uid)
gid, _ := strconv.Atoi(userInfo.Gid)
err = os.Chown(newmount.Source, uid, gid)
if err != nil {
utils.Error("UpdateService: Unable to change ownership of directory", err)
}
}
}
}
}
container.HostConfig.Mounts = form.Volumes
container.HostConfig.Binds = []string{}
}

View file

@ -5,12 +5,17 @@ import (
"errors"
"time"
"bufio"
"os"
"os/user"
"fmt"
"strings"
"strconv"
"github.com/azukaar/cosmos-server/src/utils"
"github.com/docker/docker/client"
// natting "github.com/docker/go-connections/nat"
"github.com/docker/docker/api/types/container"
mountType "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types"
)
@ -111,6 +116,49 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON, noLock
oldContainer := newConfig
if(oldContainerID != "") {
// create missing folders
for _, newmount := range newConfig.HostConfig.Mounts {
if newmount.Type == mountType.TypeBind {
newSource := newmount.Source
if os.Getenv("HOSTNAME") != "" {
if _, err := os.Stat("/mnt/host"); os.IsNotExist(err) {
utils.Error("EditContainer: Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself", err)
return "", errors.New("Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself")
}
newSource = "/mnt/host" + newSource
}
utils.Log(fmt.Sprintf("Checking directory %s for bind mount", newSource))
if _, err := os.Stat(newSource); os.IsNotExist(err) {
utils.Log(fmt.Sprintf("Not found. Creating directory %s for bind mount", newSource))
err := os.MkdirAll(newSource, 0755)
if err != nil {
utils.Error("EditContainer: Unable to create directory for bind mount", err)
return "", errors.New("Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory")
}
if newConfig.Config.User != "" {
// Change the ownership of the directory to the container.User
userInfo, err := user.Lookup(newConfig.Config.User)
if err != nil {
utils.Error("EditContainer: Unable to lookup user", err)
} else {
uid, _ := strconv.Atoi(userInfo.Uid)
gid, _ := strconv.Atoi(userInfo.Gid)
err = os.Chown(newSource, uid, gid)
if err != nil {
utils.Error("EditContainer: Unable to change ownership of directory", err)
}
}
}
}
}
}
utils.Log("EditContainer - Container updating. Retriveing currently running " + oldContainerID)
var err error

View file

@ -4,12 +4,13 @@ import (
"net/http"
"net/http/httputil"
"net/url"
"crypto/tls"
spa "github.com/roberthodgen/spa-server"
"github.com/azukaar/cosmos-server/src/utils"
)
// NewProxy takes target host and creates a reverse proxy
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool) (*httputil.ReverseProxy, error) {
url, err := url.Parse(targetHost)
if err != nil {
return nil, err
@ -17,6 +18,12 @@ func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
proxy := httputil.NewSingleHostReverseProxy(url)
if AcceptInsecureHTTPSTarget && url.Scheme == "https" {
proxy.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
proxy.ModifyResponse = func(resp *http.Response) error {
utils.Debug("Response from backend: " + resp.Status)
utils.Debug("URL was " + resp.Request.URL.String())
@ -35,7 +42,7 @@ func RouteTo(route utils.ProxyRouteConfig) http.Handler {
routeType := route.Mode
if(routeType == "SERVAPP" || routeType == "PROXY") {
proxy, err := NewProxy(destination)
proxy, err := NewProxy(destination, route.AcceptInsecureHTTPSTarget)
if err != nil {
utils.Error("Create Route", err)
}

View file

@ -148,6 +148,7 @@ type ProxyRouteConfig struct {
Mode ProxyMode
BlockCommonBots bool
BlockAPIAbuse bool
AcceptInsecureHTTPSTarget bool
}
type EmailConfig struct {

View file

@ -8,7 +8,6 @@ export default defineConfig({
build: {
outDir: '../static',
},
// base: '/ui',
server: {
proxy: {
'/cosmos/api': {
@ -16,6 +15,10 @@ export default defineConfig({
secure: false,
ws: true,
}
},
watch: {
usePolling: true
}
}
})