Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77d6a22122 | ||
|
|
40f97073e8 | ||
|
|
75339ffba1 | ||
|
|
2fdfba5a42 | ||
|
|
5987330cdc | ||
|
|
14c7c21f9f | ||
|
|
fc9fdaf8b6 | ||
|
|
5979a6d0e5 | ||
|
|
ca2c46ffce | ||
|
|
1cc7e92466 | ||
|
|
cfc3e81820 | ||
|
|
67ab2ab170 | ||
|
|
e1ce378421 | ||
|
|
f083ea028d | ||
|
|
063a82198c | ||
|
|
d03c3440de | ||
|
|
f7b28ad1e0 | ||
|
|
52a95757ce | ||
|
|
efc725dadc | ||
|
|
ff8c539829 | ||
|
|
875e17717e | ||
|
|
929f8c19f8 | ||
|
|
bdcc856071 | ||
|
|
2325881bd8 | ||
|
|
067fea2b7a | ||
|
|
8ac689ca57 | ||
|
|
5e9ffe7fcf | ||
|
|
3b3ba92d27 | ||
|
|
df2834fd81 | ||
|
|
2ecfefb35f | ||
|
|
d18d3f800b | ||
|
|
9d7fd4eaf0 | ||
|
|
032ebfd307 | ||
|
|
7f74a0f551 |
19
.editorconfig
Normal file
19
.editorconfig
Normal file
@@ -0,0 +1,19 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 120
|
||||
|
||||
[*.go]
|
||||
indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[package.json]
|
||||
indent_size = 1
|
||||
@@ -1,5 +1,6 @@
|
||||
before:
|
||||
hooks:
|
||||
- npm run clean
|
||||
- npm run build
|
||||
- packr
|
||||
builds:
|
||||
|
||||
3
.prettierrc.json
Normal file
3
.prettierrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"printWidth": 120
|
||||
}
|
||||
22
README.md
22
README.md
@@ -4,13 +4,13 @@ Dozzle is a log viewer for Docker. It's free. It's small. And it's right in your
|
||||
|
||||
While dozzle should work for most, it is not meant to be a full logging solution. For enterprise use, I recommend you look at [Loggly](https://www.loggly.com), [Papertrail](https://papertrailapp.com) or [Kibana](https://www.elastic.co/products/kibana).
|
||||
|
||||
But if you don't want to pay for those service, then you are in luck! Dozzle will be able capture all logs from your containers and send them in real-time to your browser. Installation is also very easy.
|
||||
But if you don't want to pay for those services, then you are in luck! Dozzle will be able to capture all logs from your containers and send them in real-time to your browser. Installation is also very easy.
|
||||
|
||||

|
||||
|
||||
## Getting dozzle
|
||||
|
||||
Dozzle is a very small Docker container (13.3MB virtual). Pull the latest release from the index:
|
||||
Dozzle is a very small Docker container (4 MB compressed). Pull the latest release from the index:
|
||||
|
||||
$ docker pull amir20/dozzle:latest
|
||||
|
||||
@@ -24,11 +24,21 @@ dozzle will be available at [http://localhost:8888/](http://localhost:8888/). Yo
|
||||
|
||||
#### Security
|
||||
|
||||
dozzle doesn't support authentication out of the box. You can control the device dozzle binds to by passing `-addr` parameter. For example,
|
||||
dozzle doesn't support authentication out of the box. You can control the device dozzle binds to by passing `--addr` parameter. For example,
|
||||
|
||||
$ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8888:1224 amir20/dozzle:latest -addr localhost:1224
|
||||
$ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8888:1224 amir20/dozzle:latest --addr localhost:1224
|
||||
|
||||
will bind to `localhost` on port `1224`. You can then use a reverse proxy to control who can see dozzle.
|
||||
|
||||
#### Changing base URL
|
||||
|
||||
dozzle by default mounts to "/". If you want to control the base path you can use the `--base` option. For example, if you want to mount at "/foobar",
|
||||
then you can override by using `--base /foobar`.
|
||||
|
||||
$ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8888:8080 amir20/dozzle:latest --base /foobar
|
||||
|
||||
dozzle will be available at [http://localhost:8080/foobar/](http://localhost:8080/foobar/).
|
||||
|
||||
will bind to `localhost` on port `1224`. You can then use use reverse proxy to control who can see dozzle.
|
||||
|
||||
#### Environment variable, DOCKER_API_VERSION
|
||||
|
||||
@@ -36,7 +46,7 @@ If you see
|
||||
|
||||
2018/10/31 08:53:17 Error response from daemon: client version 1.40 is too new. Maximum supported API version is 1.38
|
||||
|
||||
Then you need to modify `DOCKER_API_VERSION` to let dozzle know which version of the API is supported. By default, `DOCKER_API_VERSION=1.38` and you can change it to by passing `-e` flag. For example, this would change the `DOCKER_API_VERSION` to `1.20`
|
||||
Then you need to modify `DOCKER_API_VERSION` to let dozzle know which version of the API is supported. By default, `DOCKER_API_VERSION=1.38` and you can change it by passing `-e` flag. For example, this would change the `DOCKER_API_VERSION` to `1.20`
|
||||
|
||||
$ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -e DOCKER_API_VERSION=1.20 -p 8888:8080 amir20/dozzle:latest
|
||||
|
||||
|
||||
@@ -1,15 +1,89 @@
|
||||
<template lang="html">
|
||||
<router-view></router-view>
|
||||
<div class="columns is-marginless">
|
||||
<aside class="column menu is-2">
|
||||
<a
|
||||
role="button"
|
||||
class="navbar-burger burger is-white is-hidden-tablet is-pulled-right"
|
||||
@click="showNav = !showNav;"
|
||||
:class="{ 'is-active': showNav }"
|
||||
>
|
||||
<span></span> <span></span> <span></span>
|
||||
</a>
|
||||
<h1 class="title has-text-warning is-marginless">Dozzle</h1>
|
||||
<p class="menu-label is-hidden-mobile" :class="{ 'is-active': showNav }">Containers</p>
|
||||
<ul class="menu-list is-hidden-mobile" :class="{ 'is-active': showNav }">
|
||||
<li v-for="item in containers">
|
||||
<router-link
|
||||
:to="{ name: 'container', params: { id: item.Id } }"
|
||||
active-class="is-active"
|
||||
class="tooltip is-tooltip-right is-tooltip-info"
|
||||
:data-tooltip="item.Names[0]"
|
||||
>
|
||||
<div class="hide-overflow">{{ item.Names[0] }}</div>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
<div class="column is-offset-2"><router-view></router-view></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "App"
|
||||
name: "App",
|
||||
data() {
|
||||
return {
|
||||
containers: [],
|
||||
showNav: false
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.containers = await (await fetch(`${BASE_PATH}/api/containers.json`)).json();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.section.is-fullwidth {
|
||||
padding: 0 !important;
|
||||
<style scoped lang="scss">
|
||||
.is-hidden-mobile.is-active {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.navbar-burger {
|
||||
height: 2.35rem;
|
||||
}
|
||||
|
||||
aside {
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
padding: 1em;
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
& {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.tooltip::after,
|
||||
.tooltip::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hide-overflow {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.burger.is-white {
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
71
assets/components/ScrollbarNotification.vue
Normal file
71
assets/components/ScrollbarNotification.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template lang="html">
|
||||
<transition name="fade">
|
||||
<button
|
||||
class="button scroll-notification"
|
||||
:class="hasNew ? 'is-warning' : 'is-primary'"
|
||||
@click="scrollToBottom"
|
||||
v-show="visible"
|
||||
>
|
||||
<span class="icon large"> <i class="fas fa-chevron-down"></i> </span>
|
||||
</button>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ["messages"],
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
hasNew: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("scroll", this.onScroll, { passive: true });
|
||||
setTimeout(() => this.scrollToBottom(), 500);
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("scroll", this.onScroll);
|
||||
},
|
||||
methods: {
|
||||
scrollToBottom() {
|
||||
this.visible = false;
|
||||
window.scrollTo(0, document.documentElement.scrollHeight || document.body.scrollHeight);
|
||||
},
|
||||
onScroll() {
|
||||
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
const scrollBottom =
|
||||
(document.documentElement.scrollHeight || document.body.scrollHeight) - document.documentElement.clientHeight;
|
||||
const diff = Math.abs(scrollTop - scrollBottom);
|
||||
this.visible = diff > 50;
|
||||
if (!this.visible) {
|
||||
this.hasNew = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
messages(newValue, oldValue) {
|
||||
if (this.visible) {
|
||||
this.hasNew = true;
|
||||
} else {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.scroll-notification {
|
||||
position: fixed;
|
||||
right: 40px;
|
||||
bottom: 30px;
|
||||
}
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.15s ease-in;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,19 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Dozzle</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{{ .Hostname }} - Dozzle</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono|Gafata" rel="stylesheet" />
|
||||
<link href="styles.scss" rel="stylesheet" />
|
||||
<script>
|
||||
window["BASE_PATH"] = "{{ .Base }}";
|
||||
window["SSL_ENABLED"] = "{{ .SSL }}".toLowerCase() === "true";
|
||||
</script>
|
||||
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<section class="section is-fullwidth">
|
||||
<div id="app"></div>
|
||||
</section>
|
||||
<script src="/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body class="is-dark">
|
||||
<div id="app"></div>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import Vue from "vue";
|
||||
import VueRouter from "vue-router";
|
||||
import App from "./App.vue";
|
||||
import Index from "./pages/Index.vue";
|
||||
import Container from "./pages/Container.vue";
|
||||
import Index from "./pages/Index.vue";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const routes = [
|
||||
{ path: "/", component: Index },
|
||||
{
|
||||
path: "/",
|
||||
component: Index,
|
||||
name: "default"
|
||||
},
|
||||
{
|
||||
path: "/container/:id",
|
||||
component: Container,
|
||||
@@ -18,6 +22,7 @@ const routes = [
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: "history",
|
||||
base: BASE_PATH + "/",
|
||||
routes
|
||||
});
|
||||
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
<template lang="html">
|
||||
<ul ref="events" class="events">
|
||||
<li v-for="item in messages" class="event">
|
||||
<span class="date">{{ item.dateRelative }}</span>
|
||||
<span class="text">{{ item.message }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="is-fullheight">
|
||||
<ul ref="events" class="events">
|
||||
<li v-for="item in messages" class="event" :key="item.key">
|
||||
<span class="date">{{ item.dateRelative }}</span> <span class="text">{{ item.message }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<scrollbar-notification :messages="messages"></scrollbar-notification>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatRelative } from "date-fns";
|
||||
import ScrollbarNotification from "../components/ScrollbarNotification";
|
||||
|
||||
let ws = null;
|
||||
let nextId = 0;
|
||||
const parseMessage = data => {
|
||||
const date = new Date(data.substring(0, 30));
|
||||
const dateRelative = formatRelative(date, new Date());
|
||||
const message = data.substring(30);
|
||||
const id = nextId++;
|
||||
const key = nextId++;
|
||||
return {
|
||||
id,
|
||||
key,
|
||||
date,
|
||||
dateRelative,
|
||||
message
|
||||
@@ -27,43 +31,57 @@ const parseMessage = data => {
|
||||
export default {
|
||||
props: ["id"],
|
||||
name: "Container",
|
||||
components: {
|
||||
ScrollbarNotification
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
messages: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
ws = new WebSocket(`ws://${window.location.host}/api/logs?id=${this.id}`);
|
||||
ws.onopen = e => console.log("Connection opened.");
|
||||
ws.onclose = e => console.log("Connection closed.");
|
||||
ws.onerror = e => console.error("Connection error: " + e.data);
|
||||
ws.onmessage = e => {
|
||||
const message = parseMessage(e.data);
|
||||
this.messages.push(message);
|
||||
|
||||
this.$nextTick(() =>
|
||||
document.querySelector("li.event:last-child").scrollIntoView()
|
||||
);
|
||||
};
|
||||
created() {
|
||||
this.loadLogs(this.id);
|
||||
},
|
||||
beforeDestroy() {
|
||||
ws.close();
|
||||
ws = null;
|
||||
},
|
||||
watch: {
|
||||
id(newValue, oldValue) {
|
||||
if (oldValue !== newValue) {
|
||||
this.loadLogs(newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadLogs(id) {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
this.messages = [];
|
||||
}
|
||||
const protocol = SSL_ENABLED ? "wss" : "ws";
|
||||
ws = new WebSocket(`${protocol}://${window.location.host}${BASE_PATH}/api/logs?id=${this.id}`);
|
||||
ws.onopen = e => console.log("Connection opened.");
|
||||
ws.onclose = e => console.log("Connection closed.");
|
||||
ws.onerror = e => console.error("Connection error: " + e.data);
|
||||
ws.onmessage = e => {
|
||||
const message = parseMessage(e.data);
|
||||
this.messages.push(message);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
<style scoped>
|
||||
.events {
|
||||
color: #ddd;
|
||||
background-color: #111;
|
||||
padding: 10px;
|
||||
font-family: "Roboto Mono", monaco, monospace;
|
||||
}
|
||||
|
||||
.event {
|
||||
font-family: monaco, monospace;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
padding: 0 15px 0 30px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
@@ -71,4 +89,8 @@ export default {
|
||||
background-color: #262626;
|
||||
color: #258ccd;
|
||||
}
|
||||
|
||||
.is-fullheight {
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,60 +1,22 @@
|
||||
<template lang="html">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<section class="section">
|
||||
<ul class="is-marginless is-paddless">
|
||||
<li v-for="item in containers" class="unstyled box">
|
||||
<router-link
|
||||
:to="{ name: 'container', params: { id: item.Id } }"
|
||||
class="columns"
|
||||
>
|
||||
<div class="column is-6">
|
||||
<h2 class="is-2 hide-overflow">{{ item.Names[0] }}</h2>
|
||||
<span class="subtitle is-6 code hide-overflow">{{
|
||||
item.Command
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<span class="code hide-overflow">{{ item.Image }}</span>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<span class="subtitle is-7">{{ item.Status }}</span>
|
||||
</div>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<div class="hero is-fullheight is-dark">
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<h1 class="title">Please choose a container from the list to view the logs</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Index",
|
||||
data() {
|
||||
return {
|
||||
containers: []
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.containers = await (await fetch(`/api/containers.json`)).json();
|
||||
}
|
||||
props: [],
|
||||
name: "Default"
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.hide-overflow {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
.code {
|
||||
background-color: #f5f5f5;
|
||||
color: #ff3860;
|
||||
font-size: 0.875em;
|
||||
font-weight: 400;
|
||||
padding: 0.25em 0.5em 0.25em;
|
||||
display: block;
|
||||
border-radius: 2px;
|
||||
<style scoped>
|
||||
.hero.is-dark {
|
||||
color: #ddd;
|
||||
background-color: #111;
|
||||
}
|
||||
</style>
|
||||
|
||||
20
assets/styles.scss
Normal file
20
assets/styles.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
@charset "utf-8";
|
||||
|
||||
$menu-item-active-background-color: hsl(171, 100%, 41%);
|
||||
$menu-item-color: hsl(0, 6%, 87%);
|
||||
|
||||
@import "../node_modules/bulma/bulma.sass";
|
||||
@import "../node_modules/bulma-tooltip/src/sass";
|
||||
|
||||
.is-dark {
|
||||
color: #ddd;
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
|
||||
h1.title {
|
||||
font-family: "Gafata", sans-serif;
|
||||
}
|
||||
BIN
demo.gif
BIN
demo.gif
Binary file not shown.
|
Before Width: | Height: | Size: 41 MiB After Width: | Height: | Size: 24 MiB |
197
main.go
197
main.go
@@ -1,103 +1,146 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/gobuffalo/packr"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/gobuffalo/packr"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
var (
|
||||
cli *client.Client
|
||||
addr = flag.String("addr", ":8080", "http service address")
|
||||
upgrader = websocket.Upgrader{}
|
||||
version = "dev"
|
||||
commit = "none"
|
||||
date = "unknown"
|
||||
cli *client.Client
|
||||
addr = ""
|
||||
ssl = false
|
||||
base = "/"
|
||||
upgrader = websocket.Upgrader{}
|
||||
version = "dev"
|
||||
commit = "none"
|
||||
date = "unknown"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
cli, err = client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
flag.Parse()
|
||||
flag.StringVar(&addr, "addr", ":8080", "http service address")
|
||||
flag.StringVar(&base, "base", "/", "base address of the application to mount")
|
||||
flag.BoolVarP(&ssl, "ssl", "s", false, "Uses websockets over ssl if enabled")
|
||||
|
||||
var err error
|
||||
cli, err = client.NewClientWithOpts(client.FromEnv)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
box := packr.NewBox("./static")
|
||||
http.HandleFunc("/api/containers.json", listContainers)
|
||||
http.HandleFunc("/api/logs", logs)
|
||||
http.HandleFunc("/version", versionHandler)
|
||||
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
fileServer := http.FileServer(box)
|
||||
if box.Has(req.URL.Path) {
|
||||
fileServer.ServeHTTP(w, req)
|
||||
} else {
|
||||
bytes, _ := box.Find("index.html")
|
||||
w.Write(bytes)
|
||||
}
|
||||
}))
|
||||
r := mux.NewRouter()
|
||||
|
||||
log.Fatal(http.ListenAndServe(*addr, nil))
|
||||
if base != "/" {
|
||||
r.HandleFunc(base, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(w, req, base+"/", http.StatusMovedPermanently)
|
||||
}))
|
||||
}
|
||||
|
||||
s := r.PathPrefix(base).Subrouter()
|
||||
box := packr.NewBox("./static")
|
||||
|
||||
s.HandleFunc("/api/containers.json", listContainers)
|
||||
s.HandleFunc("/api/logs", logs)
|
||||
s.HandleFunc("/version", versionHandler)
|
||||
s.PathPrefix("/").Handler(http.StripPrefix(base, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
fileServer := http.FileServer(box)
|
||||
if box.Has(req.URL.Path) && req.URL.Path != "" && req.URL.Path != "/" {
|
||||
fileServer.ServeHTTP(w, req)
|
||||
} else {
|
||||
handleIndex(box, w)
|
||||
}
|
||||
})))
|
||||
|
||||
log.Fatal(http.ListenAndServe(addr, r))
|
||||
}
|
||||
|
||||
func versionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, version)
|
||||
fmt.Fprintln(w, commit)
|
||||
fmt.Fprintln(w, date)
|
||||
fmt.Fprintln(w, version)
|
||||
fmt.Fprintln(w, commit)
|
||||
fmt.Fprintln(w, date)
|
||||
}
|
||||
|
||||
func listContainers(w http.ResponseWriter, r *http.Request) {
|
||||
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
json.NewEncoder(w).Encode(containers)
|
||||
containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
json.NewEncoder(w).Encode(containers)
|
||||
}
|
||||
|
||||
func handleIndex(box packr.Box, w http.ResponseWriter) {
|
||||
text, _ := box.FindString("index.html")
|
||||
text = strings.Replace(text, "__BASE__", "{{ .Base }}", -1)
|
||||
tmpl, err := template.New("index.html").Parse(text)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
path := ""
|
||||
if base != "/" {
|
||||
path = base
|
||||
}
|
||||
hostname, _ := os.Hostname()
|
||||
data := struct {
|
||||
Base string
|
||||
SSL bool
|
||||
Hostname string
|
||||
}{path, ssl, hostname}
|
||||
err = tmpl.Execute(w, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func logs(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.URL.Query().Get("id")
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
id := r.URL.Query().Get("id")
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "500", Timestamps: true}
|
||||
reader, err := cli.ContainerLogs(context.Background(), id, options)
|
||||
defer reader.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300", Timestamps: true}
|
||||
reader, err := cli.ContainerLogs(context.Background(), id, options)
|
||||
defer reader.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
hdr := make([]byte, 8)
|
||||
content := make([]byte, 1024, 1024*1024)
|
||||
for {
|
||||
_, err := reader.Read(hdr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
count := binary.BigEndian.Uint32(hdr[4:])
|
||||
n, err := reader.Read(content[:count])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
err = c.WriteMessage(websocket.TextMessage, content[:n])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
}
|
||||
hdr := make([]byte, 8)
|
||||
content := make([]byte, 1024, 1024*1024)
|
||||
for {
|
||||
_, err := reader.Read(hdr)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
count := binary.BigEndian.Uint32(hdr[4:])
|
||||
n, err := reader.Read(content[:count])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
err = c.WriteMessage(websocket.TextMessage, content[:n])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
272
package-lock.json
generated
272
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dozzle",
|
||||
"version": "1.0.12",
|
||||
"version": "1.2.5",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1130,6 +1130,58 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@iamstarkov/listr-update-renderer": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz",
|
||||
"integrity": "sha512-IJyxQWsYDEkf8C8QthBn5N8tIUR9V9je6j3sMIpAkonaadjbvxmRC6RAhpa3RKxndhNnU2M6iNbtJwd7usQYIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^1.1.3",
|
||||
"cli-truncate": "^0.2.1",
|
||||
"elegant-spinner": "^1.0.1",
|
||||
"figures": "^1.7.0",
|
||||
"indent-string": "^3.0.0",
|
||||
"log-symbols": "^1.0.2",
|
||||
"log-update": "^2.3.0",
|
||||
"strip-ansi": "^3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^2.2.1",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"has-ansi": "^2.0.0",
|
||||
"strip-ansi": "^3.0.0",
|
||||
"supports-color": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"log-symbols": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
|
||||
"integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mrmlnc/readdir-enhanced": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
|
||||
@@ -1212,7 +1264,7 @@
|
||||
},
|
||||
"ansi-escapes": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
|
||||
"integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
|
||||
"dev": true
|
||||
},
|
||||
@@ -1766,6 +1818,16 @@
|
||||
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
|
||||
"dev": true
|
||||
},
|
||||
"bulma": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.2.tgz",
|
||||
"integrity": "sha512-6JHEu8U/1xsyOst/El5ImLcZIiE2JFXgvrz8GGWbnDLwTNRPJzdAM0aoUM1Ns0avALcVb6KZz9NhzmU53dGDcQ=="
|
||||
},
|
||||
"bulma-tooltip": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bulma-tooltip/-/bulma-tooltip-2.0.2.tgz",
|
||||
"integrity": "sha512-xsqWeWV7tsUn3uH04SqJeP7/CyC1RaDVIyVzr4/sIO3friIIOi7L6jc5g7qUwDxuBQl72yH/yRPuefpXoQ4hWg=="
|
||||
},
|
||||
"cache-base": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
|
||||
@@ -1790,9 +1852,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
|
||||
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
|
||||
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-api": {
|
||||
@@ -2168,16 +2230,16 @@
|
||||
}
|
||||
},
|
||||
"concurrently": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.0.1.tgz",
|
||||
"integrity": "sha512-D8UI+mlI/bfvrA57SeKOht6sEpb01dKk+8Yee4fbnkk1Ue8r3S+JXoEdFZIpzQlXJGtnxo47Wvvg/kG4ba3U6Q==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.0.tgz",
|
||||
"integrity": "sha512-pwzXCE7qtOB346LyO9eFWpkFJVO3JQZ/qU/feGeaAHiX1M3Rw3zgXKc5cZ8vSH5DGygkjzLFDzA/pwoQDkRNGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.1",
|
||||
"date-fns": "^1.23.0",
|
||||
"lodash": "^4.17.10",
|
||||
"read-pkg": "^4.0.1",
|
||||
"rxjs": "6.2.2",
|
||||
"rxjs": "^6.3.3",
|
||||
"spawn-command": "^0.0.2-1",
|
||||
"supports-color": "^4.5.0",
|
||||
"tree-kill": "^1.1.0",
|
||||
@@ -2593,8 +2655,7 @@
|
||||
"date-fns": {
|
||||
"version": "2.0.0-alpha.25",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.0.0-alpha.25.tgz",
|
||||
"integrity": "sha512-iQzJkHF0L4wah9Ae9PkvwemwFz6qmRLuNZcghmvf2t+ptLs1qXzONLiGtjmPQzL6+JpC01JjlTopY2AEy4NFAg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-iQzJkHF0L4wah9Ae9PkvwemwFz6qmRLuNZcghmvf2t+ptLs1qXzONLiGtjmPQzL6+JpC01JjlTopY2AEy4NFAg=="
|
||||
},
|
||||
"date-now": {
|
||||
"version": "0.1.4",
|
||||
@@ -3046,12 +3107,6 @@
|
||||
"strip-eof": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"exit-hook": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
|
||||
"integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=",
|
||||
"dev": true
|
||||
},
|
||||
"expand-brackets": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
|
||||
@@ -3946,7 +4001,7 @@
|
||||
},
|
||||
"globby": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
|
||||
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -4593,9 +4648,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"husky": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-1.1.4.tgz",
|
||||
"integrity": "sha512-cZjGpS7qsaBSo3fOMUuR7erQloX3l5XzL1v/RkIqU6zrQImDdU70z5Re9fGDp7+kbYlM2EtS4aYMlahBeiCUGw==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-1.2.0.tgz",
|
||||
"integrity": "sha512-/ib3+iycykXC0tYIxsyqierikVa9DA2DrT32UEirqNEFVqOj1bFMTgP3jAz8HM7FgC/C8pc/BTUa9MV2GEkZaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cosmiconfig": "^5.0.6",
|
||||
@@ -5140,14 +5195,15 @@
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.0.4.tgz",
|
||||
"integrity": "sha512-Rs0VxXoyFqHMrPQgKAMy+O907+m5Po71UVPhBi7BUBwU7ZZ2aoc+mZmpOX3DVPCoTcy6+hqJa9yIZfacNpJHdg==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-8.1.0.tgz",
|
||||
"integrity": "sha512-yfSkyJy7EuVsaoxtUSEhrD81spdJOe/gMTGea3XaV7HyoRhTb9Gdlp6/JppRZERvKSEYXP9bjcmq6CA5oL2lYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@iamstarkov/listr-update-renderer": "0.4.1",
|
||||
"chalk": "^2.3.1",
|
||||
"commander": "^2.14.1",
|
||||
"cosmiconfig": "^5.0.2",
|
||||
"cosmiconfig": "5.0.6",
|
||||
"debug": "^3.1.0",
|
||||
"dedent": "^0.7.0",
|
||||
"del": "^3.0.0",
|
||||
@@ -5158,7 +5214,6 @@
|
||||
"is-windows": "^1.0.2",
|
||||
"jest-validate": "^23.5.0",
|
||||
"listr": "^0.14.2",
|
||||
"listr-update-renderer": "https://github.com/okonet/listr-update-renderer/tarball/upgrade-log-update",
|
||||
"lodash": "^4.17.5",
|
||||
"log-symbols": "^2.2.0",
|
||||
"micromatch": "^3.1.8",
|
||||
@@ -5214,9 +5269,9 @@
|
||||
}
|
||||
},
|
||||
"listr": {
|
||||
"version": "0.14.2",
|
||||
"resolved": "https://registry.npmjs.org/listr/-/listr-0.14.2.tgz",
|
||||
"integrity": "sha512-vmaNJ1KlGuGWShHI35X/F8r9xxS0VTHh9GejVXwSN20fG5xpq3Jh4bJbnumoT6q5EDM/8/YP1z3YMtQbFmhuXw==",
|
||||
"version": "0.14.3",
|
||||
"resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz",
|
||||
"integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@samverschueren/stream-to-observable": "^0.3.0",
|
||||
@@ -5224,10 +5279,18 @@
|
||||
"is-promise": "^2.1.0",
|
||||
"is-stream": "^1.1.0",
|
||||
"listr-silent-renderer": "^1.1.1",
|
||||
"listr-update-renderer": "^0.4.0",
|
||||
"listr-verbose-renderer": "^0.4.0",
|
||||
"p-map": "^1.1.1",
|
||||
"rxjs": "^6.1.0"
|
||||
"listr-update-renderer": "^0.5.0",
|
||||
"listr-verbose-renderer": "^0.5.0",
|
||||
"p-map": "^2.0.0",
|
||||
"rxjs": "^6.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"p-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-2.0.0.tgz",
|
||||
"integrity": "sha512-GO107XdrSUmtHxVoi60qc9tUl/KkNKm+X2CF4P9amalpGxv5YqVPJNfSb0wcA+syCopkZvYYIzW8OVTQW59x/w==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"listr-silent-renderer": {
|
||||
@@ -5237,8 +5300,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"listr-update-renderer": {
|
||||
"version": "https://github.com/okonet/listr-update-renderer/tarball/upgrade-log-update",
|
||||
"integrity": "sha512-YF5bCQPbpiVDh/Ali3O5gmBYnvmNIcNZKBq0hueOqYum8T/+VR1gCLgLXmRs2OpPsVAzdsENQO0BJCyFt9FjKA==",
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz",
|
||||
"integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^1.1.3",
|
||||
@@ -5288,72 +5352,31 @@
|
||||
}
|
||||
},
|
||||
"listr-verbose-renderer": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz",
|
||||
"integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=",
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz",
|
||||
"integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^1.1.3",
|
||||
"cli-cursor": "^1.0.2",
|
||||
"chalk": "^2.4.1",
|
||||
"cli-cursor": "^2.1.0",
|
||||
"date-fns": "^1.27.2",
|
||||
"figures": "^1.7.0"
|
||||
"figures": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^2.2.1",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"has-ansi": "^2.0.0",
|
||||
"strip-ansi": "^3.0.0",
|
||||
"supports-color": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"cli-cursor": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
|
||||
"integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"restore-cursor": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"date-fns": {
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz",
|
||||
"integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==",
|
||||
"dev": true
|
||||
},
|
||||
"onetime": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
|
||||
"integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
|
||||
"dev": true
|
||||
},
|
||||
"restore-cursor": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
|
||||
"integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
|
||||
"figures": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
|
||||
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"exit-hook": "^1.0.0",
|
||||
"onetime": "^1.0.0"
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5473,9 +5496,9 @@
|
||||
}
|
||||
},
|
||||
"map-age-cleaner": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz",
|
||||
"integrity": "sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==",
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
|
||||
"integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-defer": "^1.0.0"
|
||||
@@ -7999,9 +8022,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz",
|
||||
"integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==",
|
||||
"version": "6.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
|
||||
"integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
@@ -8031,6 +8054,15 @@
|
||||
"clones": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.15.1.tgz",
|
||||
"integrity": "sha512-WiDa5BsePB/rQEbh/Fv2pVDUCasxuRYjW7GsWx8Ld23LY61vx1VV5Mzf/7mu5kLWKMryMqo65fzYL34HgaM47w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chokidar": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
@@ -8177,9 +8209,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"simple-git": {
|
||||
"version": "1.106.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.106.0.tgz",
|
||||
"integrity": "sha512-LaxKq4X9Om7bb16Cpinc36hT1YLHMM9KDQMSWJVv4Y1TGDEUuZbs+0lAk2JSKkCEO3xFjcMSx5OjvZo+i4eJvQ==",
|
||||
"version": "1.107.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.107.0.tgz",
|
||||
"integrity": "sha512-t4OK1JRlp4ayKRfcW6owrWcRVLyHRUlhGd0uN6ZZTqfDq8a5XpcUdOKiGRNobHEuMtNqzp0vcJNvhYWwh5PsQA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.0.1"
|
||||
@@ -8822,9 +8854,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"tree-kill": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz",
|
||||
"integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz",
|
||||
"integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"trim-right": {
|
||||
@@ -9101,9 +9133,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"vue-router": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.1.tgz",
|
||||
"integrity": "sha512-vLLoY452L+JBpALMP5UHum9+7nzR9PeIBCghU9ZtJ1eWm6ieUI8Zb/DI3MYxH32bxkjzYV1LRjNv4qr8d+uX/w=="
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.2.tgz",
|
||||
"integrity": "sha512-opKtsxjp9eOcFWdp6xLQPLmRGgfM932Tl56U9chYTnoWqKxQ8M20N7AkdEbM5beUh6wICoFGYugAX9vQjyJLFg=="
|
||||
},
|
||||
"vue-template-compiler": {
|
||||
"version": "2.5.17",
|
||||
@@ -9204,12 +9236,6 @@
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"xregexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz",
|
||||
"integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==",
|
||||
"dev": true
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
|
||||
@@ -9229,13 +9255,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "12.0.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz",
|
||||
"integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==",
|
||||
"version": "12.0.4",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.4.tgz",
|
||||
"integrity": "sha512-f5esswlPO351AnejaO2A1ZZr0zesz19RehQKwiRDqWtrraWrJy16tsUIKgDXFMVytvNOHPVmTiaTh3wO67I0fQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^4.0.0",
|
||||
"decamelize": "^2.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^3.0.0",
|
||||
"get-caller-file": "^1.0.1",
|
||||
"os-locale": "^3.0.0",
|
||||
@@ -9245,27 +9271,17 @@
|
||||
"string-width": "^2.0.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^3.2.1 || ^4.0.0",
|
||||
"yargs-parser": "^10.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"decamelize": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz",
|
||||
"integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"xregexp": "4.0.0"
|
||||
}
|
||||
}
|
||||
"yargs-parser": "^11.1.0"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
|
||||
"integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==",
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.0.tgz",
|
||||
"integrity": "sha512-lGA5HsbjkpCfekDBHAhgE5OE8xEoqiUDylowr+BvhRCwG1xVYTsd8hx2CYC0NY4k9RIgJeybFTG2EZW4P2aN1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^4.1.0"
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
107
package.json
107
package.json
@@ -1,54 +1,59 @@
|
||||
{
|
||||
"name": "dozzle",
|
||||
"version": "1.0.12",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "concurrently 'go run main.go' 'npm run watch-assets'",
|
||||
"watch-assets": "parcel watch assets/index.html -d static",
|
||||
"build": "parcel build assets/index.html -d static",
|
||||
"clean": "rm -rf static"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/amir20/dozzle.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/amir20/dozzle/issues"
|
||||
},
|
||||
"homepage": "https://github.com/amir20/dozzle#readme",
|
||||
"dependencies": {
|
||||
"vue": "^2.5.17",
|
||||
"vue-router": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.1.6",
|
||||
"@babel/plugin-transform-runtime": "^7.1.0",
|
||||
"@vue/component-compiler-utils": "^2.3.0",
|
||||
"concurrently": "^4.0.1",
|
||||
"date-fns": "^2.0.0-alpha.25",
|
||||
"husky": "^1.1.4",
|
||||
"lint-staged": "^8.0.4",
|
||||
"parcel-bundler": "^1.10.3",
|
||||
"prettier": "^1.15.2",
|
||||
"vue-hot-reload-api": "^2.3.1",
|
||||
"vue-template-compiler": "^2.5.17"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,vue,css}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"browserslist": [
|
||||
">5%",
|
||||
"not ie <= 8"
|
||||
"name": "dozzle",
|
||||
"version": "1.2.5",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "concurrently 'go run main.go' 'npm run watch-assets'",
|
||||
"watch-assets": "parcel watch --public-url '__BASE__' assets/index.html -d static",
|
||||
"prebuild": "npm run clean",
|
||||
"build": "parcel build --no-source-maps --public-url '__BASE__' assets/index.html -d static",
|
||||
"clean": "rm -rf static",
|
||||
"release": "goreleaser --rm-dist"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/amir20/dozzle.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/amir20/dozzle/issues"
|
||||
},
|
||||
"homepage": "https://github.com/amir20/dozzle#readme",
|
||||
"dependencies": {
|
||||
"bulma": "^0.7.2",
|
||||
"bulma-tooltip": "^2.0.2",
|
||||
"date-fns": "^2.0.0-alpha.25",
|
||||
"vue": "^2.5.17",
|
||||
"vue-router": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.1.6",
|
||||
"@babel/plugin-transform-runtime": "^7.1.0",
|
||||
"@vue/component-compiler-utils": "^2.3.0",
|
||||
"concurrently": "^4.1.0",
|
||||
"husky": "^1.2.0",
|
||||
"lint-staged": "^8.1.0",
|
||||
"parcel-bundler": "^1.10.3",
|
||||
"prettier": "^1.15.2",
|
||||
"sass": "^1.15.1",
|
||||
"vue-hot-reload-api": "^2.3.1",
|
||||
"vue-template-compiler": "^2.5.17"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,vue,css}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"browserslist": [
|
||||
">5%",
|
||||
"not ie <= 8"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user