[release] v0.7.0-unstable2
This commit is contained in:
parent
e69c81fe7a
commit
8d6329f8d5
|
@ -2,12 +2,14 @@ import * as _auth from './authentication';
|
||||||
import * as _users from './users';
|
import * as _users from './users';
|
||||||
import * as _config from './config';
|
import * as _config from './config';
|
||||||
import * as _docker from './docker';
|
import * as _docker from './docker';
|
||||||
|
import * as _market from './market';
|
||||||
|
|
||||||
import * as authDemo from './authentication.demo';
|
import * as authDemo from './authentication.demo';
|
||||||
import * as usersDemo from './users.demo';
|
import * as usersDemo from './users.demo';
|
||||||
import * as configDemo from './config.demo';
|
import * as configDemo from './config.demo';
|
||||||
import * as dockerDemo from './docker.demo';
|
import * as dockerDemo from './docker.demo';
|
||||||
import * as indexDemo from './index.demo';
|
import * as indexDemo from './index.demo';
|
||||||
|
import * as marketDemo from './market.demo';
|
||||||
|
|
||||||
import wrap from './wrap';
|
import wrap from './wrap';
|
||||||
|
|
||||||
|
@ -101,7 +103,7 @@ let newInstall = (req, onProgress) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkHost = (host) => {
|
let checkHost = (host) => {
|
||||||
return fetch('/cosmos/api/dns-check?url=' + host, {
|
return fetch('/cosmos/api/dns-check?url=' + host, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -124,7 +126,7 @@ const checkHost = (host) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDNS = (host) => {
|
let getDNS = (host) => {
|
||||||
return fetch('/cosmos/api/dns?url=' + host, {
|
return fetch('/cosmos/api/dns?url=' + host, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -153,15 +155,19 @@ let auth = _auth;
|
||||||
let users = _users;
|
let users = _users;
|
||||||
let config = _config;
|
let config = _config;
|
||||||
let docker = _docker;
|
let docker = _docker;
|
||||||
|
let market = _market;
|
||||||
|
|
||||||
if(isDemo) {
|
if(isDemo) {
|
||||||
auth = authDemo;
|
auth = authDemo;
|
||||||
users = usersDemo;
|
users = usersDemo;
|
||||||
config = configDemo;
|
config = configDemo;
|
||||||
docker = dockerDemo;
|
docker = dockerDemo;
|
||||||
|
market = marketDemo;
|
||||||
getStatus = indexDemo.getStatus;
|
getStatus = indexDemo.getStatus;
|
||||||
newInstall = indexDemo.newInstall;
|
newInstall = indexDemo.newInstall;
|
||||||
isOnline = indexDemo.isOnline;
|
isOnline = indexDemo.isOnline;
|
||||||
|
checkHost = indexDemo.checkHost;
|
||||||
|
getDNS = indexDemo.getDNS;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -169,6 +175,7 @@ export {
|
||||||
users,
|
users,
|
||||||
config,
|
config,
|
||||||
docker,
|
docker,
|
||||||
|
market,
|
||||||
getStatus,
|
getStatus,
|
||||||
newInstall,
|
newInstall,
|
||||||
isOnline,
|
isOnline,
|
||||||
|
|
9
client/src/api/market.demo.ts
Normal file
9
client/src/api/market.demo.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import wrap from './wrap';
|
||||||
|
|
||||||
|
function list() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
list,
|
||||||
|
};
|
14
client/src/api/market.ts
Normal file
14
client/src/api/market.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import wrap from './wrap';
|
||||||
|
|
||||||
|
function list() {
|
||||||
|
return wrap(fetch('/cosmos/api/markets', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
list,
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
// assets
|
// assets
|
||||||
import { HomeOutlined, AppstoreOutlined, DashboardOutlined } from '@ant-design/icons';
|
import { HomeOutlined, AppstoreOutlined, DashboardOutlined, AppstoreAddOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
// icons
|
// icons
|
||||||
const icons = {
|
const icons = {
|
||||||
|
@ -29,6 +29,14 @@ const dashboard = {
|
||||||
icon: DashboardOutlined,
|
icon: DashboardOutlined,
|
||||||
breadcrumbs: false
|
breadcrumbs: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'market',
|
||||||
|
title: 'Market',
|
||||||
|
type: 'item',
|
||||||
|
url: '/ui/market-listing',
|
||||||
|
icon: AppstoreAddOutlined,
|
||||||
|
breadcrumbs: false
|
||||||
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { getFullOrigin } from "../../utils/routes";
|
||||||
import IsLoggedIn from "../../isLoggedIn";
|
import IsLoggedIn from "../../isLoggedIn";
|
||||||
|
|
||||||
|
|
||||||
const HomeBackground = () => {
|
export const HomeBackground = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isDark = theme.palette.mode === 'dark';
|
const isDark = theme.palette.mode === 'dark';
|
||||||
return (
|
return (
|
||||||
|
@ -27,6 +27,50 @@ const HomeBackground = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const TransparentHeader = () => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const isDark = theme.palette.mode === 'dark';
|
||||||
|
|
||||||
|
const backColor = isDark ? '0,0,0' : '255,255,255';
|
||||||
|
const textColor = isDark ? 'white' : 'dark';
|
||||||
|
|
||||||
|
return <style>
|
||||||
|
{`header {
|
||||||
|
background: rgba(${backColor},0.35) !important;
|
||||||
|
border-bottom-color: rgba(${backColor},0.4) !important;
|
||||||
|
color: ${textColor} !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .MuiChip-label {
|
||||||
|
color: ${textColor} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .MuiButtonBase-root, header .MuiChip-colorDefault {
|
||||||
|
color: ${textColor} !important;
|
||||||
|
background: rgba(${backColor},0.5) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
transition: background 0.1s ease-in-out;
|
||||||
|
transition: transform 0.1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: rgba(${backColor},0.8) !important;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.MuiAlert-standard {
|
||||||
|
background: rgba(${backColor},0.35) !important;
|
||||||
|
color: ${textColor} !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</style>;
|
||||||
|
}
|
||||||
|
|
||||||
const HomePage = () => {
|
const HomePage = () => {
|
||||||
const { routeName } = useParams();
|
const { routeName } = useParams();
|
||||||
const [serveApps, setServeApps] = useState([]);
|
const [serveApps, setServeApps] = useState([]);
|
||||||
|
@ -52,9 +96,6 @@ const HomePage = () => {
|
||||||
background: 'rgba(255,255,255,0.35)',
|
background: 'rgba(255,255,255,0.35)',
|
||||||
}
|
}
|
||||||
|
|
||||||
const backColor = isDark ? '0,0,0' : '255,255,255';
|
|
||||||
const textColor = isDark ? 'white' : 'dark';
|
|
||||||
|
|
||||||
|
|
||||||
const refreshStatus = () => {
|
const refreshStatus = () => {
|
||||||
API.getStatus().then((res) => {
|
API.getStatus().then((res) => {
|
||||||
|
@ -82,41 +123,7 @@ const HomePage = () => {
|
||||||
return <Stack spacing={2} >
|
return <Stack spacing={2} >
|
||||||
<IsLoggedIn />
|
<IsLoggedIn />
|
||||||
<HomeBackground />
|
<HomeBackground />
|
||||||
<style>
|
<TransparentHeader />
|
||||||
{`header {
|
|
||||||
background: rgba(${backColor},0.35) !important;
|
|
||||||
border-bottom-color: rgba(${backColor},0.4) !important;
|
|
||||||
color: ${textColor} !important;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .MuiChip-label {
|
|
||||||
color: ${textColor} !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .MuiButtonBase-root, header .MuiChip-colorDefault {
|
|
||||||
color: ${textColor} !important;
|
|
||||||
background: rgba(${backColor},0.5) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app {
|
|
||||||
transition: background 0.1s ease-in-out;
|
|
||||||
transition: transform 0.1s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background: rgba(${backColor},0.8) !important;
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.MuiAlert-standard {
|
|
||||||
background: rgba(${backColor},0.35) !important;
|
|
||||||
color: ${textColor} !important;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
</style>
|
|
||||||
<Stack style={{ zIndex: 2 }} spacing={1}>
|
<Stack style={{ zIndex: 2 }} spacing={1}>
|
||||||
{coStatus && !coStatus.database && (
|
{coStatus && !coStatus.database && (
|
||||||
<Alert severity="error">
|
<Alert severity="error">
|
||||||
|
|
0
client/src/pages/market/details.jsx
Normal file
0
client/src/pages/market/details.jsx
Normal file
257
client/src/pages/market/listing.jsx
Normal file
257
client/src/pages/market/listing.jsx
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
import { Box, Stack } from "@mui/material";
|
||||||
|
import { HomeBackground, TransparentHeader } from "../home";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import * as API from "../../api";
|
||||||
|
import { useTheme } from "@emotion/react";
|
||||||
|
import Grid2 from "@mui/material/Unstable_Grid2/Grid2";
|
||||||
|
import { useParams } from "react-router";
|
||||||
|
import Carousel from 'react-material-ui-carousel'
|
||||||
|
import { Paper, Button , Chip} from '@mui/material'
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import {Link as LinkMUI} from '@mui/material'
|
||||||
|
import DockerComposeImport from '../servapps/containers/docker-compose';
|
||||||
|
|
||||||
|
function Screenshots({ screenshots }) {
|
||||||
|
return (
|
||||||
|
<Carousel animation="slide" navButtonsAlwaysVisible={false} fullHeightHover="true">
|
||||||
|
{
|
||||||
|
screenshots.map((item, i) => <img key={i} src={item} width="100%" />)
|
||||||
|
}
|
||||||
|
</Carousel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Showcases({ showcase, isDark }) {
|
||||||
|
return (
|
||||||
|
<Carousel animation="slide" navButtonsAlwaysVisible={false} fullHeightHover="true">
|
||||||
|
{
|
||||||
|
showcase.map((item, i) => <ShowcasesItem isDark={isDark} key={i} item={item} />)
|
||||||
|
}
|
||||||
|
</Carousel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ShowcasesItem({ isDark, item }) {
|
||||||
|
return (
|
||||||
|
<Paper style={{
|
||||||
|
position: 'relative',
|
||||||
|
background: 'url(' + item.screenshots[0] + ')',
|
||||||
|
height: '31vh',
|
||||||
|
backgroundSize: 'auto 100%',
|
||||||
|
maxWidth: '120vh',
|
||||||
|
margin: 'auto',
|
||||||
|
}}>
|
||||||
|
<Stack direction="row" spacing={2} style={{ height: '100%', overflow: 'hidden' }} justifyContent="flex-end">
|
||||||
|
{/* <img src={item.screenshots[0]} style={{ height: '100%' }} /> */}
|
||||||
|
<Stack direction="column" spacing={2} style={{ height: '100%' }} sx={{
|
||||||
|
backgroundColor: isDark ? '#1A2027' : '#fff',
|
||||||
|
padding: '20px 100px',
|
||||||
|
width: '50%',
|
||||||
|
filter: 'drop-shadow(-20px 0px 20px rgba(0, 0, 0, 1))',
|
||||||
|
|
||||||
|
'@media (max-width: 1100px)': {
|
||||||
|
width: '70%',
|
||||||
|
padding: '20px 40px',
|
||||||
|
},
|
||||||
|
|
||||||
|
'@media (max-width: 600px)': {
|
||||||
|
width: '80%',
|
||||||
|
padding: '20px 20px',
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<Stack direction="row" spacing={2}>
|
||||||
|
<img src={item.icon} style={{ width: '36px', height: '36px' }} />
|
||||||
|
<h2>{item.name}</h2>
|
||||||
|
</Stack>
|
||||||
|
<p dangerouslySetInnerHTML={{ __html: item.longDescription }} style={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}></p>
|
||||||
|
<Stack direction="row" spacing={2} justifyContent="flex-start">
|
||||||
|
<Button className="CheckButton" color="primary" variant="contained">
|
||||||
|
Install
|
||||||
|
</Button>
|
||||||
|
<Link to={"/ui/market-listing/" + item.name} style={{
|
||||||
|
textDecoration: 'none',
|
||||||
|
}}>
|
||||||
|
<Button className="CheckButton" color="primary" variant="outlined">
|
||||||
|
View
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const appCardStyle = (theme) => ({
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
|
||||||
|
...theme.typography.body2,
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
})
|
||||||
|
|
||||||
|
const gridAnim = {
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
opacity: 1,
|
||||||
|
transform: 'translateY(0px)',
|
||||||
|
'&.MuiGrid2-item--hidden': {
|
||||||
|
opacity: 0,
|
||||||
|
transform: 'translateY(-20px)',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const MarketPage = () => {
|
||||||
|
const [apps, setApps] = useState([]);
|
||||||
|
const [showcase, setShowcase] = useState([]);
|
||||||
|
const theme = useTheme();
|
||||||
|
const isDark = theme.palette.mode === 'dark';
|
||||||
|
const { appName } = useParams();
|
||||||
|
|
||||||
|
const backgroundStyle = isDark ? {
|
||||||
|
backgroundColor: 'rgb(0,0,0)',
|
||||||
|
// borderTop: '1px solid #595959'
|
||||||
|
} : {
|
||||||
|
backgroundColor: 'rgb(255,255,255)',
|
||||||
|
// borderTop: '1px solid rgb(220,220,220)'
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(API.market)
|
||||||
|
API.market.list().then((res) => {
|
||||||
|
setApps(res.data.all);
|
||||||
|
setShowcase(res.data.showcase);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let openedApp = null;
|
||||||
|
if (appName && Object.keys(apps).length > 0) {
|
||||||
|
openedApp = apps[Object.keys(apps)[0]].find((app) => app.name === appName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<HomeBackground />
|
||||||
|
<TransparentHeader />
|
||||||
|
|
||||||
|
{openedApp && <Box style={{
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
zIndex: 1300,
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||||
|
}}>
|
||||||
|
<Link to="/ui/market-listing" as={Box}
|
||||||
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
}}></Link>
|
||||||
|
|
||||||
|
<Stack direction="row" spacing={2} style={{ height: '100%', overflow: 'hidden' }} justifyContent="flex-end">
|
||||||
|
<Stack direction="column" spacing={3} style={{ height: '100%' }} sx={{
|
||||||
|
backgroundColor: isDark ? '#1A2027' : '#fff',
|
||||||
|
padding: '80px 80px',
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: '800px',
|
||||||
|
filter: 'drop-shadow(-20px 0px 20px rgba(0, 0, 0, 1))',
|
||||||
|
|
||||||
|
'@media (max-width: 700px)': {
|
||||||
|
padding: '60px 40px',
|
||||||
|
},
|
||||||
|
|
||||||
|
'@media (max-width: 500px)': {
|
||||||
|
padding: '40px 20px',
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
|
||||||
|
<Link to="/ui/market-listing" style={{
|
||||||
|
textDecoration: 'none',
|
||||||
|
}}>
|
||||||
|
<Button className="CheckButton" color="primary" variant="outlined">
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Screenshots screenshots={openedApp.screenshots} />
|
||||||
|
|
||||||
|
<Stack direction="row" spacing={2}>
|
||||||
|
<img src={openedApp.icon} style={{ width: '36px', height: '36px' }} />
|
||||||
|
<h2>{openedApp.name}</h2>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{openedApp.tags.slice(0, 8).map((tag) => <Chip label={tag} />)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div><strong>repository:</strong> <LinkMUI href={openedApp.repository}>{openedApp.repository}</LinkMUI></div>
|
||||||
|
<div><strong>image:</strong> <LinkMUI href={openedApp.image}>{openedApp.image}</LinkMUI></div>
|
||||||
|
<div><strong>compose:</strong> <LinkMUI href={openedApp.compose}>{openedApp.compose}</LinkMUI></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: openedApp.longDescription }} style={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}></div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<DockerComposeImport installer defaultName={openedApp.name} dockerComposeInit={openedApp.compose} />
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Box>}
|
||||||
|
|
||||||
|
<Stack style={{ position: 'relative' }} spacing={1}>
|
||||||
|
<Stack style={{ height: '35vh' }} spacing={1}>
|
||||||
|
<Showcases showcase={showcase} isDark={isDark}/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack spacing={1} style={{
|
||||||
|
...backgroundStyle,
|
||||||
|
marginLeft: "-24px",
|
||||||
|
marginRight: "-24px",
|
||||||
|
marginBottom: "-24px",
|
||||||
|
minHeight: 'calc(65vh - 80px)',
|
||||||
|
padding: '24px',
|
||||||
|
}}>
|
||||||
|
<Grid2 container spacing={{ xs: 1, sm: 1, md: 2 }}>
|
||||||
|
{apps && Object.keys(apps).length > 0 && apps[Object.keys(apps)[0]].map((app) => {
|
||||||
|
return <Grid2 style={{
|
||||||
|
...gridAnim,
|
||||||
|
cursor: 'pointer',
|
||||||
|
}} xs={12} sm={12} md={6} lg={4} xl={3} key={app.name} item><Link to={"/ui/market-listing/" + app.name} style={{
|
||||||
|
textDecoration: 'none',
|
||||||
|
}}>
|
||||||
|
<div key={app.name} style={appCardStyle(theme)}>
|
||||||
|
<Stack spacing={3} direction={'row'} alignItems={'center'} style={{ padding: '0px 15px' }}>
|
||||||
|
<img src={app.icon} style={{ width: 64, height: 64 }} />
|
||||||
|
<Stack spacing={1}>
|
||||||
|
<div style={{ fontWeight: "bold" }}>{app.name}</div>
|
||||||
|
<div style={{
|
||||||
|
height: '40px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
}}
|
||||||
|
>{app.description}</div>
|
||||||
|
<Stack direction={'row'} spacing={1}>
|
||||||
|
<div style={{ fontStyle: "italic", opacity: 0.7 }}>{app.tags[0]}</div>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</Link>
|
||||||
|
</Grid2>
|
||||||
|
})}
|
||||||
|
</Grid2>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</>
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MarketPage;
|
|
@ -71,12 +71,20 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installer, defaultNam
|
||||||
const [step, setStep] = useState(0);
|
const [step, setStep] = useState(0);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [openModal, setOpenModal] = useState(false);
|
const [openModal, setOpenModal] = useState(false);
|
||||||
const [dockerCompose, setDockerCompose] = useState(dockerComposeInit || '');
|
const [dockerCompose, setDockerCompose] = useState('');
|
||||||
const [service, setService] = useState({});
|
const [service, setService] = useState({});
|
||||||
const [ymlError, setYmlError] = useState('');
|
const [ymlError, setYmlError] = useState('');
|
||||||
const [serviceName, setServiceName] = useState(defaultName || 'my-service');
|
const [serviceName, setServiceName] = useState(defaultName || 'my-service');
|
||||||
const [hostnames, setHostnames] = useState([]);
|
const [hostnames, setHostnames] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const text = fetch(dockerComposeInit)
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((text) => {
|
||||||
|
setDockerCompose(text);
|
||||||
|
});
|
||||||
|
}, [dockerComposeInit]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(!openModal) {
|
if(!openModal) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { Box, Button, Checkbox, CircularProgress, Input, Stack, TextField, Typography, useMediaQuery } from '@mui/material';
|
import { Box, Button, Checkbox, CircularProgress, Input, Stack, TextField, Typography, useMediaQuery } from '@mui/material';
|
||||||
import * as API from '../../../api';
|
import * as API from '../../../api';
|
||||||
import { ReactTerminal } from "react-terminal";
|
|
||||||
import LogLine from '../../../components/logLine';
|
import LogLine from '../../../components/logLine';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import ContainerIndex from '../pages/servapps/containers';
|
||||||
import NewDockerService from '../pages/servapps/containers/newService';
|
import NewDockerService from '../pages/servapps/containers/newService';
|
||||||
import NewDockerServiceForm from '../pages/servapps/containers/newServiceForm';
|
import NewDockerServiceForm from '../pages/servapps/containers/newServiceForm';
|
||||||
import OpenIdList from '../pages/openid/openid-list';
|
import OpenIdList from '../pages/openid/openid-list';
|
||||||
|
import MarketPage from '../pages/market/listing';
|
||||||
|
|
||||||
|
|
||||||
// render - dashboard
|
// render - dashboard
|
||||||
|
@ -85,6 +86,14 @@ const MainRoutes = {
|
||||||
{
|
{
|
||||||
path: '/ui/openid-manage',
|
path: '/ui/openid-manage',
|
||||||
element: <OpenIdList />,
|
element: <OpenIdList />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/ui/market-listing/',
|
||||||
|
element: <MarketPage />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/ui/market-listing/:appName',
|
||||||
|
element: <MarketPage />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
179
package-lock.json
generated
179
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "cosmos-server",
|
"name": "cosmos-server",
|
||||||
"version": "0.6.0-unstable",
|
"version": "0.7.0-unstable",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cosmos-server",
|
"name": "cosmos-server",
|
||||||
"version": "0.6.0-unstable",
|
"version": "0.7.0-unstable",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/colors": "^6.0.0",
|
"@ant-design/colors": "^6.0.0",
|
||||||
"@ant-design/icons": "^4.7.0",
|
"@ant-design/icons": "^4.7.0",
|
||||||
|
@ -38,13 +38,13 @@
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-draggable": "^4.4.5",
|
"react-draggable": "^4.4.5",
|
||||||
"react-element-to-jsx-string": "^15.0.0",
|
"react-element-to-jsx-string": "^15.0.0",
|
||||||
|
"react-material-ui-carousel": "^3.4.2",
|
||||||
"react-number-format": "^4.9.4",
|
"react-number-format": "^4.9.4",
|
||||||
"react-perfect-scrollbar": "^1.5.8",
|
"react-perfect-scrollbar": "^1.5.8",
|
||||||
"react-redux": "^8.0.4",
|
"react-redux": "^8.0.4",
|
||||||
"react-router": "^6.4.1",
|
"react-router": "^6.4.1",
|
||||||
"react-router-dom": "^6.4.1",
|
"react-router-dom": "^6.4.1",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
"react-terminal": "^1.3.1",
|
|
||||||
"react-window": "^1.8.7",
|
"react-window": "^1.8.7",
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
"simplebar": "^5.3.8",
|
"simplebar": "^5.3.8",
|
||||||
|
@ -3045,6 +3045,31 @@
|
||||||
"url": "https://opencollective.com/mui"
|
"url": "https://opencollective.com/mui"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/icons-material": {
|
||||||
|
"version": "5.11.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.11.16.tgz",
|
||||||
|
"integrity": "sha512-oKkx9z9Kwg40NtcIajF9uOXhxiyTZrrm9nmIJ4UjkU2IdHpd4QVLbCc/5hZN/y0C6qzi2Zlxyr9TGddQx2vx2A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.21.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@mui/material": "^5.0.0",
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mui/lab": {
|
"node_modules/@mui/lab": {
|
||||||
"version": "5.0.0-alpha.132",
|
"version": "5.0.0-alpha.132",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.132.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.132.tgz",
|
||||||
|
@ -6158,6 +6183,19 @@
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||||
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/framesync": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/framesync/-/framesync-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/framesync/node_modules/tslib": {
|
||||||
|
"version": "2.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
|
||||||
|
"integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
|
||||||
|
},
|
||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
@ -8115,6 +8153,22 @@
|
||||||
"node": ">=10.13.0"
|
"node": ">=10.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/popmotion": {
|
||||||
|
"version": "9.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/popmotion/-/popmotion-9.3.6.tgz",
|
||||||
|
"integrity": "sha512-ZTbXiu6zIggXzIliMi8LGxXBF5ST+wkpXGEjeTUDUOCdSQ356hij/xjeUdv0F8zCQNeqB1+PR5/BB+gC+QLAPw==",
|
||||||
|
"dependencies": {
|
||||||
|
"framesync": "5.3.0",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"style-value-types": "4.1.4",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/popmotion/node_modules/tslib": {
|
||||||
|
"version": "2.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
|
||||||
|
"integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.24",
|
"version": "8.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
|
||||||
|
@ -8412,6 +8466,67 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-material-ui-carousel": {
|
||||||
|
"version": "3.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-material-ui-carousel/-/react-material-ui-carousel-3.4.2.tgz",
|
||||||
|
"integrity": "sha512-jUbC5aBWqbbbUOOdUe3zTVf4kMiZFwKJqwhxzHgBfklaXQbSopis4iWAHvEOLcZtSIJk4JAGxKE0CmxDoxvUuw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.7.1",
|
||||||
|
"@emotion/styled": "^11.6.0",
|
||||||
|
"@mui/icons-material": "^5.4.1",
|
||||||
|
"@mui/material": "^5.4.1",
|
||||||
|
"@mui/system": "^5.4.1",
|
||||||
|
"framer-motion": "^4.1.17"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.4.1",
|
||||||
|
"@emotion/styled": "^11.3.0",
|
||||||
|
"@mui/icons-material": "^5.0.0",
|
||||||
|
"@mui/material": "^5.0.0",
|
||||||
|
"@mui/system": "^5.0.0",
|
||||||
|
"react": "^17.0.1 || ^18.0.0",
|
||||||
|
"react-dom": "^17.0.2 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-material-ui-carousel/node_modules/@emotion/is-prop-valid": {
|
||||||
|
"version": "0.8.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||||
|
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/memoize": "0.7.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-material-ui-carousel/node_modules/@emotion/memoize": {
|
||||||
|
"version": "0.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||||
|
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/react-material-ui-carousel/node_modules/framer-motion": {
|
||||||
|
"version": "4.1.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-4.1.17.tgz",
|
||||||
|
"integrity": "sha512-thx1wvKzblzbs0XaK2X0G1JuwIdARcoNOW7VVwjO8BUltzXPyONGAElLu6CiCScsOQRI7FIk/45YTFtJw5Yozw==",
|
||||||
|
"dependencies": {
|
||||||
|
"framesync": "5.3.0",
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"popmotion": "9.3.6",
|
||||||
|
"style-value-types": "4.1.4",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "^0.8.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8 || ^17.0.0",
|
||||||
|
"react-dom": ">=16.8 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-material-ui-carousel/node_modules/tslib": {
|
||||||
|
"version": "2.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
|
||||||
|
"integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
|
||||||
|
},
|
||||||
"node_modules/react-number-format": {
|
"node_modules/react-number-format": {
|
||||||
"version": "4.9.4",
|
"version": "4.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-4.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-4.9.4.tgz",
|
||||||
|
@ -8532,50 +8647,6 @@
|
||||||
"react": ">= 0.14.0"
|
"react": ">= 0.14.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-terminal": {
|
|
||||||
"version": "1.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-terminal/-/react-terminal-1.3.1.tgz",
|
|
||||||
"integrity": "sha512-lbkrih1be0nlJptZR7uwV6YF8PMuxKJOKhGN+GVuFKp9dY/qpSYF76KMGZZJnWbbxAt5Bkf+aUt4iyy5F8NBdQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"prop-types": "^15.7.2",
|
|
||||||
"react-device-detect": "2.1.2"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"prop-types": "^15.8.1",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-terminal/node_modules/react-device-detect": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-device-detect/-/react-device-detect-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-N42xttwez3ECgu4KpOL2ICesdfoz8NCBfmc1rH9FRYSjH7NmMyANPSrQ3EvAtJyj/6TzJNhrANSO38iXjCB2Ug==",
|
|
||||||
"dependencies": {
|
|
||||||
"ua-parser-js": "^0.7.30"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">= 0.14.0 < 18.0.0",
|
|
||||||
"react-dom": ">= 0.14.0 < 18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-terminal/node_modules/ua-parser-js": {
|
|
||||||
"version": "0.7.35",
|
|
||||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz",
|
|
||||||
"integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/ua-parser-js"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "paypal",
|
|
||||||
"url": "https://paypal.me/faisalman"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-transition-group": {
|
"node_modules/react-transition-group": {
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
|
@ -9268,6 +9339,20 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/style-value-types": {
|
||||||
|
"version": "4.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-4.1.4.tgz",
|
||||||
|
"integrity": "sha512-LCJL6tB+vPSUoxgUBt9juXIlNJHtBMy8jkXzUJSBzeHWdBu6lhzHqCvLVkXFGsFIlNa2ln1sQHya/gzaFmB2Lg==",
|
||||||
|
"dependencies": {
|
||||||
|
"hey-listen": "^1.0.8",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/style-value-types/node_modules/tslib": {
|
||||||
|
"version": "2.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
|
||||||
|
"integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
|
||||||
|
},
|
||||||
"node_modules/stylis": {
|
"node_modules/stylis": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cosmos-server",
|
"name": "cosmos-server",
|
||||||
"version": "0.7.0-unstable",
|
"version": "0.7.0-unstable2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "test-server.js",
|
"main": "test-server.js",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
@ -38,13 +38,13 @@
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-draggable": "^4.4.5",
|
"react-draggable": "^4.4.5",
|
||||||
"react-element-to-jsx-string": "^15.0.0",
|
"react-element-to-jsx-string": "^15.0.0",
|
||||||
|
"react-material-ui-carousel": "^3.4.2",
|
||||||
"react-number-format": "^4.9.4",
|
"react-number-format": "^4.9.4",
|
||||||
"react-perfect-scrollbar": "^1.5.8",
|
"react-perfect-scrollbar": "^1.5.8",
|
||||||
"react-redux": "^8.0.4",
|
"react-redux": "^8.0.4",
|
||||||
"react-router": "^6.4.1",
|
"react-router": "^6.4.1",
|
||||||
"react-router-dom": "^6.4.1",
|
"react-router-dom": "^6.4.1",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
"react-terminal": "^1.3.1",
|
|
||||||
"react-window": "^1.8.7",
|
"react-window": "^1.8.7",
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
"simplebar": "^5.3.8",
|
"simplebar": "^5.3.8",
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/azukaar/cosmos-server/src/proxy"
|
"github.com/azukaar/cosmos-server/src/proxy"
|
||||||
"github.com/azukaar/cosmos-server/src/docker"
|
"github.com/azukaar/cosmos-server/src/docker"
|
||||||
"github.com/azukaar/cosmos-server/src/authorizationserver"
|
"github.com/azukaar/cosmos-server/src/authorizationserver"
|
||||||
|
"github.com/azukaar/cosmos-server/src/market"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -277,9 +278,9 @@ func StartServer() {
|
||||||
srapi.HandleFunc("/api/servapps/{containerId}/networks", docker.NetworkContainerRoutes)
|
srapi.HandleFunc("/api/servapps/{containerId}/networks", docker.NetworkContainerRoutes)
|
||||||
srapi.HandleFunc("/api/servapps/{containerId}/check-update", docker.CanUpdateImageRoute)
|
srapi.HandleFunc("/api/servapps/{containerId}/check-update", docker.CanUpdateImageRoute)
|
||||||
srapi.HandleFunc("/api/servapps", docker.ContainersRoute)
|
srapi.HandleFunc("/api/servapps", docker.ContainersRoute)
|
||||||
|
|
||||||
srapi.HandleFunc("/api/docker-service", docker.CreateServiceRoute)
|
srapi.HandleFunc("/api/docker-service", docker.CreateServiceRoute)
|
||||||
|
|
||||||
|
srapi.HandleFunc("/api/markets", market.MarketGet)
|
||||||
|
|
||||||
|
|
||||||
if(!config.HTTPConfig.AcceptAllInsecureHostname) {
|
if(!config.HTTPConfig.AcceptAllInsecureHostname) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/azukaar/cosmos-server/src/docker"
|
"github.com/azukaar/cosmos-server/src/docker"
|
||||||
"github.com/azukaar/cosmos-server/src/utils"
|
"github.com/azukaar/cosmos-server/src/utils"
|
||||||
"github.com/azukaar/cosmos-server/src/authorizationserver"
|
"github.com/azukaar/cosmos-server/src/authorizationserver"
|
||||||
|
"github.com/azukaar/cosmos-server/src/market"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -37,6 +38,8 @@ func main() {
|
||||||
utils.Log("Docker API version: " + version.APIVersion)
|
utils.Log("Docker API version: " + version.APIVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
market.Init()
|
||||||
|
|
||||||
authorizationserver.Init()
|
authorizationserver.Init()
|
||||||
|
|
||||||
StartServer()
|
StartServer()
|
||||||
|
|
56
src/market/index.go
Normal file
56
src/market/index.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package market
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/azukaar/cosmos-server/src/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type marketGetResult struct {
|
||||||
|
Showcase []appDefinition `json:"showcase"`
|
||||||
|
All map[string]interface{} `json:"all"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarketGet(w http.ResponseWriter, req *http.Request) {
|
||||||
|
if utils.AdminOnly(w, req) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(req.Method == "GET") {
|
||||||
|
err := updateCache(w, req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the first 10 results of each market
|
||||||
|
marketGetResult := marketGetResult{
|
||||||
|
All: make(map[string]interface{}),
|
||||||
|
Showcase: []appDefinition{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, market := range currentMarketcache {
|
||||||
|
results := []appDefinition{}
|
||||||
|
for _, app := range market.Results.All {
|
||||||
|
// if i < 10 {
|
||||||
|
results = append(results, app)
|
||||||
|
// } else {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
marketGetResult.All[market.Name] = results
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(currentMarketcache) > 0 {
|
||||||
|
marketGetResult.Showcase = currentMarketcache[0].Results.Showcase
|
||||||
|
}
|
||||||
|
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"status": "OK",
|
||||||
|
"data": marketGetResult,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
utils.Error("MarketGet: Method not allowed" + req.Method, nil)
|
||||||
|
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
20
src/market/init.go
Normal file
20
src/market/init.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package market
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/azukaar/cosmos-server/src/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
config := utils.GetMainConfig()
|
||||||
|
currentMarketcache = []marketCacheObject{}
|
||||||
|
|
||||||
|
for _, marketDef := range config.MarketConfig.Sources {
|
||||||
|
market := marketCacheObject{
|
||||||
|
Url: marketDef.Url,
|
||||||
|
Name: marketDef.Name,
|
||||||
|
}
|
||||||
|
currentMarketcache = append(currentMarketcache, market)
|
||||||
|
|
||||||
|
utils.Log("MarketInit: Added market " + market.Name)
|
||||||
|
}
|
||||||
|
}
|
74
src/market/update.go
Normal file
74
src/market/update.go
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package market
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/azukaar/cosmos-server/src/utils"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type appDefinition struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
LongDescription string `json:"longDescription"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Repository string `json:"repository"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
Screenshots []string `json:"screenshots"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
Compose string `json:"compose"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type marketDefinition struct {
|
||||||
|
Showcase []appDefinition `json:"showcase"`
|
||||||
|
All []appDefinition `json:"all"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type marketCacheObject struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
LastUpdate time.Time `json:"lastUpdate"`
|
||||||
|
Results marketDefinition `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentMarketcache []marketCacheObject
|
||||||
|
|
||||||
|
func updateCache(w http.ResponseWriter, req *http.Request) error {
|
||||||
|
for index, cachedMarket := range currentMarketcache {
|
||||||
|
if cachedMarket.LastUpdate.Add(time.Hour * 12).Before(time.Now()) {
|
||||||
|
utils.Log("MarketUpdate: Updating market " + cachedMarket.Name)
|
||||||
|
|
||||||
|
// fetch market.url
|
||||||
|
resp, err := http.Get(cachedMarket.Url)
|
||||||
|
if err != nil {
|
||||||
|
utils.Error("MarketUpdate: Error while fetching market" + cachedMarket.Url, err)
|
||||||
|
utils.HTTPError(w, "Market Get Error " + cachedMarket.Url, http.StatusInternalServerError, "MK001")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// parse body
|
||||||
|
var result marketDefinition
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
utils.Error("MarketUpdate: Error while parsing market" + cachedMarket.Url, err)
|
||||||
|
utils.HTTPError(w, "Market Get Error " + cachedMarket.Url, http.StatusInternalServerError, "MK003")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedMarket.Results = result
|
||||||
|
cachedMarket.LastUpdate = time.Now()
|
||||||
|
|
||||||
|
utils.Log("MarketUpdate: Updated market " + result.Source + " with " + string(len(result.All)) + " results")
|
||||||
|
|
||||||
|
// save to cache
|
||||||
|
currentMarketcache[index] = cachedMarket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -86,6 +86,7 @@ type Config struct {
|
||||||
RequireMFA bool
|
RequireMFA bool
|
||||||
AutoUpdate bool
|
AutoUpdate bool
|
||||||
OpenIDClients []OpenIDClient
|
OpenIDClients []OpenIDClient
|
||||||
|
MarketConfig MarketConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPConfig struct {
|
type HTTPConfig struct {
|
||||||
|
@ -167,3 +168,12 @@ type OpenIDClient struct {
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
Redirect string `json:"redirect"`
|
Redirect string `json:"redirect"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MarketConfig struct {
|
||||||
|
Sources []MarketSource
|
||||||
|
}
|
||||||
|
|
||||||
|
type MarketSource struct {
|
||||||
|
Name string
|
||||||
|
Url string
|
||||||
|
}
|
|
@ -78,6 +78,14 @@ var DefaultConfig = Config{
|
||||||
Routes: []ProxyRouteConfig{},
|
Routes: []ProxyRouteConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MarketConfig: MarketConfig{
|
||||||
|
Sources: []MarketSource{
|
||||||
|
MarketSource{
|
||||||
|
Url: "https://cosmos-cloud.io/repository",
|
||||||
|
Name: "Cosmos Cloud",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func FileExists(path string) bool {
|
func FileExists(path string) bool {
|
||||||
|
@ -166,6 +174,16 @@ func ReadConfigFromFile() Config {
|
||||||
Fatal("Reading Config File: " + errString, err)
|
Fatal("Reading Config File: " + errString, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if outdated
|
||||||
|
if len(config.MarketConfig.Sources) == 0 {
|
||||||
|
config.MarketConfig.Sources = []MarketSource{
|
||||||
|
MarketSource{
|
||||||
|
Url: "https://cosmos-cloud.io/repository",
|
||||||
|
Name: "Cosmos Cloud",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +261,6 @@ func GetConfigFileName() string {
|
||||||
return configFile
|
return configFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func CreateDefaultConfigFileIfNecessary() bool {
|
func CreateDefaultConfigFileIfNecessary() bool {
|
||||||
configFile := GetConfigFileName()
|
configFile := GetConfigFileName()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue