Skip to content

Commit 27698fb

Browse files
author
Timo Stark
committed
Bugfix #4
- Add a feature to be able to create configuration files outside of /etc/nginx. - Read all configuration files included in NGINX configuration files.
1 parent 0ac3d17 commit 27698fb

File tree

9 files changed

+115
-102
lines changed

9 files changed

+115
-102
lines changed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ LABEL org.opencontainers.image.title="NGINX Development Center" \
2525

2626
COPY metadata.json .
2727
COPY logo.svg .
28-
COPY --from=client-builder /ui/build ui
28+
COPY --from=client-builder /ui/build uide

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
IMAGE?=nginx/nginx-docker-extension
1+
IMAGE?=nginx/docker-extension
22
TAG?=latest
33

44
BUILDER=buildx-multi-arch

ui/src/App.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ export function App() {
77
return (
88
<>
99
<Typography variant="h2">NGINX Development Center</Typography>
10-
<Typography variant="subtitle2">Alpha-Release alpha.0.1</Typography>
1110
<NginxInstance />
1211
</>
1312
);

ui/src/configuration/ConfigurationParser.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,13 @@ export class ConfigurationParser {
7070
}
7171

7272
// Comment line
73-
// find Includes to get the filename
7473
if (line.substring(0, 1) === '#') {
74+
// find Includes to get the filename.
7575
if (line.match('# configuration file')) {
7676
//get configuration file name.
77-
configurationFile = line.match("[^\\/]*$") ? line.match("[^\\/]*$")![0] : ""
77+
console.log(line)
78+
// configurationFile = line.match("[^\\/]*$") ? line.match("[^\\/]*$")![0] : ""
79+
configurationFile = line.match("\\/.*$") ? line.match("\\/.*$")![0] : ""
7880
configurationFile = configurationFile.replace(":", "")
7981
return
8082
}
@@ -140,6 +142,7 @@ export class ConfigurationParser {
140142
return
141143
}
142144
})
145+
console.log(configuration);
143146
return configuration
144147
}
145148
}

ui/src/configurationEditor/ConfigurationEditor.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ interface ConfigurationEditorProps {
2626

2727
}
2828

29-
//Reafactor! Dependency violation!
29+
//Refactor! Dependency violation!
3030
let instanceService: InstancesService = new InstancesService()
3131

3232
export function ConfigurationEditor(props: ConfigurationEditorProps) {
@@ -48,7 +48,7 @@ export function ConfigurationEditor(props: ConfigurationEditorProps) {
4848
instanceService.getConfigurations(props.nginxInstance.id).then((data: any) => {
4949
let filesArray: Array<string> = data
5050
filesArray = filesArray.filter(item => item != "").map((item: string) => {
51-
let match = item.match('(?:\\/etc\\/nginx\\/.*)');
51+
let match = item.match('\\/.*$');
5252
if (match != undefined) {
5353
return match[0].replace(`:`, ``)
5454
} else {

ui/src/configurationEditor/NewConfigurationFile.tsx

+4-6
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ export function NewConfigurationFile(props: NewConfigurationFileProps) {
2323
props.instanceService.displayErrorMessage(`Filename can not be empty!`);
2424
return;
2525
}
26-
27-
props.instanceService.sendConfigurationToFile(`/etc/nginx/conf.d/${fileName}`, props.nginxInstance.id, content).then((data: any) => {
26+
// Make the path configurable
27+
props.instanceService.sendConfigurationToFile(fileName, props.nginxInstance.id, content).then((data: any) => {
2828
props.instanceService.reloadNGINX(props.nginxInstance.id).then((data: any) => {
2929
props.instanceService.displaySuccessMessage("Configuration successfully updated!");
3030
}).catch((reason: any) => {
@@ -37,13 +37,11 @@ export function NewConfigurationFile(props: NewConfigurationFileProps) {
3737
<Grid container direction={"column"} sx={{margin: 0, padding: 5}}>
3838
<Typography variant={"h2"} sx={{marginBottom: 2}}>Add a new configuration File</Typography>
3939
<Alert severity="info" sx={{marginBottom: 4}}>
40-
Make sure the configuration filename ends with a .conf file extension!
40+
Make sure the configuration filename ends with a .conf file extension and is a absolute path.
41+
Example: /etc/nginx/conf.d/example.conf
4142
</Alert>
4243
<TextField
4344
label="Configuration File Name"
44-
InputProps={{
45-
startAdornment: <InputAdornment position="start" sx={{marginRight: "1.5rem"}}>/etc/nginx/conf.d/</InputAdornment>,
46-
}}
4745
onChange={(e: any) => {
4846
setFileName(e.target.value)
4947
}}

ui/src/instances/InstancesService.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,11 @@ export class InstancesService {
115115

116116
// Maybe this can be the default function to send docker exec commands?
117117
async sendConfigurationToFile(file: string, containerId: string, configurationB64: string): Promise<any> {
118-
const ret = await this.ddClient.docker.cli.exec(
118+
return await this.ddClient.docker.cli.exec(
119119
"exec",
120120
[containerId,
121121
"/bin/sh", "-c", `"echo ${configurationB64} |base64 -d > ${file}"`]);
122-
return ret
123-
}
122+
}
124123

125124
async reloadNGINX(containerId: string): Promise<any> {
126125
return await this.ddClient.docker.cli.exec(

ui/src/instances/NginxInstances.tsx

+95-84
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ import {
99
Tooltip,
1010
ThemeProvider,
1111
Typography,
12-
createTheme,
12+
createTheme, Button,
1313
} from "@mui/material";
14-
import styled from "@emotion/styled";
1514
import "./Instance.css";
1615

1716
import {
18-
ArrowBackIosNewOutlined,
19-
Store
17+
ArrowBackIosNewOutlined
2018
} from "@mui/icons-material";
2119

2220
import DnsIcon from '@mui/icons-material/Dns';
@@ -89,7 +87,6 @@ export function NginxInstance() {
8987
})
9088
}
9189

92-
9390
//Refactoring! Move this into a separate component
9491
const [tabValue, setTabValue] = useState('1');
9592
const handleTabChange = (event: React.SyntheticEvent, newValue: string) => {
@@ -125,90 +122,104 @@ export function NginxInstance() {
125122

126123
return (
127124
<Box sx={{m: 1}}>
128-
{!loading ? (
129-
!nginxInstance.id ? (
130-
<Box>
131-
<Typography variant="subtitle2">Active containers running NGINX</Typography>
132-
<Grid container> {
133-
instances.map((inst: any, key: number) => (
134-
//Refactoring Component Instance
135-
<Grid item sm={6} lg={4} key={key}>
136-
<Box className={"ngx-instance"} borderRadius={1} boxShadow={10} margin={2}
137-
padding={2}
138-
background-color={"red"}
139-
border={"1px solid rgba(255,255,255,.2)"}
140-
onClick={nginxInstanceOnClickHandler(inst.id, inst.name)}>
141-
<Typography variant="h5">{inst.name}</Typography>
142-
<Typography variant="subtitle2" display="inline">Container ID: </Typography>
143-
<Typography variant="body1" display="inline">{inst.id.substring(0, 12)}</Typography>
144-
<Box paddingTop={1}>
145-
<Typography variant="subtitle2" display="inline">Status: </Typography>
146-
<Typography variant="body1" display="inline">{inst.status.toLowerCase()}</Typography>
147-
</Box>
148-
<Box>
149-
<Typography variant="subtitle2" display="inline">NGINX Version: </Typography>
150-
<Typography variant="body1" display="inline">{inst.out.substring(15, inst.out.length)}</Typography>
151-
</Box>
125+
{!loading ? (
126+
!nginxInstance.id ? (
127+
instances == 0 ? (
128+
<Box>
129+
There are no active NGINX containers! Start a container to get started!
130+
{/* @ts-expect-error not typed yet! */}
131+
<Typography component={"pre"} variant="inline-code">docker run -d -P nginx:latest</Typography>
132+
</Box>) : (
133+
<Box>
134+
<Typography variant="subtitle2">Active containers running NGINX</Typography>
135+
<Grid container> {
136+
instances.map((inst: any, key: number) => (
137+
//Refactoring Component Instance
138+
<Grid item sm={6} lg={4} key={key}>
139+
<Box className={"ngx-instance"} borderRadius={1} boxShadow={10} margin={2}
140+
padding={2}
141+
background-color={"red"}
142+
border={"1px solid rgba(255,255,255,.2)"}
143+
onClick={nginxInstanceOnClickHandler(inst.id, inst.name)}>
144+
<Typography variant="h5">{inst.name}</Typography>
145+
<Typography variant="subtitle2" display="inline">Container ID: </Typography>
146+
<Typography variant="body1"
147+
display="inline">{inst.id.substring(0, 12)}</Typography>
148+
<Box paddingTop={1}>
149+
<Typography variant="subtitle2" display="inline">Status: </Typography>
150+
<Typography variant="body1"
151+
display="inline">{inst.status.toLowerCase()}</Typography>
152+
</Box>
153+
<Box>
154+
<Typography variant="subtitle2" display="inline">NGINX
155+
Version: </Typography>
156+
<Typography variant="body1"
157+
display="inline">{inst.out.substring(15, inst.out.length)}</Typography>
158+
</Box>
159+
<Box>
160+
<Typography variant="subtitle2" display="inline">Open Ports
161+
(Host:Container): </Typography>
162+
{inst.ports.map((port: any, key: number) => containerNetwork(port, key))}
163+
</Box>
164+
{inst.mounts.length > 0 ? (
152165
<Box>
153-
<Typography variant="subtitle2" display="inline">Open Ports (Host:Container): </Typography>
154-
{inst.ports.map((port: any, key: number) => containerNetwork(port, key))}
166+
<Typography variant="subtitle2" display="inline">Number of Mounted
167+
Volumes: </Typography>
168+
<Typography variant="body1"
169+
display="inline">{inst.mounts.length}</Typography>
155170
</Box>
156-
{inst.mounts.length > 0 ? (
157-
<Box>
158-
<Typography variant="subtitle2" display="inline">Number of Mounted Volumes: </Typography>
159-
<Typography variant="body1" display="inline">{inst.mounts.length}</Typography>
160-
</Box>
161-
) : ("")}
162-
</Box>
163-
</Grid>
164-
))}
165-
</Grid>
166-
</Box>
167-
) : (
168-
<Box>
169-
<Grid className={errorClasses.bannerBackground}>
170-
<Typography paddingTop={2}>
171-
<Tooltip title="Back to Instances Overview">
172-
<IconButton className={"ngx-back-button"} onClick={() => {
173-
setContainerId(undefined)
174-
setNginxInstance({id: "", name: "", mounts: []})
175-
}} disabled={errorClasses.backToDashboardDisabled}>
176-
<ArrowBackIosNewOutlined/>
177-
</IconButton>
178-
</Tooltip>
179-
<Typography variant="h5" display="inline">{nginxInstance.name}</Typography>
180-
</Typography>
181-
<Typography variant={"inherit"} paddingLeft={5} paddingBottom={2}>
182-
Container ID: {nginxInstance.id.substring(0, 12)}
183-
</Typography>
184-
{renderErrorMessageIfAny()}
171+
) : ("")}
172+
</Box>
173+
</Grid>
174+
))}
185175
</Grid>
186-
187-
<TabContext value={tabValue}>
188-
<Box>
189-
<Tabs value={tabValue} onChange={handleTabChange} aria-label="icon label tabs example">
190-
<Tab icon={<DnsIcon/>} label="Servers" value={"1"}/>
191-
<Tab icon={<BorderColorIcon/>} label="Configuration Editor" value={"2"}/>
192-
{/*<Tab icon={<Store />} label="Templates Store" value={"3"}/>*/}
193-
{/*<Tab icon={<FileDownload/>} label="Export Configuration" value={"4"}/>*/}
194-
</Tabs>
195-
</Box>
196-
<TabPanel value={"1"}>
197-
<ConfigurationUi containerId={nginxInstance.id} nginxInstance={nginxInstance}/>
198-
</TabPanel>
199-
<TabPanel value={"2"}>
200-
<ConfigurationEditor nginxInstance={nginxInstance}/>
201-
</TabPanel>
202-
<TabPanel value={"3"}>
203-
<TemplateStore />
204-
</TabPanel>
205-
<TabPanel value={"4"}>
206-
<>Exports</>
207-
</TabPanel>
208-
</TabContext>
209176
</Box>
210177
)
211-
) : (<Typography variant='h3'>Loading...</Typography>)}
178+
) : (
179+
<Box>
180+
<Grid className={errorClasses.bannerBackground}>
181+
<Typography paddingTop={2}>
182+
<Tooltip title="Back to Instances Overview">
183+
<IconButton className={"ngx-back-button"} onClick={() => {
184+
setContainerId(undefined)
185+
setNginxInstance({id: "", name: "", mounts: []})
186+
}} disabled={errorClasses.backToDashboardDisabled}>
187+
<ArrowBackIosNewOutlined/>
188+
</IconButton>
189+
</Tooltip>
190+
<Typography variant="h5" display="inline">{nginxInstance.name}</Typography>
191+
</Typography>
192+
<Typography variant={"inherit"} paddingLeft={5} paddingBottom={2}>
193+
Container ID: {nginxInstance.id.substring(0, 12)}
194+
</Typography>
195+
{renderErrorMessageIfAny()}
196+
</Grid>
197+
198+
<TabContext value={tabValue}>
199+
<Box>
200+
<Tabs value={tabValue} onChange={handleTabChange} aria-label="icon label tabs example">
201+
<Tab icon={<DnsIcon/>} label="Servers" value={"1"}/>
202+
<Tab icon={<BorderColorIcon/>} label="Configuration Editor" value={"2"}/>
203+
{/*<Tab icon={<Store />} label="Templates Store" value={"3"}/>*/}
204+
{/*<Tab icon={<FileDownload/>} label="Export Configuration" value={"4"}/>*/}
205+
</Tabs>
206+
</Box>
207+
<TabPanel value={"1"}>
208+
<ConfigurationUi containerId={nginxInstance.id} nginxInstance={nginxInstance}/>
209+
</TabPanel>
210+
<TabPanel value={"2"}>
211+
<ConfigurationEditor nginxInstance={nginxInstance}/>
212+
</TabPanel>
213+
<TabPanel value={"3"}>
214+
<TemplateStore/>
215+
</TabPanel>
216+
<TabPanel value={"4"}>
217+
<>Exports</>
218+
</TabPanel>
219+
</TabContext>
220+
</Box>
221+
)
222+
) : (<Typography variant='h3'>Loading...</Typography>)}
212223
</Box>
213224
);
214225
}

ui/src/main.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import {DockerMuiThemeProvider} from "@docker/docker-mui-theme";
66
import {App} from './App';
77
import {ThemeProvider, useTheme} from "@mui/material";
88

9-
const CustomThemeProvider = ({ children }: any) => {
9+
10+
const CustomThemeProvider = ({children}: any) => {
1011
const theme = useTheme();
12+
13+
// @ts-ignore
1114
theme.components = {
1215
...theme.components,
1316
MuiChip: {
@@ -16,7 +19,7 @@ const CustomThemeProvider = ({ children }: any) => {
1619
textTransform: "inherit"
1720
},
1821
},
19-
},
22+
}
2023
};
2124
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
2225
};

0 commit comments

Comments
 (0)