UI Redesign

UI has been redesigned to use blurs and a default background.

Docker file now puts the database in a separate folder to make mounting
a docker volume easier.

Fixed issues with adding applications without health checks.
This commit is contained in:
Joe Bellus 2022-02-12 21:59:38 -05:00
parent 80732fa1e1
commit c97238958f
10 changed files with 89 additions and 44 deletions

View File

@ -1,6 +1,6 @@
FROM alpine:latest FROM alpine:latest
COPY target/x86_64-unknown-linux-musl/release/vade /app/vade COPY target/x86_64-unknown-linux-musl/release/vade /app/vade
EXPOSE 8088 EXPOSE 8089
WORKDIR app WORKDIR app
RUN touch data.db RUN mkdir data
CMD ["./vade"] CMD ["./vade", "--db data/"]

BIN
public/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -15,7 +15,7 @@ axios.interceptors.request.use(function (config) {
axios.defaults.baseURL = process.env.NODE_ENV === 'development' axios.defaults.baseURL = process.env.NODE_ENV === 'development'
? 'http://localhost:8088' ? 'http://localhost:8089'
: ''; : '';
axios.interceptors.response.use(function (response) { axios.interceptors.response.use(function (response) {

View File

@ -76,15 +76,24 @@ async fn main() {
let st = state.clone(); let st = state.clone();
actix_rt::spawn(async move { actix_rt::spawn(async move {
loop { loop {
tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
let apps: Vec<entity::application::Model> = entity::application::Entity::find() let apps: Vec<entity::application::Model> = entity::application::Entity::find()
.filter(entity::application::Column::EnableHealthcheck.eq(true)) .filter(entity::application::Column::EnableHealthcheck.eq(true))
.all(&st.db) .all(&st.db)
.await .await
.unwrap(); .unwrap();
let client = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap();
for app in apps { for app in apps {
match reqwest::get(app.url).await { match client
.request(reqwest::Method::GET, app.url)
.timeout(std::time::Duration::from_secs(5))
.send()
.await
{
Ok(res) if res.status() == 200 => { Ok(res) if res.status() == 200 => {
st.healthcheck_status.lock().await.insert(app.id, true); st.healthcheck_status.lock().await.insert(app.id, true);
let _ = events::EventBroker::from_registry() let _ = events::EventBroker::from_registry()
@ -94,7 +103,18 @@ async fn main() {
}) })
.await; .await;
} }
_ => { Err(e) => {
tracing::warn!("Error performing healthcheck: {}", e);
st.healthcheck_status.lock().await.insert(app.id, false);
let _ = events::EventBroker::from_registry()
.send(events::Event::HealthcheckChange {
app_id: app.id,
alive: false,
})
.await;
}
Ok(res) => {
tracing::warn!("Non 200 status code: {}", res.status());
st.healthcheck_status.lock().await.insert(app.id, false); st.healthcheck_status.lock().await.insert(app.id, false);
let _ = events::EventBroker::from_registry() let _ = events::EventBroker::from_registry()
.send(events::Event::HealthcheckChange { .send(events::Event::HealthcheckChange {
@ -105,6 +125,7 @@ async fn main() {
} }
} }
} }
tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
} }
}); });
@ -113,9 +134,9 @@ async fn main() {
HttpServer::new(move || { HttpServer::new(move || {
let cors = actix_cors::Cors::permissive(); let cors = actix_cors::Cors::permissive();
App::new() App::new()
.wrap(cors)
.app_data(state.clone()) .app_data(state.clone())
.wrap(tracing_actix_web::TracingLogger::default()) .wrap(tracing_actix_web::TracingLogger::default())
.wrap(cors)
.service(web::resource("/events/{token}").to(socket_session::event_session_index)) .service(web::resource("/events/{token}").to(socket_session::event_session_index))
.service(api::routes()) .service(api::routes())
.service(dist) .service(dist)

View File

@ -83,6 +83,7 @@ export default {
} else { } else {
let resp = await axios.post("/api/applications", { let resp = await axios.post("/api/applications", {
active: true, active: true,
enableHealthcheck: !!this.app.enableHealthcheck,
...this.app, ...this.app,
}); });
if (resp.status == 200) { if (resp.status == 200) {

View File

@ -33,7 +33,7 @@ export default {
} }
.tile:hover { .tile:hover {
transition: all 0.1s; transition: all 0.1s;
background-color: #333; background-color: rgba(50,50,50, 0.5);
box-shadow: rgba(0, 0, 0, 0.5) 0px 20px 30px -10px; box-shadow: rgba(0, 0, 0, 0.5) 0px 20px 30px -10px;
} }
.label { .label {

View File

@ -19,6 +19,8 @@ export default {
<style> <style>
.bookmark-tile { .bookmark-tile {
padding: 5px 25px; padding: 5px 25px;
border-radius: 5px;
transition: all 0.2s;
} }
.bookmark-tile, .bookmark-tile svg { .bookmark-tile, .bookmark-tile svg {
@ -28,6 +30,8 @@ export default {
} }
.bookmark-tile:hover, .bookmark-tile:hover svg { .bookmark-tile:hover, .bookmark-tile:hover svg {
transition: all 0.2s;
background: rgba(0,0,0,0.25);
color: #fff; color: #fff;
} }

View File

@ -62,6 +62,7 @@ export default {
left: 50%; left: 50%;
transform: translate(-50%, 0%); transform: translate(-50%, 0%);
display: none; display: none;
z-index: 100;
} }
.modal.open { .modal.open {

View File

@ -15,6 +15,6 @@ export default {
<style scoped> <style scoped>
.tile.new { .tile.new {
background: #444; background: rgba(0,0,0,0.8);
} }
</style> </style>

View File

@ -16,6 +16,50 @@
:appData="app" :appData="app"
:key="app.id" :key="app.id"
/> />
</div>
</div>
<div class="container" v-for="cat in applicationCategories" :key="cat.id">
<h1 @click="appCatClicked(cat)" class="editable">
<font-awesome-icon v-if="cat.glyph" :icon="cat.glyph" />
{{cat.categoryName}}
</h1>
<div class="app-tiles">
<application-tile
@clicked="appTileClicked"
v-for="app in appsForCategory(cat.id)"
:appData="app"
:key="app.id"
/>
</div>
</div>
<div class="container" v-if="bookmarks.length">
<h1 class="bookmark">BOOKMARKS</h1>
<div class="bookmark-container">
<div class="bookmark-category" v-if="bookmarksForCategory(null).length">
<h2>UNCATEGORZED</h2>
<bookmark-tile
v-for="bm in bookmarksForCategory(null)"
:key="bm.id"
:bookmark="bm"
@clicked="bookmarkClicked"
/>
</div>
<div class="bookmark-category" v-for="cat in bookmarkCategories" :key="cat.id">
<h2 @click="bookmarkCatClicked(cat)" class="editable">
<font-awesome-icon v-if="cat.glyph" :icon="cat.glyph" />
{{cat.categoryName}}
</h2>
<bookmark-tile
@clicked="bookmarkClicked"
v-for="bm in bookmarksForCategory(cat.id)"
:key="bm.id"
:bookmark="bm"
/>
</div>
</div>
</div>
<edit-mode-button :editMode="editMode" @click="toggleEdit" />
<application-modal <application-modal
:open="appOpen" :open="appOpen"
:data="editApp" :data="editApp"
@ -43,51 +87,6 @@
@update="reload" @update="reload"
/> />
</div> </div>
</div>
<div class="container" v-for="cat in applicationCategories" :key="cat.id">
<h1 @click="appCatClicked(cat)" class="editable">
<font-awesome-icon v-if="cat.glyph" :icon="cat.glyph" />
{{cat.categoryName}}
</h1>
<div class="app-tiles">
<application-tile
@clicked="appTileClicked"
v-for="app in appsForCategory(cat.id)"
:appData="app"
:key="app.id"
/>
</div>
</div>
<div class="container" v-if="bookmarks.length">
<h1 class="bookmark">BOOKMARKS</h1>
<div class="bookmark-container">
<div class="bookmark-category">
<h2>UNCATEGORZED</h2>
<bookmark-tile
v-for="bm in bookmarksForCategory(null)"
:key="bm.id"
:bookmark="bm"
@clicked="bookmarkClicked"
/>
</div>
<div class="bookmark-category" v-for="cat in bookmarkCategories" :key="cat.id">
<h2 @click="bookmarkCatClicked(cat)" class="editable">
<font-awesome-icon v-if="cat.glyph" :icon="cat.glyph" />
{{cat.categoryName}}
</h2>
<bookmark-tile
@clicked="bookmarkClicked"
v-for="bm in bookmarksForCategory(cat.id)"
:key="bm.id"
:bookmark="bm"
/>
</div>
</div>
</div>
<edit-mode-button :editMode="editMode" @click="toggleEdit" />
</div>
</template> </template>
<script> <script>
@ -252,16 +251,22 @@ h1 {
font-weight: bold; font-weight: bold;
line-height: 1; line-height: 1;
margin-bottom: 5px; margin-bottom: 5px;
padding-left: 25px; font-size: 18px;
font-size: 24px; padding-bottom: 10px;
color: rgba(255,255,255,0.25);
text-shadow: 1px 1px rgba(0,0,0,0.25);
text-transform: uppercase;
text-align: right;
} }
h2 { h2 {
color: #fff;
font-size: 18px; font-size: 18px;
text-transform: uppercase; text-transform: uppercase;
padding-left: 25px; padding-left: 25px;
font-weight: bold; font-weight: bold;
margin-bottom: 5px; margin-bottom: 5px;
color: rgba(255,255,255,0.25);
text-shadow: 1px 1px rgba(0,0,0,0.25);
} }
.edit-mode .editable { .edit-mode .editable {
cursor: pointer; cursor: pointer;
@ -275,12 +280,25 @@ h2 svg {
grid-template-columns: repeat(4, 25%); grid-template-columns: repeat(4, 25%);
} }
.container { .container {
padding: 10px 50px; padding: 25px;
width: 1000px; width: 1000px;
margin: auto auto; margin: 20px auto;
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
background-color: rgba(0,0,0,0.4);
border-radius: 15px;
}
.container svg {
color: rgba(255,255,255,0.25);
} }
.dashboard { .dashboard {
margin-top: 150px; user-select: none;
min-height: calc(100vh - 150px);
min-width: 100vw;
padding-top: 150px;
background: url('/bg.jpg');
background-position: center center;
background-size: cover;
} }
.editmode-tiles { .editmode-tiles {
display: flex; display: flex;