Co-authored-by: XakPlant <xakplant@users.noreply.github.com>
This commit is contained in:
AB
2024-04-28 13:15:07 +03:00
parent 35f57de110
commit 48521cb8a3
4 changed files with 317 additions and 202 deletions

View File

@ -1,3 +1,11 @@
:root {
--app-h: 100vh;
--app-space-1: 8px;
}
/* /*
* -- BASE STYLES -- * -- BASE STYLES --
* Most of these are inherited from Base, but I want to change a few. * Most of these are inherited from Base, but I want to change a few.
@ -22,6 +30,7 @@ a {
.button { .button {
border-radius: 4px; border-radius: 4px;
} }
.delete-button { .delete-button {
background: #9d2c2c; background: #9d2c2c;
border: 1px solid #480b0b; border: 1px solid #480b0b;
@ -32,7 +41,8 @@ a {
* -- LAYOUT STYLES -- * -- LAYOUT STYLES --
* This layout consists of three main elements, `#nav` (navigation bar), `#list` (server list), and `#main` (server content). All 3 elements are within `#layout` * This layout consists of three main elements, `#nav` (navigation bar), `#list` (server list), and `#main` (server content). All 3 elements are within `#layout`
*/ */
#layout, #nav, #list, #main { #nav,
#main {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
@ -45,6 +55,7 @@ a {
background: rgb(37, 42, 58); background: rgb(37, 42, 58);
text-align: center; text-align: center;
} }
/* Show the "Menu" button on phones */ /* Show the "Menu" button on phones */
#nav .nav-menu-button { #nav .nav-menu-button {
display: block; display: block;
@ -55,8 +66,9 @@ a {
/* When "Menu" is clicked, the navbar should be 80% height */ /* When "Menu" is clicked, the navbar should be 80% height */
#nav.active { #nav.active {
height: 80%; height: 100%;
} }
/* Don't show the navigation items... */ /* Don't show the navigation items... */
.nav-inner { .nav-inner {
display: none; display: none;
@ -78,14 +90,17 @@ a {
border: none; border: none;
text-align: left; text-align: left;
} }
#nav .pure-menu-link:hover, #nav .pure-menu-link:hover,
#nav .pure-menu-link:focus { #nav .pure-menu-link:focus {
background: rgb(55, 60, 90); background: rgb(55, 60, 90);
} }
#nav .pure-menu-link { #nav .pure-menu-link {
color: #fff; color: #fff;
margin-left: 0.5em; margin-left: 0.5em;
} }
#nav .pure-menu-heading { #nav .pure-menu-heading {
border-bottom: none; border-bottom: none;
font-size: 110%; font-size: 110%;
@ -110,45 +125,63 @@ a {
margin-right: 0.5em; margin-right: 0.5em;
border-radius: 3px; border-radius: 3px;
} }
.server-label-personal { .server-label-personal {
background: #ffc94c; background: #ffc94c;
} }
.server-label-work { .server-label-work {
background: #41ccb4; background: #41ccb4;
} }
.server-label-travel { .server-label-travel {
background: #40c365; background: #40c365;
} }
/* server Item Styles */ /* server Item Styles */
.server-item { .server-content-title {
padding: 0.9em 1em; padding: var(--app-space-1);
border-bottom: 1px solid #ddd; /* color: #ffffff; */
border-left: 6px solid transparent;
} }
.server-item {
padding: var(--app-space-1);
/* color: #ffffff; */
cursor: pointer;
}
.server-item:hover {
background: #d1d0d0;
}
.server-name { .server-name {
text-transform: uppercase; text-transform: uppercase;
} }
.server-name, .server-name,
.server-info { .server-info {
margin: 0; margin: 0;
font-size: 100%; font-size: 100%;
} }
.server-info { .server-info {
color: #999; color: #999;
font-size: 80%; font-size: 80%;
} }
.server-comment { .server-comment {
font-size: 90%; font-size: 90%;
margin: 0.4em 0; margin: 0.4em 0;
} }
.server-add { .server-add {
cursor: pointer; cursor: pointer;
text-align: center; text-align: center;
font-size: 150%; font-size: 150%;
color: #999; color: #999;
} }
.server-add:hover { .server-add:hover {
color: black; color: black;
} }
@ -156,21 +189,23 @@ a {
.server-item-selected { .server-item-selected {
background: #eeeeee; background: #eeeeee;
} }
.server-item-unread { .server-item-unread {
border-left: 6px solid #1b98f8; border-left: 6px solid #1b98f8;
} }
.server-item-broken { .server-item-broken {
border-left: 6px solid #880d06; border-left: 6px solid #880d06;
} }
.server-item:hover {
background: #d1d0d0;
}
/* server Content Styles */ /* server Content Styles */
.server-content-header, .server-content-body, .server-content-footer { .server-content-header,
.server-content-body,
.server-content-footer {
padding: 1em 2em; padding: 1em 2em;
} }
.server-content-header { .server-content-header {
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
} }
@ -178,18 +213,22 @@ a {
.server-content-title { .server-content-title {
margin: 0.5em 0 0; margin: 0.5em 0 0;
} }
.server-content-subtitle { .server-content-subtitle {
font-size: 1em; font-size: 1em;
margin: 0; margin: 0;
font-weight: normal; font-weight: normal;
} }
.server-content-subtitle span { .server-content-subtitle span {
color: #999; color: #999;
} }
.server-content-controls { .server-content-controls {
margin-top: 2em; margin-top: 2em;
text-align: right; text-align: right;
} }
.server-content-controls .secondary-button { .server-content-controls .secondary-button {
margin-bottom: 0.3em; margin-bottom: 0.3em;
} }
@ -207,21 +246,16 @@ a {
*/ */
@media (min-width: 40em) { @media (min-width: 40em) {
/* Move the layout over so we can fit the nav + list in on the left */
#layout {
padding-left:500px;
position: relative;
}
/* These are position:fixed; elements that will be in the left 500px of the screen */ /* These are position:fixed; elements that will be in the left 500px of the screen */
#nav, #list { #nav {
position: fixed; position: fixed;
top: 0; top: 0;
bottom: 0; bottom: 0;
overflow: auto; overflow: auto;
} }
#nav { #nav {
margin-left:-500px; /* margin-left: -500px; */
width: 150px; width: 150px;
height: 100%; height: 100%;
} }
@ -237,12 +271,6 @@ a {
display: none; display: none;
} }
#list {
margin-left: -350px;
width: 100%;
height: 33%;
border-bottom: 1px solid #ddd;
}
#main { #main {
position: fixed; position: fixed;
@ -251,7 +279,8 @@ a {
bottom: 0; bottom: 0;
left: 150px; left: 150px;
overflow: auto; overflow: auto;
width: auto; /* so that it's not 100% */ width: auto;
/* so that it's not 100% */
} }
} }
@ -264,12 +293,7 @@ a {
@media (min-width: 60em) { @media (min-width: 60em) {
/* This will take up the entire height, and be a little thinner */ /* This will take up the entire height, and be a little thinner */
#list {
margin-left: -350px;
width:350px;
height: 100%;
border-right: 1px solid #ddd;
}
/* This will now take up it's own column, so don't need position: fixed; */ /* This will now take up it's own column, so don't need position: fixed; */
#main { #main {
@ -350,3 +374,53 @@ a {
border: 1px solid #EDD; border: 1px solid #EDD;
color: #A66; color: #A66;
} }
#layout {
display: grid;
grid-template-areas:
"menu entety-menu content";
height: var(--app-h);
justify-content: left;
}
#menu {
grid-area: menu;
width: 150px;
height: 100%;
background-color: #333;
}
#entety-menu {
grid-area: entety-menu;
width: 300px;
height: 100%;
background-color: #fff;
}
#content {
grid-area: content;
height: 100%;
}
@media (max-width: 60em) {
#layout {
display: grid;
grid-template-areas:
"menu"
"entety-menu"
"content";
grid-template-rows: min-content min-content;
}
#menu {
width: 100vw;
height: fit-content;
}
#entety-menu {
width: 100vw;
height: initial;
}
}

File diff suppressed because one or more lines are too long

View File

@ -2,13 +2,12 @@
{% block content %} {% block content %}
<div id="list" class="pure-u-1-3" xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html"> <div id="entety-menu">
<div class="server-item pure-g"> <div id="list">
<h1 class="server-content-title">Clients</h1> <h1 class="server-content-title">Clients</h1>
</div>
{% for client, values in CLIENTS.items() %} {% for client, values in CLIENTS.items() %}
<div class="server-item server-item-{% if client == selected_client %}unread{% else %}selected{% endif %} pure-g"> <div class="server-item server-item-{% if client == selected_client %}unread{% else %}selected{% endif %} pure-g">
<div class="pure-u-3-4" onclick="location.href='/clients?selected_client={{ client }}';"> <div class="" onclick="location.href='/clients?selected_client={{ client }}';">
<h5 class="server-name">{{ values["name"] }}</h5> <h5 class="server-name">{{ values["name"] }}</h5>
<h4 class="server-info">Allowed {{ values["servers"]|length }} server{% if values["servers"]|length >1 %}s{%endif%}</h4> <h4 class="server-info">Allowed {{ values["servers"]|length }} server{% if values["servers"]|length >1 %}s{%endif%}</h4>
</div> </div>
@ -21,11 +20,13 @@
</div> </div>
</div> </div>
</div>
{% if add_client %} {% if add_client %}
<div class="pure-u-1-3"> <div id="content">
<div class="">
<div class="server-content-header pure-g"> <div class="server-content-header pure-g">
<div class="pure-u-1-2"> <div class="">
<h1 class="server-content-title">Add new client</h1> <h1 class="server-content-title">Add new client</h1>
</div> </div>
</div> </div>
@ -33,11 +34,11 @@
<form action="/add_client" class="pure-form pure-form-stacked" method="POST"> <form action="/add_client" class="pure-form pure-form-stacked" method="POST">
<fieldset> <fieldset>
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<input type="text" class="pure-u-23-24" name="name" required placeholder="Name"/> <input type="text" class="" name="name" required placeholder="Name"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<input type="text" class="pure-u-23-24" name="comment" placeholder="Comment"/> <input type="text" class="" name="comment" placeholder="Comment"/>
</div> </div>
<div class="pure-checkbox"> <div class="pure-checkbox">
{% for server in SERVERS %} {% for server in SERVERS %}
@ -54,15 +55,17 @@
</form> </form>
</div> </div>
</div> </div>
</div>
{% endif %} {% endif %}
{% if selected_client and not add_client %} {% if selected_client and not add_client %}
{% set client = CLIENTS[selected_client] %} {% set client = CLIENTS[selected_client] %}
<div class="pure-u-1-2"> <div id="content">
<div class="">
<div class="server-content-header pure-g"> <div class="server-content-header pure-g">
<div class="pure-u-1-2"> <div class="">
<h1 class="server-content-title">{{client['name']}}</h1> <h1 class="server-content-title">{{client['name']}}</h1>
<h4 class="server-info">{{ client['comment'] }}</h4> <h4 class="server-info">{{ client['comment'] }}</h4>
<h4 class="server-info">id {{ selected_client }}</h4> <h4 class="server-info">id {{ selected_client }}</h4>
@ -73,11 +76,11 @@
<form action="/add_client" class="pure-form pure-form-stacked" method="POST"> <form action="/add_client" class="pure-form pure-form-stacked" method="POST">
<fieldset> <fieldset>
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<input type="text" class="pure-u-1" name="name" required value="{{client['name']}}"/> <input type="text" class="pure-u-1" name="name" required value="{{client['name']}}"/>
<input type="hidden" class="pure-u-1" name="old_name" required value="{{client['name']}}"/> <input type="hidden" class="pure-u-1" name="old_name" required value="{{client['name']}}"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<input type="text" class="pure-u-1" name="comment" value="{{client['comment']}}"/> <input type="text" class="pure-u-1" name="comment" value="{{client['comment']}}"/>
</div> </div>
<input type="hidden" class="pure-u-1" name="user_id" value="{{selected_client}}"/> <input type="hidden" class="pure-u-1" name="user_id" value="{{selected_client}}"/>
@ -101,19 +104,23 @@
<div> <div>
<h3>Invite text</h3><hr> <h3>Invite text</h3><hr>
<textarea style="width: 100%; rows=10"> <p>Install Outline VPN. Copy and paste the keys below into the Outline client.
Install OutLine VPN. Copy and paste below keys to OutLine client. The same keys can be used simultaneously on multiple devices.</p>
Same keys will work simultaneously on many devices.
{% for server in SERVERS -%} {% for server in SERVERS -%}
{% if server.info()['local_server_id'] in client['servers'] %} {% if server.info()['local_server_id'] in client['servers'] %}
{{server.info()['name']}} {% set salt = bcrypt.gensalt() %}
```{% for key in server.data["keys"] %}{% if key.key_id == client['name'] %}ssconf://{{ dynamic_hostname }}/dynamic/{{server.info()['name']}}/{{selected_client}}#{{server.info()['comment']}}{% endif %}{% endfor %}``` {% set secret_string = server.info()['local_server_id'] + selected_client %}
{% set hash_secret = bcrypt.hashpw(
password=secret_string.encode('utf-8'),
salt=salt).decode('utf-8') %}
<p><b>Server location:</b> {{server.info()['name']}}</p>
<p><b>Client link:</b> {% for key in server.data["keys"] %}{% if key.key_id == client['name'] %}ssconf://{{ dynamic_hostname }}/dynamic/{{server.info()['local_server_id'][0:SECRET_LINK_LENGTH]}}{{selected_client[0:SECRET_LINK_LENGTH]}}{{hash_secret[SECRET_LINK_PREFIX|length:]}}#{{server.info()['comment']}}{% endif %}{% endfor %}</p>
{% endif %} {% endif %}
{%- endfor -%}</textarea> {%- endfor -%}
</div> </div>
<hr> <hr>
<div style="padding-top: 15px; padding-bottom: 15px"> <div>
<div class="pure-u-1"> <div class="pure-u-1">
<h3>Dynamic Access Keys</h3> <h3>Dynamic Access Keys</h3>
<table class="pure-table"> <table class="pure-table">
@ -174,6 +181,7 @@ Same keys will work simultaneously on many devices.
</form> </form>
</div> </div>
</div> </div>
</div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -2,10 +2,11 @@
{% block content %} {% block content %}
<div id="list" class="pure-u-1-3" xmlns="http://www.w3.org/1999/html">
<div class="server-item pure-g">
<div id="entety-menu">
<div>
<h1 class="server-content-title">Servers</h1> <h1 class="server-content-title">Servers</h1>
</div>
{% for server in SERVERS %} {% for server in SERVERS %}
{% set total_traffic = namespace(total_bytes=0) %} {% set total_traffic = namespace(total_bytes=0) %}
{% for key in server.data["keys"] %} {% for key in server.data["keys"] %}
@ -34,26 +35,28 @@
</div> </div>
</div> </div>
</div>
{% if add_server %} {% if add_server %}
<div class="pure-u-1-3"> <div id="content">
<div >
<div class="server-content-header pure-g"> <div class="server-content-header pure-g">
<div class="pure-u-1-2"> <div >
<h1 class="server-content-title">Add new server</h1> <h1 class="server-content-title">Add new server</h1>
</div> </div>
</div> </div>
<div class="server-content-body"> <div class="server-content-body">
<form action="/add_server" class="pure-form pure-form-stacked" method="POST"> <form action="/add_server" class="pure-form pure-form-stacked" method="POST">
<fieldset> <fieldset>
<div class="pure-g"> <div >
<div class="pure-u-1 pure-u-md-1-3"> <div >
<input type="text" class="pure-u-23-24" name="url" placeholder="Server management URL"/> <input type="text" name="url" placeholder="Server management URL"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div >
<input type="text"class="pure-u-23-24" name="cert" placeholder="Certificate"/> <input type="text" name="cert" placeholder="Certificate"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div >
<input type="text" class="pure-u-23-24" name="comment" placeholder="Comment"/> <input type="text" name="comment" placeholder="Comment"/>
</div> </div>
</div> </div>
<button type="submit" class="pure-button pure-input-1 pure-button-primary">Add</button> <button type="submit" class="pure-button pure-input-1 pure-button-primary">Add</button>
@ -61,9 +64,9 @@
</form> </form>
</div> </div>
</div> </div>
</div>
{% endif %} {% endif %}
{% if SERVERS|length != 0 and not add_server %} {% if SERVERS|length != 0 and not add_server %}
{% if selected_server is none %} {% if selected_server is none %}
@ -71,11 +74,12 @@
{% else %} {% else %}
{% set server = SERVERS[selected_server|int] %} {% set server = SERVERS[selected_server|int] %}
{% endif %} {% endif %}
<div id="main" class="pure-u-1"> <div id="content">
<div id="main" >
<div class="server-content"> <div class="server-content">
<div class="server-content-header pure-g"> <div class="server-content-header pure-g">
<div class="pure-u-1-2"> <div>
<h1 class="server-content-title">{{server.info()["name"]}}</h1> <h1 class="content-title">{{server.info()["name"]}}</h1>
<p class="server-content-subtitle"> <p class="server-content-subtitle">
<span>v.{{server.info()["version"]}} {{server.info()["local_server_id"]}}</span> <span>v.{{server.info()["version"]}} {{server.info()["local_server_id"]}}</span>
</p> </p>
@ -95,35 +99,35 @@
<form class="pure-form pure-form-stacked" method="POST"> <form class="pure-form pure-form-stacked" method="POST">
<fieldset> <fieldset>
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<label for="name">Server Name</br>This will not be exposed to client</label> <label for="name">Server Name</br>This will not be exposed to client</label>
<input type="text" id="name" class="pure-u-23-24" name="name" value="{{server.info()['name']}}"/> <input type="text" id="name" class="" name="name" value="{{server.info()['name']}}"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<label for="comment">Comment</br>This will be exposed to client and will be used as "Server name" in client Outline app</label> <label for="comment">Comment</br>This will be exposed to client and will be used as "Server name" in client Outline app</label>
<input type="text" id="comment" class="pure-u-23-24" name="comment" value="{{server.info()['comment']}}"/> <input type="text" id="comment" class="" name="comment" value="{{server.info()['comment']}}"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1">
<label for="port_for_new_access_keys">Port For New Access Keys</label> <label for="port_for_new_access_keys">Port For New Access Keys</label>
<input type="text" id="port_for_new_access_keys" class="pure-u-23-24" name="port_for_new_access_keys" value="{{server.info()['port_for_new_access_keys']}}"/> <input type="text" id="port_for_new_access_keys" name="port_for_new_access_keys" value="{{server.info()['port_for_new_access_keys']}}"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<label for="hostname_for_access_keys">Hostname For Access Keys</label> <label for="hostname_for_access_keys">Hostname For Access Keys</label>
<input type="text" id="hostname_for_access_keys" class="pure-u-23-24" name="hostname_for_access_keys" value="{{server.info()['hostname_for_access_keys']}}"/> <input type="text" id="hostname_for_access_keys" name="hostname_for_access_keys" value="{{server.info()['hostname_for_access_keys']}}"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<label for="url">Server URL</label> <label for="url">Server URL</label>
<input type="text" readonly id="url" class="pure-u-23-24" name="url" value="{{server.info()['url']}}"/> <input type="text" readonly id="url" name="url" value="{{server.info()['url']}}"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<label for="cert">Server Access Certificate</label> <label for="cert">Server Access Certificate</label>
<input type="text" readonly id="cert" class="pure-u-23-24" name="cert" value="{{server.info()['cert']}}"/> <input type="text" readonly id="cert" name="cert" value="{{server.info()['cert']}}"/>
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 ">
<label for="created_timestamp_ms">Created</label> <label for="created_timestamp_ms">Created</label>
<input type="text" readonly id="created_timestamp_ms" class="pure-u-23-24" name="created_timestamp_ms" value="{{format_timestamp(server.info()['created_timestamp_ms']) }}"/> <input type="text" readonly id="created_timestamp_ms" name="created_timestamp_ms" value="{{format_timestamp(server.info()['created_timestamp_ms']) }}"/>
</div> </div>
<input type="hidden" readonly id="server_id" class="pure-u-23-24" name="server_id" value="{{server.info()['local_server_id']}}"/> <input type="hidden" readonly id="server_id" name="server_id" value="{{server.info()['local_server_id']}}"/>
</div> </div>
<p>Share anonymous metrics</p> <p>Share anonymous metrics</p>
<label for="metrics_enabled" class="pure-radio"> <label for="metrics_enabled" class="pure-radio">
@ -144,6 +148,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
{% endif %} {% endif %}