Compare commits

...

63 Commits

Author SHA1 Message Date
Amir Raminfar
33de8a4f07 Uses labels instead 2018-11-27 11:22:13 -08:00
Amir Raminfar
93cfd0e597 Fixes memory leaks 2018-11-27 11:13:02 -08:00
Amir Raminfar
537f7c0a01 Fixes gin commands 2018-11-27 09:43:28 -08:00
Amir Raminfar
e114f877c1 Fixes env 2018-11-27 09:01:21 -08:00
Amir Raminfar
55bc51c9c2 Fixes interface name 2018-11-27 08:44:21 -08:00
Amir Raminfar
d93b662907 Removes unused css 2018-11-26 16:44:16 -08:00
Amir Raminfar
3eec6cdd14 1.3.2 2018-11-26 16:10:13 -08:00
Amir Raminfar
3b9adf8260 Makes aside scrollable 2018-11-26 16:09:59 -08:00
Amir Raminfar
dde707a97a Removes tips 2018-11-26 16:06:17 -08:00
Amir Raminfar
50ccf6311b 1.3.1 2018-11-26 14:53:38 -08:00
Amir Raminfar
705a339e49 Adds sorting and cleans up ids and names 2018-11-26 14:52:20 -08:00
Amir Raminfar
f81c240a47 1.3.0 2018-11-26 11:15:53 -08:00
Amir Raminfar
262095e5bb Update README.md 2018-11-26 11:15:32 -08:00
Amir Raminfar
ba900c4374 Uses EventSource instead of websockets (#8)
* Replaces websockets with event-stream

* Adds base_path

* Removes SSL

* Adds event listener for events

* Adds more logging

* Adds event listener to home page
2018-11-26 11:05:16 -08:00
Amir Raminfar
9dc6b3790d 1.2.8 2018-11-25 16:15:59 -08:00
Amir Raminfar
b96785f2be Adds title for pages 2018-11-25 16:14:55 -08:00
Amir Raminfar
dd6f4b1e31 1.2.7 2018-11-25 12:24:15 -08:00
Amir Raminfar
651291ecad Removes hostname from title 2018-11-25 12:23:29 -08:00
Amir Raminfar
a5bcec68cb 1.2.6 2018-11-25 12:14:12 -08:00
Amir Raminfar
77d6a22122 Disables source maps 2018-11-25 12:14:00 -08:00
Amir Raminfar
40f97073e8 1.2.5 2018-11-25 12:10:55 -08:00
Amir Raminfar
75339ffba1 Docker hostname added 2018-11-25 12:10:46 -08:00
Amir Raminfar
2fdfba5a42 Updates packages in node 2018-11-25 12:06:49 -08:00
Amir Raminfar
5987330cdc 1.2.4 2018-11-24 17:48:24 -08:00
Amir Raminfar
14c7c21f9f Fixes bug on ipad 2018-11-24 17:48:18 -08:00
Amir Raminfar
fc9fdaf8b6 1.2.3 2018-11-24 17:34:20 -08:00
Amir Raminfar
5979a6d0e5 Adds responsiveness for menu on left 2018-11-24 17:32:22 -08:00
Amir Raminfar
ca2c46ffce 1.2.2 2018-11-20 09:20:42 -08:00
Amir Raminfar
1cc7e92466 Update README.md 2018-11-20 09:20:31 -08:00
Amir Raminfar
cfc3e81820 Fixes typo #6 2018-11-20 07:45:24 -08:00
Amir Raminfar
67ab2ab170 1.2.1 2018-11-20 07:13:15 -08:00
Amir Raminfar
e1ce378421 Adds SSL support for wss:// (#7)
* 1.2.0

* Adds SSL support
2018-11-20 07:12:13 -08:00
Amir Raminfar
f083ea028d Fixes error in readme 2018-11-19 17:25:59 -08:00
Amir Raminfar
063a82198c Updates demo 2018-11-19 17:22:17 -08:00
Amir Raminfar
d03c3440de 1.2.0 2018-11-19 13:04:25 -08:00
Amir Raminfar
f7b28ad1e0 Adds base option (#5)
* Adds base option

* Removes base unused

* Adds readme

* Adds redirect
2018-11-19 13:04:06 -08:00
Amir Raminfar
52a95757ce Adds clean and release in npm 2018-11-19 07:52:34 -08:00
Amir Raminfar
efc725dadc 1.1.2 2018-11-19 07:50:07 -08:00
Amir Raminfar
ff8c539829 Updates readme 2018-11-19 07:49:57 -08:00
Amir Raminfar
875e17717e Deletes static files first 2018-11-19 07:48:55 -08:00
Amir Raminfar
929f8c19f8 1.1.1 2018-11-19 07:36:12 -08:00
Amir Raminfar
bdcc856071 1.1.0 2018-11-18 19:25:40 -08:00
Amir Raminfar
2325881bd8 Ui redesign (#3)
* Adds a different layout

* Adds new layout and uses sass to get bulma

* Adds new layout and uses sass to get bulma

* Adds title

* Adds ellipses

* Adds tooltip

* Updates packages

* Fixes / page
2018-11-18 19:25:18 -08:00
Amir Raminfar
067fea2b7a Adds prettier 2018-11-18 08:43:32 -08:00
Amir Raminfar
8ac689ca57 1.0.16 2018-11-17 17:42:11 -08:00
Amir Raminfar
5e9ffe7fcf Fixes bug in safari 2018-11-17 17:42:07 -08:00
Amir Raminfar
3b3ba92d27 1.0.15 2018-11-17 16:33:15 -08:00
Amir Raminfar
df2834fd81 Adds new font. Adds scrollboar notification 2018-11-17 16:32:55 -08:00
Amir Raminfar
2ecfefb35f fixes spacing 2018-11-17 11:12:48 -08:00
Amir Raminfar
d18d3f800b Edtiroconfig 2018-11-17 11:02:35 -08:00
Amir Raminfar
9d7fd4eaf0 1.0.14 2018-11-14 07:41:53 -08:00
Amir Raminfar
032ebfd307 Makes background dark 2018-11-14 07:40:24 -08:00
Amir Raminfar
7f74a0f551 1.0.13 2018-11-13 16:46:18 -08:00
Amir Raminfar
dc42180339 Fixes scroll 2018-11-13 16:45:12 -08:00
Amir Raminfar
972cbb8b2e Adds more vuejs 2018-11-13 16:38:49 -08:00
Amir Raminfar
5ee895357d Adds close 2018-11-13 16:38:14 -08:00
Amir Raminfar
d7cfe64273 Updates babel 2018-11-13 16:38:14 -08:00
Amir Raminfar
f06354f909 1.0.12 2018-11-13 16:38:14 -08:00
Amir Raminfar
0416fd541c Update README.md 2018-11-13 15:30:58 -08:00
Amir Raminfar
98701b1c7c 1.0.11 2018-11-01 12:00:11 -07:00
Amir Raminfar
17e08c02bb Adds version 2018-11-01 12:00:03 -07:00
Amir Raminfar
22108a2782 1.0.10 2018-11-01 10:12:22 -07:00
Amir Raminfar
742056bbef Adds latest npm 2018-11-01 10:12:13 -07:00
19 changed files with 981 additions and 405 deletions

19
.editorconfig Normal file
View 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

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@ node_modules
.cache
static
a_main-packr.go
dozzle
dozzle
gin-bin

View File

@@ -1,5 +1,6 @@
before:
hooks:
- npm run clean
- npm run build
- packr
builds:

3
.prettierrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"printWidth": 120
}

View File

@@ -8,7 +8,8 @@ services:
before_install:
- nvm install --lts
- npm i
- npm i -g npm
- npm ci
- go get -u github.com/gobuffalo/packr/packr
after_success:

View File

@@ -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.
![Image](demo.gif)
## 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 8080: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 the dozzle.
#### Environment variable, DOCKER_API_VERSION
@@ -36,9 +46,9 @@ 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
$ docker run --volume=/var/run/docker.sock:/var/run/docker.sock -e DOCKER_API_VERSION=1.20 -p 8888:8080 amir20/dozzle:latest
If you are not sure what to set `DOCKER_API_VERSION` then run `docker version` which will show supported API version.

View File

@@ -1,15 +1,102 @@
<template lang="html">
<router-view></router-view>
<div class="columns is-marginless">
<aside class="column menu is-2-desktop is-3-tablet">
<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, name: item.name } }" active-class="is-active">
<div class="hide-overflow">{{ item.name }}</div>
</router-link>
</li>
</ul>
</aside>
<div class="column is-offset-2-desktop is-offset-3-tablet"><router-view></router-view></div>
<vue-headful :title="title" />
</div>
</template>
<script>
let es;
export default {
name: "App"
name: "App",
data() {
return {
title: "Dozzle",
containers: [],
showNav: false
};
},
async created() {
await this.fetchContainerList();
this.title = `${this.containers.length} containers - Dozzle`;
es = new EventSource(`${BASE_PATH}/api/events/stream`);
es.addEventListener("containers-changed", e => setTimeout(this.fetchContainerList, 1000), false);
},
beforeDestroy() {
if (es) {
es.close();
es = null;
}
},
methods: {
async fetchContainerList() {
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 (min-width: 769px) {
& {
height: 100vh;
overflow: auto;
}
}
@media screen and (max-width: 768px) {
& {
position: sticky;
top: 0;
left: 0;
right: 0;
background: #222;
}
.menu-label {
margin-top: 1em;
}
}
}
.hide-overflow {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.burger.is-white {
color: #fff;
}
</style>

View 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>

View File

@@ -1,19 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<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">
<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 }}";
</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>

View File

@@ -1,13 +1,19 @@
import Vue from "vue";
import VueRouter from "vue-router";
import vueHeadful from "vue-headful";
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);
Vue.component("vue-headful", vueHeadful);
const routes = [
{ path: "/", component: Index },
{
path: "/",
component: Index,
name: "default"
},
{
path: "/container/:id",
component: Container,
@@ -18,6 +24,7 @@ const routes = [
const router = new VueRouter({
mode: "history",
base: BASE_PATH + "/",
routes
});

View File

@@ -1,16 +1,28 @@
<template lang="html">
<ul ref="events" class="events"></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>
<vue-headful :title="title" />
</div>
</template>
<script>
import { formatRelative } from "date-fns";
let ws;
import ScrollbarNotification from "../components/ScrollbarNotification";
let es = 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 key = nextId++;
return {
key,
date,
dateRelative,
message
@@ -18,48 +30,56 @@ const parseMessage = data => {
};
export default {
props: ["id"],
props: ["id", "name"],
name: "Container",
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 data = parseMessage(e.data);
const parent = this.$refs.events;
const item = document.createElement("li");
item.className = "event";
const date = document.createElement("span");
date.className = "date";
date.innerHTML = data.dateRelative;
item.appendChild(date);
const message = document.createElement("span");
message.className = "text";
message.innerHTML = data.message;
item.appendChild(message);
parent.appendChild(item);
this.$nextTick(() => item.scrollIntoView());
components: {
ScrollbarNotification
},
data() {
return {
messages: [],
title: ""
};
},
created() {
this.loadLogs(this.id);
},
beforeDestroy() {
if (es) {
es.close();
es = null;
}
},
watch: {
id(newValue, oldValue) {
if (oldValue !== newValue) {
this.loadLogs(newValue);
}
}
},
methods: {
loadLogs(id) {
if (es) {
es.close();
es = null;
this.messages = [];
}
es = new EventSource(`${BASE_PATH}/api/logs/stream?id=${id}`);
es.onmessage = e => this.messages.push(parseMessage(e.data));
this.title = `${this.name} - Dozzle`;
}
}
};
</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;
}
@@ -67,4 +87,8 @@ export default {
background-color: #262626;
color: #258ccd;
}
.is-fullheight {
min-height: 100vh;
}
</style>

View File

@@ -1,55 +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>
</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>

19
assets/styles.scss Normal file
View File

@@ -0,0 +1,19 @@
@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";
.is-dark {
color: #ddd;
background-color: #111;
}
body {
font-family: "Roboto", sans-serif;
}
h1.title {
font-family: "Gafata", sans-serif;
}

BIN
demo.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 MiB

After

Width:  |  Height:  |  Size: 24 MiB

69
docker/client.go Normal file
View File

@@ -0,0 +1,69 @@
package docker
import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/client"
"io"
"log"
"sort"
"strings"
)
type dockerClient struct {
cli *client.Client
}
type Client interface {
ListContainers() ([]Container, error)
ContainerLogs(ctx context.Context, id string) (io.ReadCloser, error)
Events(ctx context.Context) (<-chan events.Message, <-chan error)
}
func NewClient() Client {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
log.Fatal(err)
}
return &dockerClient{cli}
}
func (d *dockerClient) ListContainers() ([]Container, error) {
list, err := d.cli.ContainerList(context.Background(), types.ContainerListOptions{})
if err != nil {
return nil, err
}
var containers []Container
for _, c := range list {
container := Container{
ID: c.ID[:12],
Names: c.Names,
Name: strings.TrimPrefix(c.Names[0], "/"),
Image: c.Image,
ImageID: c.ImageID,
Command: c.Command,
Created: c.Created,
State: c.State,
Status: c.Status,
}
containers = append(containers, container)
}
sort.Slice(containers, func(i, j int) bool {
return containers[i].Name < containers[j].Name
})
return containers, nil
}
func (d *dockerClient) ContainerLogs(ctx context.Context, id string) (io.ReadCloser, error) {
options := types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300", Timestamps: true}
return d.cli.ContainerLogs(ctx, id, options)
}
func (d *dockerClient) Events(ctx context.Context) (<-chan events.Message, <-chan error) {
return d.cli.Events(ctx, types.EventsOptions{})
}

13
docker/types.go Normal file
View File

@@ -0,0 +1,13 @@
package docker
type Container struct {
ID string `json:"id"`
Names []string `json:"names"`
Name string `json:"name"`
Image string `json:"image"`
ImageID string `json:"imageId"`
Command string `json:"command"`
Created int64 `json:"created"`
State string `json:"state"`
Status string `json:"status"`
}

254
main.go
View File

@@ -1,95 +1,201 @@
package main
import (
"context"
"encoding/binary"
"encoding/json"
"flag"
"log"
"net/http"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/gobuffalo/packr"
"github.com/gorilla/websocket"
"context"
"encoding/binary"
"encoding/json"
"fmt"
"github.com/amir20/dozzle/docker"
"github.com/gobuffalo/packr"
"github.com/gorilla/mux"
flag "github.com/spf13/pflag"
"html/template"
"log"
"net/http"
"strings"
)
var (
cli *client.Client
addr = flag.String("addr", ":8080", "http service address")
upgrader = websocket.Upgrader{}
version = "dev"
commit = "none"
date = "unknown"
dockerClient docker.Client
addr = ""
base = "/"
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")
dockerClient = docker.NewClient()
flag.Parse()
}
func main() {
box := packr.NewBox("./static")
http.HandleFunc("/api/containers.json", listContainers)
http.HandleFunc("/api/logs", logs)
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/stream", streamLogs)
s.HandleFunc("/api/events/stream", streamEvents)
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.Printf("Accepting connections on %s", addr)
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)
}
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 := dockerClient.ListContainers()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = json.NewEncoder(w).Encode(containers)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
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()
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)
}
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)
}
path := ""
if base != "/" {
path = base
}
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
}
}
data := struct{ Base string }{path}
err = tmpl.Execute(w, data)
if err != nil {
panic(err)
}
}
func streamLogs(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
if id == "" {
http.Error(w, "id is required", http.StatusBadRequest)
return
}
f, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
return
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
reader, err := dockerClient.ContainerLogs(ctx, id)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer reader.Close()
go func() {
<-r.Context().Done()
reader.Close()
}()
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Transfer-Encoding", "chunked")
hdr := make([]byte, 8)
content := make([]byte, 1024, 1024*1024)
for {
_, err := reader.Read(hdr)
if err != nil {
break
}
count := binary.BigEndian.Uint32(hdr[4:])
n, err := reader.Read(content[:count])
if err != nil {
break
}
_, err = fmt.Fprintf(w, "data: %s\n\n", content[:n])
if err != nil {
break
}
f.Flush()
}
}
func streamEvents(w http.ResponseWriter, r *http.Request) {
f, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Transfer-Encoding", "chunked")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
messages, _ := dockerClient.Events(ctx)
Loop:
for {
select {
case message, closed := <-messages:
if closed {
log.Println("Breaking from messages")
break Loop
}
switch message.Action {
case "connect":
fallthrough
case "disconnect":
fallthrough
case "create":
fallthrough
case "destroy":
_, err := fmt.Fprintf(w, "event: containers-changed\n")
_, err = fmt.Fprintf(w, "data: %s\n\n", message.Action)
if err != nil {
log.Println(err)
break
}
f.Flush()
}
case <-r.Context().Done():
cancel()
break Loop
}
}
}

516
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "dozzle",
"version": "1.0.9",
"version": "1.3.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -14,40 +14,102 @@
}
},
"@babel/core": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.1.2.tgz",
"integrity": "sha512-IFeSSnjXdhDaoysIlev//UzHZbdEmm7D0EIH2qtse9xK7mXEZQpYjs2P00XlP1qYsYvid79p+Zgg6tz1mp6iVw==",
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.1.6.tgz",
"integrity": "sha512-Hz6PJT6e44iUNpAn8AoyAs6B3bl60g7MJQaI0rZEar6ECzh6+srYO1xlIdssio34mPaUtAb1y+XlkkSJzok3yw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/generator": "^7.1.2",
"@babel/helpers": "^7.1.2",
"@babel/parser": "^7.1.2",
"@babel/generator": "^7.1.6",
"@babel/helpers": "^7.1.5",
"@babel/parser": "^7.1.6",
"@babel/template": "^7.1.2",
"@babel/traverse": "^7.1.0",
"@babel/types": "^7.1.2",
"@babel/traverse": "^7.1.6",
"@babel/types": "^7.1.6",
"convert-source-map": "^1.1.0",
"debug": "^3.1.0",
"json5": "^0.5.0",
"debug": "^4.1.0",
"json5": "^2.1.0",
"lodash": "^4.17.10",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"source-map": "^0.5.0"
},
"dependencies": {
"@babel/generator": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.1.6.tgz",
"integrity": "sha512-brwPBtVvdYdGxtenbQgfCdDPmtkmUBZPjUoK5SXJEBuHaA5BCubh9ly65fzXz7R6o5rA76Rs22ES8Z+HCc0YIQ==",
"dev": true,
"requires": {
"@babel/types": "^7.1.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.10",
"source-map": "^0.5.0",
"trim-right": "^1.0.1"
}
},
"@babel/parser": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.6.tgz",
"integrity": "sha512-dWP6LJm9nKT6ALaa+bnL247GHHMWir3vSlZ2+IHgHgktZQx0L3Uvq2uAWcuzIe+fujRsYWBW2q622C5UvGK9iQ==",
"dev": true
},
"@babel/traverse": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.6.tgz",
"integrity": "sha512-CXedit6GpISz3sC2k2FsGCUpOhUqKdyL0lqNrImQojagnUMXf8hex4AxYFRuMkNGcvJX5QAFGzB5WJQmSv8SiQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/generator": "^7.1.6",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.0.0",
"@babel/parser": "^7.1.6",
"@babel/types": "^7.1.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.10"
}
},
"@babel/types": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.6.tgz",
"integrity": "sha512-DMiUzlY9DSjVsOylJssxLHSgj6tWM9PRFJOGW/RaOglVOK9nzTxoOMfTfRQXGUCUQ/HmlG2efwC+XqUEJ5ay4w==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.10",
"to-fast-properties": "^2.0.0"
}
},
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
"integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
"json5": {
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz",
"integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"ms": {
@@ -55,6 +117,12 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
"dev": true
}
}
},
@@ -282,14 +350,90 @@
}
},
"@babel/helpers": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.1.2.tgz",
"integrity": "sha512-Myc3pUE8eswD73aWcartxB16K6CGmHDv9KxOmD2CeOs/FaEAQodr3VYGmlvOmog60vNQ2w8QbatuahepZwrHiA==",
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.1.5.tgz",
"integrity": "sha512-2jkcdL02ywNBry1YNFAH/fViq4fXG0vdckHqeJk+75fpQ2OH+Az6076tX/M0835zA45E0Cqa6pV5Kiv9YOqjEg==",
"dev": true,
"requires": {
"@babel/template": "^7.1.2",
"@babel/traverse": "^7.1.0",
"@babel/types": "^7.1.2"
"@babel/traverse": "^7.1.5",
"@babel/types": "^7.1.5"
},
"dependencies": {
"@babel/generator": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.1.6.tgz",
"integrity": "sha512-brwPBtVvdYdGxtenbQgfCdDPmtkmUBZPjUoK5SXJEBuHaA5BCubh9ly65fzXz7R6o5rA76Rs22ES8Z+HCc0YIQ==",
"dev": true,
"requires": {
"@babel/types": "^7.1.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.10",
"source-map": "^0.5.0",
"trim-right": "^1.0.1"
}
},
"@babel/parser": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.6.tgz",
"integrity": "sha512-dWP6LJm9nKT6ALaa+bnL247GHHMWir3vSlZ2+IHgHgktZQx0L3Uvq2uAWcuzIe+fujRsYWBW2q622C5UvGK9iQ==",
"dev": true
},
"@babel/traverse": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.1.6.tgz",
"integrity": "sha512-CXedit6GpISz3sC2k2FsGCUpOhUqKdyL0lqNrImQojagnUMXf8hex4AxYFRuMkNGcvJX5QAFGzB5WJQmSv8SiQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/generator": "^7.1.6",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.0.0",
"@babel/parser": "^7.1.6",
"@babel/types": "^7.1.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.10"
}
},
"@babel/types": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.1.6.tgz",
"integrity": "sha512-DMiUzlY9DSjVsOylJssxLHSgj6tWM9PRFJOGW/RaOglVOK9nzTxoOMfTfRQXGUCUQ/HmlG2efwC+XqUEJ5ay4w==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.10",
"to-fast-properties": "^2.0.0"
}
},
"debug": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
"integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
"dev": true
}
}
},
"@babel/highlight": {
@@ -986,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",
@@ -1622,6 +1818,11 @@
"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=="
},
"cache-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
@@ -1646,9 +1847,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": {
@@ -2024,16 +2225,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",
@@ -2449,8 +2650,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",
@@ -2902,12 +3102,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",
@@ -3794,6 +3988,12 @@
"integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=",
"dev": true
},
"globals": {
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.9.0.tgz",
"integrity": "sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg==",
"dev": true
},
"globby": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
@@ -3925,6 +4125,11 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"headful": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/headful/-/headful-1.0.3.tgz",
"integrity": "sha512-vF9Vfddn1QWmziliht2mji6ayI78+hUuSC+Kt0GEqLw/51zWgi1KF7oLtIQf3nlkg8sQQOlznkkIaF4W9lIt9w=="
},
"hex-color-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
@@ -4443,13 +4648,13 @@
"dev": true
},
"husky": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/husky/-/husky-1.1.2.tgz",
"integrity": "sha512-9TdkUpBeEOjz0AnFdUN4i3w8kEbOsVs9/WSeJqWLq2OO6bcKQhVW64Zi+pVd/AMRLpN3QTINb6ZXiELczvdmqQ==",
"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",
"execa": "^0.9.0",
"execa": "^1.0.0",
"find-up": "^3.0.0",
"get-stdin": "^6.0.0",
"is-ci": "^1.2.1",
@@ -4460,25 +4665,14 @@
"slash": "^2.0.0"
},
"dependencies": {
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"dev": true,
"requires": {
"lru-cache": "^4.0.1",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"execa": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-0.9.0.tgz",
"integrity": "sha512-BbUMBiX4hqiHZUA5+JujIjNb6TyAlp2D5KLheMjMluwOuzcnylDL4AxZYLLn1n2AGB49eSWwyKvvEQoRpnAtmA==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"dev": true,
"requires": {
"cross-spawn": "^5.0.1",
"get-stream": "^3.0.0",
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
@@ -4486,11 +4680,14 @@
"strip-eof": "^1.0.0"
}
},
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"dev": true,
"requires": {
"pump": "^3.0.0"
}
}
}
},
@@ -4998,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",
@@ -5016,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",
@@ -5072,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",
@@ -5082,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": {
@@ -5095,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",
@@ -5146,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
}
}
},
@@ -5331,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"
@@ -7515,9 +7680,9 @@
"dev": true
},
"prettier": {
"version": "1.14.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.3.tgz",
"integrity": "sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==",
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.15.2.tgz",
"integrity": "sha512-YgPLFFA0CdKL4Eg2IHtUSjzj/BWgszDHiNQAe0VAIBse34148whfdzLagRL+QiKS+YfK5ftB6X4v/MBw8yCoug==",
"dev": true
},
"pretty-format": {
@@ -7857,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"
@@ -7889,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",
@@ -8035,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"
@@ -8077,6 +8251,12 @@
}
}
},
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true
},
"slice-ansi": {
"version": "0.0.4",
"resolved": "http://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
@@ -8674,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": {
@@ -8946,6 +9126,14 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.5.17.tgz",
"integrity": "sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ=="
},
"vue-headful": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/vue-headful/-/vue-headful-2.0.1.tgz",
"integrity": "sha512-h2G/jXCi2hAx6O3gwWN8uTj1eQlSKNHgvkCVZcokZneGczWCRghAUCFYrOvZQM+F+SyFB3YXqoI62rE0Sc8QsA==",
"requires": {
"headful": "^1.0.3"
}
},
"vue-hot-reload-api": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz",
@@ -8953,9 +9141,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",
@@ -9056,12 +9244,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",
@@ -9081,13 +9263,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",
@@ -9097,27 +9279,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"
}
}
}

View File

@@ -1,54 +1,60 @@
{
"name": "dozzle",
"version": "1.0.9",
"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.2",
"@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.2",
"lint-staged": "^8.0.4",
"parcel-bundler": "^1.10.3",
"prettier": "^1.14.3",
"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.3.2",
"description": "",
"main": "index.js",
"scripts": {
"prestart": "npm run clean",
"start": "DOCKER_API_VERSION=1.38 concurrently 'gin -a 8080 -i 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/ a_main-packr.go",
"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",
"date-fns": "^2.0.0-alpha.25",
"vue": "^2.5.17",
"vue-headful": "^2.0.1",
"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"
]
}