[release] version 0.5.0-unstable3

This commit is contained in:
Yann Stepienik 2023-05-14 13:11:59 +01:00
parent c670456d47
commit 3cbd88f4a6
10 changed files with 171 additions and 22 deletions

View file

@ -1,3 +1,11 @@
## Version 0.5.0
- Add Terminal to containers
- Add Create Container
- Add support for importing Docker Compose
- Fixed 2 bugs with the smart shield, that made it too strict
- Added more infoon the shield when blocking someone
- Fixed home background image
## Version 0.4.3
- Fix for exposing routes from the details page

View file

@ -61,10 +61,6 @@ const preStyle = {
marginTop: '10px',
marginLeft: '0',
marginRight: '0',
display: 'block',
textAlign: 'left',
verticalAlign: 'baseline',
opacity: '1',
}
const DockerComposeImport = () => {
@ -73,14 +69,22 @@ const DockerComposeImport = () => {
const [openModal, setOpenModal] = useState(false);
const [dockerCompose, setDockerCompose] = useState('');
const [service, setService] = useState({});
const [ymlError, setYmlError] = useState('');
useEffect(() => {
if(dockerCompose === '') {
return;
}
let doc;
let newService = {};
let doc = yaml.load(dockerCompose);
try {
doc = yaml.load(dockerCompose);
} catch (e) {
console.log(e);
setYmlError(e.message);
return;
}
// convert to the proper format
if(doc.services) {
@ -110,7 +114,6 @@ const DockerComposeImport = () => {
}
setService(doc);
console.log(doc);
}, [dockerCompose]);
return <>
@ -134,6 +137,10 @@ const DockerComposeImport = () => {
reader.readAsText(file);
}}
/>
<div style={{color: 'red'}}>
{ymlError}
</div>
<TextField
multiline
@ -141,7 +148,12 @@ const DockerComposeImport = () => {
fullWidth
value={dockerCompose}
onChange={(e) => setDockerCompose(e.target.value)}
style={preStyle}
sx={preStyle}
InputProps={{
sx: {
color: '#EEE',
}
}}
rows={20}></TextField>
</Stack>}
{step === 1 && <Stack spacing={2}>
@ -154,6 +166,7 @@ const DockerComposeImport = () => {
setOpenModal(false);
setStep(0);
setDockerCompose('');
setYmlError('');
}}>Close</Button>
<Button onClick={() => {
if(step === 0) {

View file

@ -18,6 +18,7 @@ import NetworkContainerSetup from './network';
import VolumeContainerSetup from './volumes';
import DockerTerminal from './terminal';
import { Link } from 'react-router-dom';
import { smartDockerLogConcat, tryParseProgressLog } from '../../../utils/docker';
const preStyle = {
backgroundColor: '#000',
@ -56,6 +57,7 @@ const NewDockerService = ({service}) => {
const [log, setLog] = React.useState([]);
const [isDone, setIsDone] = React.useState(false);
const [openModal, setOpenModal] = React.useState(false);
const preRef = React.useRef(null);
React.useEffect(() => {
// refreshContainer();
@ -64,7 +66,8 @@ const NewDockerService = ({service}) => {
const create = () => {
setLog([])
API.docker.createService(service, (newlog) => {
setLog((old) => [...old, newlog]);
setLog((old) => smartDockerLogConcat(old, newlog));
preRef.current.scrollTop = preRef.current.scrollHeight;
if (newlog.includes('[OPERATION SUCCEEDED]')) {
setIsDone(true);
}
@ -100,19 +103,11 @@ const NewDockerService = ({service}) => {
}}
>Restart</Button>
}
<Link to={`/ui/servapps`}>
<Button
variant="outlined"
color="primary"
fullWidth
startIcon={<AppstoreOutlined />}
>Back to Servapps</Button>
</Link>
</Stack>}
<pre style={preStyle}>
<pre style={preStyle} ref={preRef}>
{!log.length && JSON.stringify(service, false ,2)}
{log.map((l) => {
return <div>{l}</div>
return <div>{tryParseProgressLog(l)}</div>
})}
</pre>
</Stack>

View file

@ -205,7 +205,7 @@ const NewDockerServiceForm = () => {
{
title: 'Storage',
disabled: maxTab < 2,
children: <Stack spacing={2}><VolumeContainerSetup ontainer containerInfo={containerInfo} OnChange={(values) => {
children: <Stack spacing={2}><VolumeContainerSetup newContainer containerInfo={containerInfo} OnChange={(values) => {
console.log(values)
const newValues = {
...containerInfo,

View file

@ -12,6 +12,8 @@ const DockerTerminal = ({containerInfo, refresh}) => {
const isInteractive = Config.Tty;
const theme = useTheme();
const isMobile = useMediaQuery((theme) => theme.breakpoints.down('sm'))
const [history, setHistory] = useState([]);
const [historyCursor, setHistoryCursor] = useState(0);
const [message, setMessage] = useState('');
const [output, setOutput] = useState([
@ -24,10 +26,31 @@ const DockerTerminal = ({containerInfo, refresh}) => {
const [isConnected, setIsConnected] = useState(false);
const handleKeyDown = (e) => {
if (e.key === 'Enter' && e.ctrlKey) {
if (e.key === 'Enter') {
// shift + enter for new line
if (e.shiftKey) {
return;
}
e.preventDefault();
sendMessage();
}
if (e.key === 'ArrowUp') {
e.preventDefault();
if (historyCursor > 0) {
setHistoryCursor(historyCursor - 1);
setMessage(history[historyCursor - 1]);
}
}
if (e.key === 'ArrowDown') {
e.preventDefault();
if (historyCursor < history.length - 1) {
setHistoryCursor(historyCursor + 1);
setMessage(history[historyCursor + 1]);
} else {
setHistoryCursor(history.length);
setMessage('');
}
}
};
const makeInteractive = () => {
@ -85,7 +108,9 @@ const DockerTerminal = ({containerInfo, refresh}) => {
const sendMessage = () => {
if (ws.current) {
ws.current.send(message);
setHistoryCursor(history.length + 1);
setMessage('');
setHistory((prevHistory) => [...prevHistory, message]);
}
};

View file

@ -0,0 +1,42 @@
export const smartDockerLogConcat = (log, newLogRaw) => {
const containsId = (log, id) => {
try {
const parsedLog = JSON.parse(log);
return parsedLog.id === id;
}
catch (e) {
return false;
}
}
try {
const newLog = JSON.parse(newLogRaw);
if (newLog.id) {
if (log.find((l) => containsId(l, newLog.id))) {
return log.map((l) => (containsId(l, newLog.id) ? newLogRaw : l));
} else {
return [...log, newLogRaw];
}
}
return [...log, newLogRaw];
} catch (e) {
console.log(e);
return [...log, newLogRaw];
}
}
export const tryParseProgressLog = (log) => {
try {
const parsedLog = JSON.parse(log);
if (parsedLog.status && parsedLog.progress) {
return `${parsedLog.id} ${parsedLog.status} ${parsedLog.progress}`
} else if (parsedLog.status && parsedLog.progressDetail && parsedLog.progressDetail.current) {
return `${parsedLog.id} ${parsedLog.status} ${parsedLog.progressDetail.current}/${parsedLog.progressDetail.total}`
} else if (parsedLog.status) {
return `${parsedLog.id} ${parsedLog.status} ${parsedLog.sha256 || ""}`
}
return log;
} catch (e) {
return log;
}
}

View file

@ -1,6 +1,6 @@
{
"name": "cosmos-server",
"version": "0.5.0-unstable2",
"version": "0.5.0-unstable3",
"description": "",
"main": "test-server.js",
"bugs": {

View file

@ -7,6 +7,9 @@ import (
"strings"
"time"
"bufio"
"strconv"
"os"
"os/user"
"errors"
"github.com/docker/go-connections/nat"
"github.com/docker/docker/api/types/mount"
@ -311,6 +314,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "Image %s pulled\n", container.Image)
flusher.Flush()
}
// Create containers
for _, container := range serviceRequest.Services {
utils.Log(fmt.Sprintf("Creating container %s...", container.Name))
@ -362,6 +366,39 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
}
}
// 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)
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())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
return
}
// Change the ownership of the directory to the container.User
userInfo, err := user.Lookup(container.User)
if err != nil {
utils.Error("CreateService: Unable to lookup user", err)
fmt.Fprintf(w, "[ERROR] Unable to lookup user: "+err.Error())
flusher.Flush()
} else {
uid, _ := strconv.Atoi(userInfo.Uid)
gid, _ := strconv.Atoi(userInfo.Gid)
err = os.Chown(newmount.Source, 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())
flusher.Flush()
}
}
}
}
}
hostConfig := &conttype.HostConfig{
PortBindings: PortBindings,
Mounts: container.Volumes,

View file

@ -4,6 +4,8 @@ import (
"encoding/json"
"net/http"
"os"
"os/user"
"strconv"
"github.com/azukaar/cosmos-server/src/utils"
containerType "github.com/docker/docker/api/types/container"
@ -83,6 +85,33 @@ 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

@ -292,4 +292,4 @@ func Test() error {
// fmt.Println(jellyfin.NetworkSettings)
return nil
}
}