Skip to content

Commit 2fbf4b4

Browse files
jaapmarcusAlecRust
andauthored
Add install script generator (hestiacp#3355)
* Add support for an install generator * Update colors * Add select box for languages * Tidy install generator UI a bit * Tidy * Use dialog to display install string * Add dependencies + Conflicts VSFTP + Proftpd and Mysql And Mysql8 can't go together - Sieve requires Dovecot - Dovecot requires Exim and so on * Update Teams page * Remove exta code * Refine install generator * Improve install modal * Simplify install generator code --------- Co-authored-by: Alec Rust <me@alecrust.com>
1 parent 3a254e1 commit 2fbf4b4

File tree

10 files changed

+648
-1
lines changed

10 files changed

+648
-1
lines changed

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export default defineConfig({
5454
function nav(): DefaultTheme.NavItem[] {
5555
return [
5656
{ text: "Features", link: "/features.md" },
57+
{ text: "Install", link: "/install.md" },
5758
{ text: "Documentation", link: "/docs/introduction/getting-started.md", activeMatch: "/docs/" },
5859
{ text: "Team", link: "/team.md" },
5960
{ text: "Demo", link: "https://demo.hestiacp.com:8083/" },
Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
<script lang="ts">
2+
import { InstallOptions } from "../../../_data/options";
3+
import { LanguagesOptions } from "../../../_data/languages";
4+
import { ref } from "vue";
5+
const slot = ref(null);
6+
7+
export default {
8+
props: {
9+
languages: {
10+
type: Array<LanguagesOptions>,
11+
required: true,
12+
selected: "en",
13+
},
14+
items: {
15+
type: Array<InstallOptions>,
16+
required: true,
17+
},
18+
},
19+
data() {
20+
return {
21+
pageloader: false,
22+
hestia_wget:
23+
"wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh",
24+
hestia_install: "sudo bash hst-install.sh",
25+
installStr: "",
26+
};
27+
},
28+
methods: {
29+
getOptionString(item: InstallOptions): string {
30+
if (item.textField && item.selected) {
31+
return item.text.length >= 2 ? `${item.param} '${item.text}'` : "";
32+
}
33+
34+
if (item.selectField) {
35+
return `${item.param} '${item.text}'`;
36+
}
37+
38+
return item.param.includes("force") && item.selected
39+
? item.param
40+
: `${item.param}${item.selected ? " yes" : " no"}`;
41+
},
42+
generateString() {
43+
const installStr = this.items.map(this.getOptionString).filter(Boolean);
44+
45+
this.installStr = `${this.hestia_install} ${installStr.join(" ")}`;
46+
(this.$refs.dialog as HTMLDialogElement).showModal();
47+
},
48+
closeDialog(e) {
49+
if (e.target === this.$refs.dialogClose || e.target === this.$refs.dialog) {
50+
(this.$refs.dialog as HTMLDialogElement).close();
51+
}
52+
},
53+
toggleOption(e) {
54+
if (e.target.checked) {
55+
let conflicts = e.target.getAttribute("conflicts");
56+
if (conflicts) {
57+
document.getElementById(conflicts).checked = false;
58+
}
59+
let depends = e.target.getAttribute("depends");
60+
if (depends) {
61+
document.getElementById(depends).checked = true;
62+
}
63+
}
64+
},
65+
copyToClipboard(text: string, button: HTMLButtonElement) {
66+
navigator.clipboard.writeText(text).then(
67+
() => {
68+
button.textContent = "Copied!";
69+
setTimeout(() => {
70+
button.textContent = "Copy";
71+
}, 1000);
72+
},
73+
(err) => {
74+
console.error("Could not copy to clipboard:", err);
75+
}
76+
);
77+
},
78+
},
79+
};
80+
</script>
81+
82+
<template>
83+
<div class="container">
84+
<div class="grid">
85+
<div class="form-group" v-for="item in items">
86+
<div class="form-check u-mb10">
87+
<input
88+
@change="toggleOption"
89+
type="checkbox"
90+
class="form-check-input"
91+
v-model="item.selected"
92+
:value="item.value"
93+
:id="item.id"
94+
:conflicts="item.conflicts"
95+
:depends="item.depends"
96+
/>
97+
<label :for="item.id">{{ item.id }}</label>
98+
</div>
99+
<template v-if="item.textField || item.selectField">
100+
<label class="form-label" :for="'input-' + item.id">{{ item.desc }}</label>
101+
</template>
102+
<template v-else>
103+
<p>{{ item.desc }}</p>
104+
</template>
105+
<div v-if="item.textField">
106+
<input type="text" class="form-control" v-model="item.text" :id="'input-' + item.id" />
107+
</div>
108+
<div v-if="item.selectField">
109+
<select class="form-select" v-model="item.text" :id="'input-' + item.id">
110+
<option v-for="language in languages" :value="language.value" :key="language.value">
111+
{{ language.text }}
112+
</option>
113+
</select>
114+
</div>
115+
</div>
116+
</div>
117+
<div class="u-text-center u-mb10">
118+
<button @click="generateString" class="form-submit" type="button">Submit</button>
119+
</div>
120+
<dialog ref="dialog" class="modal" @click="closeDialog">
121+
<button class="modal-close" @click="closeDialog" type="button" ref="dialogClose">
122+
Close
123+
</button>
124+
<div ref="dialogContent" class="modal-content">
125+
<h1 class="modal-heading">Installation instructions</h1>
126+
<p class="u-mb10">
127+
Log in to your server as root, either directly or via SSH:
128+
<code>ssh root@your.server</code> and download the installation script:
129+
</p>
130+
<div class="u-pos-relative">
131+
<input
132+
type="text"
133+
class="form-control u-monospace u-mb10"
134+
v-model="hestia_wget"
135+
readonly
136+
/>
137+
<button
138+
class="button-positioned"
139+
@click="copyToClipboard(hestia_wget, $event.target)"
140+
type="button"
141+
title="Copy to Clipboard"
142+
>
143+
Copy
144+
</button>
145+
</div>
146+
<p class="u-mb10">Then run the following command:</p>
147+
<div class="u-pos-relative">
148+
<textarea class="form-control u-min-height100" v-model="installStr" readonly />
149+
<button
150+
class="button-positioned"
151+
@click="copyToClipboard(installStr, $event.target)"
152+
type="button"
153+
title="Copy to Clipboard"
154+
>
155+
Copy
156+
</button>
157+
</div>
158+
</div>
159+
</dialog>
160+
</div>
161+
</template>
162+
163+
<style scoped>
164+
.container {
165+
margin: 0px auto;
166+
max-width: 1152px;
167+
}
168+
.grid {
169+
display: grid;
170+
grid-gap: 20px;
171+
margin-top: 30px;
172+
margin-bottom: 30px;
173+
174+
@media (min-width: 640px) {
175+
grid-template-columns: 1fr 1fr;
176+
}
177+
178+
@media (min-width: 960px) {
179+
grid-template-columns: 1fr 1fr 1fr;
180+
}
181+
}
182+
.form-group {
183+
font-size: 0.9em;
184+
border-radius: 10px;
185+
padding: 15px 20px;
186+
background-color: var(--vp-c-bg-alt);
187+
}
188+
.form-label {
189+
display: inline-block;
190+
margin-left: 2px;
191+
padding-bottom: 5px;
192+
text-transform: capitalize;
193+
}
194+
.form-control {
195+
font-size: 0.9em;
196+
border: 1px solid var(--vp-c-border);
197+
border-radius: 4px;
198+
background-color: var(--vp-c-bg);
199+
width: 100%;
200+
padding: 5px 10px;
201+
202+
&:hover {
203+
border-color: var(--vp-c-border-hover);
204+
}
205+
206+
&:focus {
207+
border-color: var(--vp-c-brand);
208+
}
209+
}
210+
.form-select {
211+
appearance: auto;
212+
font-size: 0.9em;
213+
border: 1px solid var(--vp-c-border);
214+
border-radius: 4px;
215+
background-color: var(--vp-c-bg);
216+
padding: 5px 10px;
217+
width: 100%;
218+
219+
&:hover {
220+
border-color: var(--vp-c-border-hover);
221+
}
222+
223+
&:focus {
224+
border-color: var(--vp-c-brand);
225+
}
226+
}
227+
.form-check {
228+
position: relative;
229+
padding-left: 20px;
230+
margin-left: 3px;
231+
min-height: 24px;
232+
233+
& label {
234+
font-weight: 600;
235+
}
236+
}
237+
.form-check-input {
238+
position: absolute;
239+
margin-top: 5px;
240+
margin-left: -20px;
241+
}
242+
.form-submit {
243+
border: 1px solid transparent;
244+
display: inline-block;
245+
font-weight: 600;
246+
transition: color 0.25s, border-color 0.25s, background-color 0.25s;
247+
border-radius: 20px;
248+
font-size: 16px;
249+
padding: 10px 20px;
250+
background-color: var(--vp-button-brand-bg);
251+
border-color: var(--vp-button-brand-border);
252+
color: var(--vp-button-brand-text);
253+
254+
&:hover {
255+
background-color: var(--vp-button-brand-hover-bg);
256+
border-color: var(--vp-button-brand-hover-border);
257+
color: var(--vp-button-brand-hover-text);
258+
}
259+
260+
&:active {
261+
background-color: var(--vp-button-brand-active-bg);
262+
border-color: var(--vp-button-brand-active-border);
263+
color: var(--vp-button-brand-active-text);
264+
}
265+
}
266+
.button-positioned {
267+
position: absolute;
268+
right: 1px;
269+
top: 1px;
270+
border-top-right-radius: 3px;
271+
border-bottom-right-radius: 3px;
272+
color: var(--vp-c-brand);
273+
font-weight: 600;
274+
padding: 6px 10px;
275+
background-color: var(--vp-c-bg);
276+
}
277+
.modal {
278+
position: fixed;
279+
border-radius: 10px;
280+
border: 1px solid var(--vp-c-border);
281+
box-shadow: 0 8px 40px 0 rgb(0 0 0 / 35%);
282+
padding: 0;
283+
284+
&::backdrop {
285+
background-color: rgb(0 0 0 / 60%);
286+
}
287+
}
288+
.modal-close {
289+
position: absolute;
290+
top: 10px;
291+
right: 15px;
292+
font-weight: 600;
293+
color: var(--vp-c-brand);
294+
}
295+
.modal-content {
296+
padding: 30px;
297+
}
298+
.modal-heading {
299+
font-weight: 600;
300+
font-size: 1.3em;
301+
text-align: center;
302+
margin-bottom: 15px;
303+
}
304+
code {
305+
background-color: var(--vp-c-bg-alt);
306+
border-radius: 3px;
307+
padding: 2px 5px;
308+
}
309+
.u-mb10 {
310+
margin-bottom: 10px !important;
311+
}
312+
.u-min-height100 {
313+
min-height: 100px;
314+
}
315+
.u-text-center {
316+
text-align: center !important;
317+
}
318+
.u-monospace {
319+
font-family: monospace !important;
320+
}
321+
.u-pos-relative {
322+
position: relative !important;
323+
}
324+
</style>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script lang="ts"></script>
2+
3+
<template>
4+
<form class="InstallForm" id="form">
5+
<div class="InstallOptionsSection">
6+
<slot name="list" />
7+
</div>
8+
<cite
9+
>Based on: <a href="https://github.com/gabizz/hestiacp-scriptline-generator">@gabizz</a> and
10+
<a href="https://github.com/turbopixel/HestiaCP-Command-Creator">@turbopixel</a></cite
11+
>
12+
</form>
13+
</template>
14+
15+
<style scoped>
16+
.InstallForm {
17+
margin: 0.55em 0;
18+
padding: 0 1em;
19+
line-height: 1.5;
20+
}
21+
cite {
22+
font-size: small;
23+
margin: 0.55em 0;
24+
display: block;
25+
text-align: center;
26+
27+
& a {
28+
color: var(--vp-c-txt-1) !important;
29+
}
30+
}
31+
</style>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<template>
2+
<div class="InstallPage">
3+
<slot></slot>
4+
</div>
5+
</template>
6+
7+
<style scoped>
8+
.InstallPage {
9+
line-height: 1.5;
10+
}
11+
.InstallPage :deep(.container) {
12+
display: flex;
13+
flex-direction: column;
14+
margin: 0 auto;
15+
max-width: 1152px;
16+
}
17+
18+
.InstallPage :deep(a) {
19+
font-weight: 500;
20+
color: var(--vp-c-brand);
21+
text-decoration-style: dotted;
22+
transition: color 0.25s;
23+
}
24+
25+
.InstallPage :deep(a:hover) {
26+
color: var(--vp-c-brand-dark);
27+
}
28+
</style>

0 commit comments

Comments
 (0)