Add reload to instances + start configs

This commit is contained in:
Jordan Blasenhauer 2024-08-15 10:44:11 +02:00
parent e218f8e621
commit fea36fb073
20 changed files with 1315 additions and 1207 deletions

View file

@ -0,0 +1,402 @@
from .utils.widgets import (
button_widget,
button_group_widget,
title_widget,
subtitle_widget,
text_widget,
tabulator_widget,
input_widget,
icons_widget,
regular_widget,
unmatch_widget,
)
from .utils.table import add_column
from .utils.format import get_fields_from_field
from typing import Optional
columns = [
add_column(title="Name", field="name", formatter="text"),
add_column(title="Type", field="type", formatter="text"),
add_column(title="global", field="global", formatter="icons"),
add_column(
title="services", field="services", formatter="buttonGroup"
), # We will display a button with a modal that show all services apply. Case global, show all services.
add_column(
title="actions", field="actions", formatter="buttonGroup"
), # edit button that will switch to the form using display store + delete with modal to confirm
]
def configs_filter(types: Optional[list] = None) -> list: # healths = "up", "down", "loading"
filters = [
{
"type": "like",
"fields": ["name"],
"setting": {
"id": "input-search-name",
"name": "input-search-name",
"label": "configs_search_name", # keep it (a18n)
"value": "",
"inpType": "input",
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"fieldSize": "sm",
"popovers": [
{
"iconName": "info",
"text": "configs_search_name_desc",
}
],
"placeholder": "configs_search_name_placeholder", # keep it (a18n)
},
},
{
"type": "=",
"fields": ["global"],
"setting": {
"id": "select-global",
"name": "select-global",
"label": "configs_select_global", # keep it (a18n)
"value": "all", # keep "all"
"values": ["all", "yes", "no"], # keep
"inpType": "select",
"onlyDown": True,
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"fieldSize": "sm",
"popovers": [
{
"iconName": "info",
"text": "configs_select_global_desc",
}
],
},
},
]
if types is not None and len(types) >= 2:
filters.append(
{
"type": "=",
"fields": ["type"],
"setting": {
"id": "select-type",
"name": "select-type",
"label": "configs_select_type", # keep it (a18n)
"value": "all", # keep "all"
"values": ["all"] + types,
"inpType": "select",
"onlyDown": True,
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"fieldSize": "sm",
"popovers": [
{
"iconName": "info",
"text": "configs_select_type_desc",
}
],
},
}
)
return filters
def configs_item(
is_global: bool, # "yes" or "no"
filename: str = "",
config_type: str = "",
types: Optional[list] = None,
services: Optional[list] = None,
config_value: str = "",
is_new: bool = False,
display_index: int = 1,
):
global_text = "yes" if is_global else "no"
actions = [
button_widget(
id=f"edit-user-{filename}-{global_text}",
text="action_edit", # keep it (a18n)
color="edit",
size="normal",
hideText=True,
iconName="pen",
iconColor="white",
attrs={
"data-display-index": display_index,
"data-display-group": "main",
},
),
button_widget(
id=f"delete-user-{filename}-{global_text}",
text="action_delete", # keep it (a18n)
color="error",
size="normal",
hideText=True,
iconName="trash",
iconColor="white",
modal={
"widgets": [
title_widget(title="user_management_delete_title"), # keep it (a18n)
text_widget(text="user_management_delete_global_subtitle" if is_global else "user_management_delete_no_global_subtitle"), # keep it (a18n)
text_widget(bold=True, text=filename),
button_group_widget(
buttons=[
button_widget(
id=f"close-delete-btn-{filename}-{global_text}",
text="action_close", # keep it (a18n)
color="close",
size="normal",
attrs={"data-close-modal": ""}, # a11y
),
button_widget(
id=f"delete-btn-{filename}-{global_text}",
text="action_delete", # keep it (a18n)
color="delete",
size="normal",
iconName="trash",
iconColor="white",
attrs={
"data-submit-form": f"""{{ "filename" : "{filename}", "is_global" : "{global_text}" }}""",
"data-submit-endpoint": "/delete",
},
),
]
),
],
},
),
]
services_items = []
# get id
for index, service in enumerate(services):
services_items.append(
{
"id": text_widget(text=index)["data"],
"name": text_widget(text=service)["data"],
}
)
services_detail = (
button_group_widget(
buttons=[
button_widget(
id=f"services-btn-{filename}-{global_text}",
type="button",
iconName="disk",
iconColor="white",
text="configs_show_services", # keep it (a18n)
color="orange",
size="normal",
modal={
"widgets": [
title_widget(title="configs_services_title"), # keep it (a18n)
text_widget(text="configs_services_subtitle"), # keep it (a18n)
tabulator_widget(
id=f"table-services-{filename}-{global_text}",
columns=[{"title": "id", "field": "id", "formatter": "text"}, {"title": "Name", "field": "name", "formatter": "text"}],
# Add every services that apply to the conf. All if global.
items=services_items,
filters=[
{
"type": "like",
"fields": ["name"],
"setting": {
"id": "input-search-service",
"name": "input-search-service",
"label": "configs_search_service", # keep it (a18n)
"value": "",
"inpType": "input",
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
},
},
],
),
button_group_widget(
buttons=[
button_widget(
id=f"close-services-btn-{filename}-{global_text}",
text="action_close", # keep it (a18n)
color="close",
size="normal",
attrs={"data-close-modal": ""}, # a11y
)["data"],
]
),
],
},
)["data"]
]
),
)
return {
"name": text_widget(text=filename)["data"],
"type": text_widget(text=config_type)["data"],
"is_global": icons_widget(
iconName="check" if is_global == "up" else "cross" if is_global == "down" else "search",
value=global_text,
)["data"],
"actions": {"buttons": actions},
}
def configs_form(
is_global: bool, # "yes" or "no"
filename: str = "",
config_type: str = "",
types: Optional[list] = None,
services: Optional[list] = None,
config_value: str = "",
is_new: bool = False,
display_index: int = 1,
) -> dict:
global_text = "yes" if is_global else "no"
return {
"type": "card",
"maxWidthScreen": "md",
"display": ["main", 2],
"widgets": [
title_widget(
title="configs_create_title", # keep it (a18n)
),
subtitle_widget(
subtitle="configs_create_subtitle", # keep it (a18n)
),
regular_widget(
maxWidthScreen="xs",
endpoint="/add",
fields=[
get_fields_from_field(
input_widget(
id="instance-name",
name="instance-name",
label="configs_name", # keep it (a18n)
value="",
pattern="", # add your pattern if needed
columns={"pc": 12, "tablet": 12, "mobile": 12},
placeholder="configs_name_placeholder", # keep it (a18n)
popovers=[
{
"iconName": "info",
"text": "configs_name_desc",
}
],
)
),
get_fields_from_field(
input_widget(
id="instance-hostname",
name="instance-hostname",
label="configs_hostname", # keep it (a18n)
value="",
pattern="", # add your pattern if needed
columns={"pc": 12, "tablet": 12, "mobile": 12},
placeholder="configs_hostname_placeholder", # keep it (a18n)
popovers=[
{
"iconName": "info",
"text": "configs_hostname_desc",
}
],
)
),
],
buttons=[
button_widget(
id="create-instance-submit",
text="action_create",
iconName="plus",
iconColor="white",
color="success",
size="normal",
type="submit",
containerClass="flex justify-center",
)
],
),
],
}
def configs_tabs():
return {
"type": "tabs",
"widgets": [
button_group_widget(
buttons=[
button_widget(
text="configs_tab_list",
display=["main", 1],
size="tab",
color="info",
iconColor="white",
iconName="list",
),
button_widget(
text="configs_tab_add",
color="success",
display=["main", 2],
size="tab",
iconColor="white",
iconName="plus",
),
]
)
],
}
def configs_list(instances: Optional[list] = None, types: Optional[list] = None, methods: Optional[list] = None, healths: Optional[list] = None) -> dict:
if instances is None or len(instances) == 0:
return {
"type": "card",
"gridLayoutClass": "transparent",
"widgets": [
unmatch_widget(text="configs_not_found"),
],
}
items = []
for instance in instances:
items.append(
instance_item(
instance_name=instance.get("name", ""),
hostname=instance.get("hostname", ""),
instance_type=instance.get("type", ""),
method=instance.get("method", ""),
health=instance.get("health", ""),
creation_date=instance.get("creation_date", ""),
last_seen=instance.get("last_seen", ""),
)
)
return {
"type": "card",
"display": ["main", 1],
"widgets": [
title_widget(
title="configs_list_title", # keep it (a18n)
),
subtitle_widget(
subtitle="configs_list_subtitle", # keep it (a18n)
),
tabulator_widget(
id="table-instances",
columns=columns,
items=items,
filters=configs_filter(types=types, methods=methods, healths=healths),
),
],
}
def configs_builder(instances: Optional[list] = None, types: Optional[list] = None, methods: Optional[list] = None, healths: Optional[list] = None) -> list:
return [
# Tabs is button group with display value and a size tab inside a tabs container
configs_tabs(),
configs_list(instances=instances, types=types, methods=methods, healths=healths),
configs_new_form(),
]

View file

@ -1,333 +0,0 @@
from .utils.widgets import (
input_widget,
select_widget,
checkbox_widget,
combobox_widget,
editor_widget,
button_widget,
button_group_widget,
title_widget,
text_widget,
tabulator_widget,
icons_widget,
)
from typing import Optional
from .utils.table import add_column
configs_columns = [
add_column(title="Name", field="name", formatter="text"),
add_column(title="Type", field="type", formatter="text"),
add_column(title="global", field="global", formatter="icons"),
add_column(
title="services", field="services", formatter="buttonGroup"
), # We will display a button with a modal that show all services apply. Case global, show all services.
add_column(
title="actions", field="actions", formatter="buttonGroup"
), # edit button that will switch to the form using display store + delete with modal to confirm
]
def config_form(
is_global: str, # "yes" or "no"
filename: str = "",
config_type: str = "",
types: Optional[list] = None,
services: Optional[list] = None,
config_value: str = "",
is_new: bool = False,
display_index: int = 1,
):
return (
{
"type": "card",
"display": ["main", display_index], # Allow to toggle between each form using displayStore
"widgets": [
button_widget(
id=f"back-btn-{filename}-global-{is_global}" if not is_new else "back-btn",
text="action_back", # keep it (a18n)
color="info",
size="normal",
display=["main", 0] if not is_new else [], # Return to list if edit
),
input_widget(
id=f"config-name-new" if is_new else f"config-name-{filename}-global-{is_global}",
name="config-name",
label="configs_filename", # keep it (a18n)
value="" if is_new else filename, # empty if new or replace by the filename value to edit (.conf excluded)
pattern="", # add your pattern if needed
columns={"pc": 3, "tablet": 4, "mobile": 12},
),
# Select between available types
select_widget(
id="select-type",
name="select-type",
label="configs_types", # keep it (a18n)
value="" if is_new else config_type,
values=types or [], # set all available types like ["http", "modsec"]
columns={"pc": 3, "tablet": 4, "mobile": 12},
onlyDown=True,
),
# Add script on Page.vue to disabled listcheck in case checkbox is checked
# This checkbox is priority over services checklist
# Check or not to used globally the conf
checkbox_widget(
id="config-global",
name="config-global",
label="configs_global", # keep it (a18n)
value=is_global, # no if new, else it depends of the current conf
columns={"pc": 3, "tablet": 4, "mobile": 12},
),
# Case checkbox is checked, this checklist will be ignored on server
# Combobox ATM but will be replace by a checklist
# set services list ATM, we will update by a checklist with [{value : "service1", is_check : bool}, ...]
combobox_widget(
id="combo-services",
name="combo-services",
label="configs_types", # keep it (a18n)
value="",
values=services or [], # set services list ATM, we will update by a checklist with [{value : "service1", is_check : bool}, ...]
onlyDown=True,
columns={"pc": 3, "tablet": 4, "mobile": 12},
),
# Editor to edit the conf
editor_widget(
id="config-value",
name="config-value",
label="configs_value", # keep it (a18n)
value="" if is_new else config_value,
columns={"pc": 3, "tablet": 4, "mobile": 12},
),
input_widget(
id="operation",
name="operation",
label="configs_operation", # keep it (a18n)
value="new" if is_new else "edit",
pattern="", # add your pattern if needed
columns={"pc": 3, "tablet": 4, "mobile": 12},
inpClass="hidden", # hide it
),
button_widget(
id="update-config",
text="action_create" if is_new else "action_update", # keep it (a18n)
color="success",
size="normal",
),
],
},
)
def configs_filter(types: list):
return [
{
"type": "like",
"fields": ["name"],
"setting": {
"id": "input-search-name",
"name": "input-search-name",
"label": "configs_search_name", # keep it (a18n)
"value": "",
"inpType": "input",
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
},
},
{
"type": "=",
"fields": ["type"],
"setting": {
"id": "select-type",
"name": "select-type",
"label": "configs_select_type", # keep it (a18n)
"value": "all", # keep "all"
"values": ["all"] + types,
"inpType": "select",
"onlyDown": True,
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
},
},
{
"type": "=",
"fields": ["global"],
"setting": {
"id": "select-global",
"name": "select-global",
"label": "configs_select_global", # keep it (a18n)
"value": "all", # keep "all"
"values": ["all", "yes", "no"], # keep
"inpType": "select",
"onlyDown": True,
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
},
},
]
def config_item(filename: str, conf_type: str, is_global: str, services: list, display_index: int):
services_items = []
# get id
for index, service in enumerate(services):
services_items.append(
{
"id": text_widget(text=index)["data"],
"name": text_widget(text=service)["data"],
}
)
return (
{
"name": text_widget(text=filename)["data"],
"type": text_widget(text=conf_type)["data"],
"global": icons_widget(iconName="check" if is_global == "yes" else "cross", value=is_global)["data"],
"services": button_group_widget(
buttons=[
button_widget(
id=f"services-btn-{filename}-global-{is_global}",
type="button",
iconName="disk",
iconColor="white",
text="configs_show_services", # keep it (a18n)
color="orange",
size="normal",
modal={
"widgets": [
title_widget(title="configs_services_title"), # keep it (a18n)
text_widget(text="configs_services_subtitle"), # keep it (a18n)
tabulator_widget(
id=f"table-services-{filename}-global-{is_global}",
columns=[{"title": "id", "field": "id", "formatter": "text"}, {"title": "Name", "field": "name", "formatter": "text"}],
# Add every services that apply to the conf. All if global.
items=services_items,
filters=[
{
"type": "like",
"fields": ["name"],
"setting": {
"id": "input-search-service",
"name": "input-search-service",
"label": "configs_search_service", # keep it (a18n)
"value": "",
"inpType": "input",
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
},
},
],
),
button_group_widget(
buttons=[
button_widget(
id=f"close-services-btn-{filename}-global-{is_global}",
text="action_close", # keep it (a18n)
color="close",
size="normal",
attrs={"data-close-modal": ""}, # a11y
)["data"],
]
),
],
},
)["data"]
]
),
"actions": button_group_widget(
buttons=[
button_widget(
id=f"edit-{filename}-global-{is_global}",
type="button",
iconName="pen",
iconColor="white",
text="configs_edit_config", # keep it (a18n)
hideText=True,
color="yellow",
size="normal",
display=["main", display_index],
)["data"],
# Delete button with modal to confirm
button_widget(
id=f"delete-{filename}-global-{is_global}",
type="button",
iconName="trash",
iconColor="white",
text="configs_delete_config", # keep it (a18n)
hideText=True,
color="error",
size="normal",
modal={
"widgets": [
title_widget(title="configs_delete_title"), # keep it (a18n)
text_widget(text="configs_delete_subtitle"), # keep it (a18n)
button_group_widget(
buttons=[
button_widget(
id=f"close-delete-btn-{filename}-global-{is_global}",
text="action_close", # keep it (a18n)
color="close",
size="normal",
attrs={"data-close-modal": ""}, # a11y
)["data"],
]
),
button_widget(
id=f"delete-btn-{filename}-global-{is_global}",
text="action_delete", # keep it (a18n)
color="delete",
size="normal",
attrs={
"data-submit-form": f"""{{"conf_name" : "{filename}-global-{is_global}", "conf_type" : "{conf_type}" }}""",
"data-submit-endpoint": "/delete",
},
)["data"],
],
},
)["data"],
]
),
},
)
def configs_builder(configs: list, config_types) -> list:
configs_items = []
configs_forms = []
# Start adding the new config form
configs_forms.append(config_form(is_new=True, display_index=1, types=config_types, is_global="no"))
# display_index start at 2 because 1 is the new and 0 is the configs table
for index, config in enumerate(configs):
display_index = index + 2
configs_items.append(
config_item(
filename=config.get("filename"),
conf_type=config.get("type"),
is_global=config.get("is_global"),
services=config.get("services"),
display_index=display_index,
)
)
configs_forms.append(
config_form(
filename=config.get("filename"),
config_type=config.get("type"),
types=config_types,
is_global=config.get("is_global"),
services=config.get("services"),
config_value=config.get("config_value"),
display_index=display_index,
)
)
return [
{
"type": "card",
"display": ["main", 0],
"widgets": [
tabulator_widget(
id="table-configs",
columns=configs_columns,
items=configs_items,
filters=configs_filter(config_types),
),
],
},
] + configs_forms

View file

@ -178,7 +178,46 @@ def instance_item(
),
],
},
)
),
button_widget(
id=f"reload-instance-{instance_name}",
text="action_reload", # keep it (a18n)
color="success",
size="normal",
hideText=True,
iconName="refresh",
iconColor="white",
modal={
"widgets": [
title_widget(title="instances_reload_title"), # keep it (a18n)
text_widget(text="instances_reload_subtitle"), # keep it (a18n)
text_widget(bold=True, text=instance_name),
button_group_widget(
buttons=[
button_widget(
id=f"close-reload-btn-{instance_name}",
text="action_close", # keep it (a18n)
color="close",
size="normal",
attrs={"data-close-modal": ""}, # a11y
),
button_widget(
id=f"reload-btn-{instance_name}",
text="action_reload", # keep it (a18n)
color="info",
size="normal",
iconName="globe",
iconColor="white",
attrs={
"data-submit-form": f"""{{"instance_name" : "{instance_name}", "instance_hostname" : "{hostname}" }}""",
"data-submit-endpoint": "/reload",
},
),
]
),
],
},
),
]
if method.strip() in ("ui", "manual", "default"):
@ -381,8 +420,6 @@ def instances_list(instances: Optional[list] = None, types: Optional[list] = Non
def instances_builder(instances: Optional[list] = None, types: Optional[list] = None, methods: Optional[list] = None, healths: Optional[list] = None) -> list:
items = []
return [
# Tabs is button group with display value and a size tab inside a tabs container
instances_tabs(),

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
from utils import save_builder
from pages.configs2 import configs_builder
from pages.configs import configs_builder
configs = [
@ -28,4 +28,4 @@ config_types = ["http", "https", "socks4", "socks5"]
builder = configs_builder(configs, config_types)
save_builder("configs2", builder, update_page=False)
save_builder(page_name="configs", output=builder, script_name="configs")

View file

@ -0,0 +1,86 @@
<script setup>
// Containers
import Grid from "@components/Widget/Grid.vue";
import GridLayout from "@components/Widget/GridLayout.vue";
import Tabulator from "@components/Widget/Tabulator.vue";
import Button from "@components/Widget/Button.vue";
import ButtonGroup from "@components/Widget/ButtonGroup.vue";
import Regular from "@components/Form/Regular.vue";
import Title from "@components/Widget/Title.vue";
import Subtitle from "@components/Widget/Subtitle.vue";
import MessageUnmatch from "@components/Message/Unmatch.vue";
import { useEqualStr } from "@utils/global.js";
/**
* @name Builder/Configs.vue
* @description This component is lightweight builder containing only the necessary components to create the configs page.
* @example
* [
* {
* type: "card",
* gridLayoutClass: "transparent",
* widgets: [
* { type: "MessageUnmatch",
* data: { text: "bans_not_found" }
* },
* ],
* },
* ];
* @param {array} builder - Array of containers and widgets
*/
const props = defineProps({
builder: {
type: Array,
required: true,
},
});
</script>
<template>
<!-- top level grid (layout) -->
<GridLayout
v-for="(container, index) in props.builder"
:key="index"
:gridLayoutClass="container.containerClass"
:maxWidthScreen="container.maxWidthScreen"
:type="container.type"
:title="container.title"
:link="container.link"
:columns="container.containerColumns"
:id="container.id"
:display="container.display"
>
<!-- widget grid -->
<Grid>
<!-- widget element -->
<template v-for="(widget, index) in container.widgets" :key="index">
<Title v-if="useEqualStr(widget.type, 'Title')" v-bind="widget.data" />
<Subtitle
v-if="useEqualStr(widget.type, 'Subtitle')"
v-bind="widget.data"
/>
<MessageUnmatch
v-if="useEqualStr(widget.type, 'MessageUnmatch')"
v-bind="widget.data"
/>
<Tabulator
v-if="useEqualStr(widget.type, 'Tabulator')"
v-bind="widget.data"
/>
<Button
v-if="useEqualStr(widget.type, 'Button')"
v-bind="widget.data"
/>
<Regular
v-if="useEqualStr(widget.type, 'Regular')"
v-bind="widget.data"
/>
<ButtonGroup
v-if="useEqualStr(widget.type, 'ButtonGroup')"
v-bind="widget.data"
/>
</template>
</Grid>
</GridLayout>
</template>

View file

@ -9,7 +9,7 @@ import ButtonGroup from "@components/Widget/ButtonGroup.vue";
import { useEqualStr } from "@utils/global.js";
/**
* @name Builder/PLugin.vue
* @name Builder/PLugins.vue
* @description This component is lightweight builder containing only the necessary components to create the plugins page.
* @example
* [

View file

@ -12,8 +12,8 @@ import MessageUnmatch from "@components/Message/Unmatch.vue";
import { useEqualStr } from "@utils/global.js";
/**
* @name Builder/Bans.vue
* @description This component is lightweight builder containing only the necessary components to create the bans page.
* @name Builder/UserManagement.vue
* @description This component is lightweight builder containing only the necessary components to create the user management page.
* @example
* [
* {

View file

@ -41,7 +41,7 @@ const icon = reactive({
:data-color="icon.color"
:data-value="props.value"
:aria-disabled="props.disabled ? 'true' : 'false'"
data-svg="box"
data-svg="back"
role="img"
aria-hidden="true"
:class="[props.iconClass, icon.color, 'fill dark:brightness-[125%]']"

View file

@ -0,0 +1,58 @@
<script setup>
import { defineProps, reactive } from "vue";
/**
* @name Icons/Refresh.vue
* @description This component is a svg icon representing refresh.
* @example
* {
* color: 'info',
* }
* @param {String} [iconClass="icon-default"] - The class of the icon.
* @param {Any} [value=""] - Attach a value to icon. Useful on some cases like table filtering using icons.
* @param {String} [color="dark"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
* @param {Boolean} [disabled=false] - If true, the icon will be disabled.
*/
const props = defineProps({
iconClass: {
type: String,
required: false,
default: "icon-default",
},
value: {
type: [String, Number],
required: false,
default: "",
},
color: {
type: String,
required: false,
default: "sky",
},
disabled: { type: Boolean, required: false, default: false },
});
const icon = reactive({
color: props.color || "sky",
});
</script>
<template>
<svg
:data-color="icon.color"
:data-value="props.value"
:aria-disabled="props.disabled ? 'true' : 'false'"
data-svg="reload"
role="img"
aria-hidden="true"
:class="[props.iconClass, icon.color, 'fill dark:brightness-[125%]']"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M4.755 10.059a7.5 7.5 0 0 1 12.548-3.364l1.903 1.903h-3.183a.75.75 0 1 0 0 1.5h4.992a.75.75 0 0 0 .75-.75V4.356a.75.75 0 0 0-1.5 0v3.18l-1.9-1.9A9 9 0 0 0 3.306 9.67a.75.75 0 1 0 1.45.388Zm15.408 3.352a.75.75 0 0 0-.919.53 7.5 7.5 0 0 1-12.548 3.364l-1.902-1.903h3.183a.75.75 0 0 0 0-1.5H2.984a.75.75 0 0 0-.75.75v4.992a.75.75 0 0 0 1.5 0v-3.18l1.9 1.9a9 9 0 0 0 15.059-4.035.75.75 0 0 0-.53-.918Z"
clip-rule="evenodd"
/>
</svg>
</template>

View file

@ -39,6 +39,7 @@ import Document from "@components/Icons/Document.vue";
import Eye from "@components/Icons/Eye.vue";
import Uncheck from "@components/Icons/Uncheck.vue";
import Back from "@components/Icons/Back.vue";
import Refresh from "@components/Icons/Refresh.vue";
/**
* @name Widget/Icons.vue
@ -111,6 +112,7 @@ onMounted(() => {
v-if="useEqualStr(props.iconName, 'exclamation')"
v-bind="icon"
/>
<Refresh v-if="useEqualStr(props.iconName, 'refresh')" v-bind="icon" />
<Back v-if="useEqualStr(props.iconName, 'back')" v-bind="icon" />
<Box v-if="useEqualStr(props.iconName, 'box')" v-bind="icon" />
<Uncheck v-if="useEqualStr(props.iconName, 'uncheck')" v-bind="icon" />

View file

@ -206,6 +206,8 @@
"instances_method_popover": "Select the instance method.",
"instances_health": "health",
"instances_health_popover": "Select the instance health. Health is based on the last seen date.",
"instances_reload_title": "Reload instance",
"instances_reload_subtitle": "Are you sure to reload the following instance ?",
"instances_ping_title": "Ping instance",
"instances_ping_subtitle": "Ping will check instance health of the following instance.",
"instances_delete_title": "Delete instance",

View file

@ -0,0 +1,44 @@
<script setup>
import { reactive, onBeforeMount, onMounted } from "vue";
import DashboardLayout from "@components/Dashboard/Layout.vue";
import BuilderConfigs from "@components/Builder/Configs.vue";
import { useGlobal } from "@utils/global.js";
import { useDisplayStore } from "@store/global.js";
/**
* @name Page/Configs.vue
* @description This component is the configs page.
This page displays current users and allows to manage them.
We are using displayStore and setting ["main", 1] to display the instances list first.
*/
// Set default store
const displayStore = useDisplayStore();
displayStore.setDisplay("main", 0);
const configs = reactive({
builder: "",
});
onBeforeMount(() => {
// Get builder data
const dataAtt = "data-server-builder";
const dataEl = document.querySelector(`[${dataAtt}]`);
const data =
dataEl && !dataEl.getAttribute(dataAtt).includes(dataAtt)
? JSON.parse(atob(dataEl.getAttribute(dataAtt)))
: {};
configs.builder = data;
});
onMounted(() => {
// Set the page title
useGlobal();
});
</script>
<template>
<DashboardLayout>
<BuilderConfigs v-if="configs.builder" :builder="configs.builder" />
</DashboardLayout>
</template>

View file

@ -0,0 +1,11 @@
import { createApp } from "vue";
import { createPinia } from "pinia";
import { getI18n } from "@utils/lang.js";
import Configs from "./Configs.vue";
const pinia = createPinia();
createApp(Configs)
.use(pinia)
.use(getI18n(["dashboard", "action", "inp", "icons", "configs"]))
.mount("#app");

File diff suppressed because one or more lines are too long

View file

@ -29,93 +29,6 @@ onMounted(() => {
// Set the page title
useGlobal();
});
// const data = [
// {
// type: "card",
// link : "https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui"
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_version",
// subtitle: "home_all_features_available" if is_pro_version else "home_upgrade_pro",
// subtitleColor: "success" is is_pro_version else "warning",
// stat: "home_pro" if is_pro_version else "home_free",
// iconName: "crown" if is_pro_version else "core",
// },
// },
// ],
// },
// {
// type: "card",
// link: "https://github.com/bunkerity/bunkerweb",
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_version_number",
// subtitle: "home_latest_version" if is_latest_version else "home_upgrade_available",
// subtitleColor: "success" if is_latest_version else "warning",
// stat: <current_version>,
// iconName: "wire",
// },
// },
// ],
// },
// {
// type: "card",
// link: "/instances",
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_instances",
// subtitle: "home_total_number",
// subtitleColor: "info",
// stat: "<instances_total>",
// iconName: "box",
// },
// },
// ],
// },
// {
// type: "card",
// link: "/services",
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_services",
// subtitle: "home_all_methods_included",
// subtitleColor: "info",
// stat: "<services_total>",
// iconName: "disk",
// },
// },
// ],
// },
// {
// type: "card",
// link: "/plugins",
// containerColumns: { pc: 4, tablet: 6, mobile: 12 },
// widgets: [
// {
// type: "Stat",
// data: {
// title: "home_plugins",
// subtitle: "home_no_error" if all_plugins_ok else "home_errors_found",
// subtitleColor: "success" if all_plugins_ok else "error",
// stat: "<plugins_total>",
// iconName: "puzzle",
// },
// },
// ],
// },
// ];
</script>
<template>

View file

@ -35,28 +35,6 @@ onMounted(() => {
// Set the page title
useGlobal();
});
// const data = [
// {
// type: "Instance",
// data: {
// details: [
// { key: <instances_hostname="hostname">, value: "www.example.com" },
// { key: <instances_method="method">, value: <dashboard_ui> or <dashboard_scheduler>...},
// { key: <instances_port="port">, value: "1084" },
// { key: <instances_status="status">, value: <instances_active="active"> or <instances_inactive="inactive"> },
// ],
// status: "success",
// title: "www.example.com",
// buttons: [
// {
// text: <action_*>,
// color: "edit",
// size: "normal",
// },
// ],
// },
// },
// ];
</script>
<template>

File diff suppressed because one or more lines are too long

View file

@ -35,28 +35,6 @@ onMounted(() => {
// Set the page title
useGlobal();
});
// const data = [
// {
// type: "Instance",
// data: {
// details: [
// { key: <instances_hostname="hostname">, value: "www.example.com" },
// { key: <instances_method="method">, value: <dashboard_ui> or <dashboard_scheduler>...},
// { key: <instances_port="port">, value: "1084" },
// { key: <instances_status="status">, value: <instances_active="active"> or <instances_inactive="inactive"> },
// ],
// status: "success",
// title: "www.example.com",
// buttons: [
// {
// text: <action_*>,
// color: "edit",
// size: "normal",
// },
// ],
// },
// },
// ];
</script>
<template>

File diff suppressed because one or more lines are too long