Compare commits
12 Commits
pkg/dev/ub
...
19e15d3fe7
Author | SHA1 | Date | |
---|---|---|---|
19e15d3fe7 | |||
db0a3ac98f | |||
b335a825a3 | |||
f959fdb93f | |||
6a52595fa7 | |||
450a9de82d | |||
3afd252c71 | |||
8027ba7bcc | |||
61c3cd33be | |||
62344993f5 | |||
9035280818 | |||
82f21d0298 |
@ -5,4 +5,5 @@
|
|||||||
/bin
|
/bin
|
||||||
/misc/docker/Dockerfile
|
/misc/docker/Dockerfile
|
||||||
/.env
|
/.env
|
||||||
/.env.dist
|
/.env.dist
|
||||||
|
/tools
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@
|
|||||||
/vendor
|
/vendor
|
||||||
/bin
|
/bin
|
||||||
/node_modules
|
/node_modules
|
||||||
/.env
|
/.env
|
||||||
|
/tools
|
16
Makefile
16
Makefile
@ -1,3 +1,15 @@
|
|||||||
|
DOCKER_DATE_TAG := $(shell date --utc +%Y.%-m.%-d%-H%-M)
|
||||||
|
|
||||||
|
YQ_VERSION ?= v4.30.4
|
||||||
|
YQ_BINARY ?= yq_linux_amd64
|
||||||
|
|
||||||
|
tools: tools/yq/bin/yq
|
||||||
|
|
||||||
|
tools/yq/bin/yq:
|
||||||
|
mkdir -p tools/yq/bin
|
||||||
|
wget https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${YQ_BINARY} -O tools/yq/bin/yq &&\
|
||||||
|
chmod +x tools/yq/bin/yq
|
||||||
|
|
||||||
build:
|
build:
|
||||||
CGO_ENABLED=0 go build -v -o bin/fake-smtp ./cmd/fake-smtp
|
CGO_ENABLED=0 go build -v -o bin/fake-smtp ./cmd/fake-smtp
|
||||||
|
|
||||||
@ -21,13 +33,15 @@ docker-run:
|
|||||||
|
|
||||||
docker-release:
|
docker-release:
|
||||||
docker tag fake-smtp:latest bornholm/fake-smtp:latest
|
docker tag fake-smtp:latest bornholm/fake-smtp:latest
|
||||||
|
docker tag fake-smtp:latest bornholm/fake-smtp:$(DOCKER_DATE_TAG)
|
||||||
docker login
|
docker login
|
||||||
docker push bornholm/fake-smtp:latest
|
docker push bornholm/fake-smtp:latest
|
||||||
|
docker push bornholm/fake-smtp:$(DOCKER_DATE_TAG)
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test -v -race ./...
|
go test -v -race ./...
|
||||||
|
|
||||||
release: dist
|
release: dist tools
|
||||||
@./misc/script/release.sh
|
@./misc/script/release.sh
|
||||||
|
|
||||||
dist:
|
dist:
|
||||||
|
20
README.md
20
README.md
@ -55,6 +55,18 @@ smtp:
|
|||||||
# Configuration du stockage
|
# Configuration du stockage
|
||||||
data:
|
data:
|
||||||
path: fakesmtp.db
|
path: fakesmtp.db
|
||||||
|
|
||||||
|
# Configuration du relais SMTP
|
||||||
|
relay:
|
||||||
|
enabled: false # Activer/désactiver le relais SMTP
|
||||||
|
address: "" # Adresse du serveur au format "host:port"
|
||||||
|
identity: "" # Identité du compte utilisateur, peut être laissé vide
|
||||||
|
username: "" # Identifiant du compte SMTP, non utilisé sur anonymous = true
|
||||||
|
password: "" # Mot de passe du compte SMTP, non utilisé sur anonymous = true
|
||||||
|
anonymous: false # Utiliser le mode d'authentification "anonyme"
|
||||||
|
useTLS: false # Utiliser TLS pour se connecter au serveur SMTP
|
||||||
|
insecureSkipVerify: true # Ne pas vérifier le certificat du serveur pour les connexions TLS/STARTTLS
|
||||||
|
fromOverride: "" # Surcharger l'adresse émetteur des courriels transmis
|
||||||
```
|
```
|
||||||
|
|
||||||
### Variables d'environnement
|
### Variables d'environnement
|
||||||
@ -79,6 +91,14 @@ Les valeurs des variables d'environnement surchargent les valeurs présentes dan
|
|||||||
|`FAKESMTP_SMTP_ALLOWINSECUREAUTH`|`smtp.allowInsecureAuth`|
|
|`FAKESMTP_SMTP_ALLOWINSECUREAUTH`|`smtp.allowInsecureAuth`|
|
||||||
|`FAKESMTP_SMTP_DEBUG`|`smtp.debug`|
|
|`FAKESMTP_SMTP_DEBUG`|`smtp.debug`|
|
||||||
|`FAKESMTP_DATA_PATH`|`data.path`|
|
|`FAKESMTP_DATA_PATH`|`data.path`|
|
||||||
|
|`FAKESMTP_RELAY_ENABLED`|`relay.enabled`|
|
||||||
|
|`FAKESMTP_RELAY_ADDRESS`|`relay.address`|
|
||||||
|
|`FAKESMTP_RELAY_IDENTITY`|`relay.identity`|
|
||||||
|
|`FAKESMTP_RELAY_USERNAME`|`relay.username`|
|
||||||
|
|`FAKESMTP_RELAY_PASSWORD`|`relay.password`|
|
||||||
|
|`FAKESMTP_RELAY_ANONYMOUS`|`relay.anonymous`|
|
||||||
|
|`FAKESMTP_RELAY_INSECURE_SKIP_VERIFY`|`relay.insecureSkipVerify`|
|
||||||
|
|`FAKESMTP_RELAY_FROM_OVERRIDE`|`relay.fromOverride`|
|
||||||
|
|
||||||
## Démarrer avec les sources
|
## Démarrer avec les sources
|
||||||
|
|
||||||
|
@ -53,6 +53,11 @@ func getServiceContainer(conf *config.Config) (*service.Container, error) {
|
|||||||
cqrs.CommandHandlerFunc(command.HandleDeleteEmail),
|
cqrs.CommandHandlerFunc(command.HandleDeleteEmail),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
bus.RegisterCommand(
|
||||||
|
cqrs.MatchCommandRequest(&command.RelayEmailRequest{}),
|
||||||
|
cqrs.CommandHandlerFunc(command.HandleRelayEmail),
|
||||||
|
)
|
||||||
|
|
||||||
bus.RegisterQuery(
|
bus.RegisterQuery(
|
||||||
cqrs.MatchQueryRequest(&query.GetInboxRequest{}),
|
cqrs.MatchQueryRequest(&query.GetInboxRequest{}),
|
||||||
cqrs.QueryHandlerFunc(query.HandleGetInbox),
|
cqrs.QueryHandlerFunc(query.HandleGetInbox),
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/jhillyerd/enmime"
|
"github.com/jhillyerd/enmime"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/cqrs"
|
"gitlab.com/wpetit/goweb/cqrs"
|
||||||
|
"gitlab.com/wpetit/goweb/logger"
|
||||||
"gitlab.com/wpetit/goweb/middleware/container"
|
"gitlab.com/wpetit/goweb/middleware/container"
|
||||||
"gitlab.com/wpetit/goweb/service"
|
"gitlab.com/wpetit/goweb/service"
|
||||||
)
|
)
|
||||||
@ -74,12 +75,31 @@ func (s *Session) Data(r io.Reader) error {
|
|||||||
return errors.Wrap(err, "could not retrieve cqrs service")
|
return errors.Wrap(err, "could not retrieve cqrs service")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conf, err := config.From(s.ctn)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not retrieve config service")
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Relay.Enabled {
|
||||||
|
cmd := &command.RelayEmailRequest{
|
||||||
|
Envelope: env,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := bus.Exec(s.ctx, cmd); err != nil {
|
||||||
|
logger.Error(s.ctx, "could not exec command", logger.E(err))
|
||||||
|
|
||||||
|
return errors.Wrapf(err, "could not exec '%T' command", cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cmd := &command.StoreEmailRequest{
|
cmd := &command.StoreEmailRequest{
|
||||||
Envelope: env,
|
Envelope: env,
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := bus.Exec(s.ctx, cmd); err != nil {
|
if _, err := bus.Exec(s.ctx, cmd); err != nil {
|
||||||
return errors.Wrap(err, "could not exec command")
|
logger.Error(s.ctx, "could not exec command", logger.E(err))
|
||||||
|
|
||||||
|
return errors.Wrapf(err, "could not exec '%T' command", cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{{block "title" . -}}{{- end}}</title>
|
<title>{{block "title" . -}}{{- end}}</title>
|
||||||
{{- block "head_style" . -}}
|
{{- block "head_style" . -}}
|
||||||
<link rel="stylesheet" href="/css/main.css" />
|
<link rel="stylesheet" href="{{ .BaseURL }}/css/main.css" />
|
||||||
{{end}}
|
{{end}}
|
||||||
{{- block "head_script" . -}}
|
{{- block "head_script" . -}}
|
||||||
<script type="text/javascript" src="/main.js"></script>
|
<script type="text/javascript" src="{{ .BaseURL }}/main.js"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="columns is-mobile">
|
<div class="columns is-mobile">
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<h1 class="is-size-3 title">
|
<h1 class="is-size-3 title">
|
||||||
<a href="/" rel="Inbox" class="has-text-grey-dark">
|
<a href="{{ .BaseURL }}/" rel="Inbox" class="has-text-grey-dark">
|
||||||
{{if or .Emails .Email}}
|
{{if or .Emails .Email}}
|
||||||
📬
|
📬
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
{{define "header_buttons"}}
|
{{define "header_buttons"}}
|
||||||
<button class="button is-danger"
|
<button class="button is-danger"
|
||||||
data-controller="restful"
|
data-controller="restful"
|
||||||
data-restful-endpoint="./{{ .Email.ID }}"
|
data-restful-endpoint="{{ .BaseURL }}/emails/{{ .Email.ID }}"
|
||||||
data-restful-method="DELETE"
|
data-restful-method="DELETE"
|
||||||
data-restful-redirect="../">
|
data-restful-redirect="{{ .BaseURL }}/">
|
||||||
🗑️ Delete
|
🗑️ Delete
|
||||||
</button>
|
</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
@ -24,8 +24,9 @@
|
|||||||
<h4 class="title is-size-4">Attachments ({{len .Email.Attachments}})</h4>
|
<h4 class="title is-size-4">Attachments ({{len .Email.Attachments}})</h4>
|
||||||
<ul>
|
<ul>
|
||||||
{{ $email := .Email }}
|
{{ $email := .Email }}
|
||||||
|
{{ $baseURL := .BaseURL }}
|
||||||
{{range $i, $a := .Email.Attachments}}
|
{{range $i, $a := .Email.Attachments}}
|
||||||
<li><a href="{{ $email.ID }}/attachments/{{ $i }}" download="{{ $a.Name }}">{{ $a.Name }}</a></li>
|
<li><a href="{{ $baseURL }}/emails/{{ $email.ID }}/attachments/{{ $i }}" download="{{ $a.Name }}">{{ $a.Name }}</a></li>
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -44,7 +45,7 @@
|
|||||||
data-controller="iframe"
|
data-controller="iframe"
|
||||||
data-action="load->iframe#onLoad"
|
data-action="load->iframe#onLoad"
|
||||||
style="width:100%;{{if not .Email.HTML}}display:none;{{end}}"
|
style="width:100%;{{if not .Email.HTML}}display:none;{{end}}"
|
||||||
src="{{ .Email.ID }}/html">
|
src="{{ .BaseURL }}/emails/{{ .Email.ID }}/html">
|
||||||
</iframe>
|
</iframe>
|
||||||
<div data-target="tabs.tabContent" data-tabs-for="text" style="{{if .Email.HTML}}display:none;{{end}}width:100%;overflow:hidden;">
|
<div data-target="tabs.tabContent" data-tabs-for="text" style="{{if .Email.HTML}}display:none;{{end}}width:100%;overflow:hidden;">
|
||||||
<pre style="white-space:pre-line;">{{ .Email.Text }}</pre>
|
<pre style="white-space:pre-line;">{{ .Email.Text }}</pre>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{{define "header_buttons"}}
|
{{define "header_buttons"}}
|
||||||
<button
|
<button
|
||||||
data-controller="restful"
|
data-controller="restful"
|
||||||
data-restful-endpoint="/emails"
|
data-restful-endpoint="{{ .BaseURL }}/emails"
|
||||||
data-restful-method="DELETE"
|
data-restful-method="DELETE"
|
||||||
class="button is-danger">
|
class="button is-danger">
|
||||||
🗑️ Clear
|
🗑️ Clear
|
||||||
@ -24,10 +24,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{{ $baseURL := .BaseURL }}
|
||||||
{{range .Emails}}
|
{{range .Emails}}
|
||||||
<tr data-controller="inbox-entry"
|
<tr data-controller="inbox-entry"
|
||||||
data-action="click->inbox-entry#onClick"
|
data-action="click->inbox-entry#onClick"
|
||||||
data-inbox-entry-link="./emails/{{ .ID }}">
|
data-inbox-entry-link="{{ $baseURL }}/emails/{{ .ID }}">
|
||||||
<td class="email-subject"><div>{{ .Subject }}</div></td>
|
<td class="email-subject"><div>{{ .Subject }}</div></td>
|
||||||
<td class="email-from">
|
<td class="email-from">
|
||||||
{{range .From}}
|
{{range .From}}
|
||||||
@ -44,7 +45,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="email-actions">
|
<td class="email-actions">
|
||||||
<div class="buttons is-right">
|
<div class="buttons is-right">
|
||||||
<a href="./emails/{{ .ID }}" class="button is-small is-link">👁️ See</a>
|
<a href="{{ $baseURL }}/emails/{{ .ID }}" class="button is-small is-link">👁️ See</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.14
|
|||||||
require (
|
require (
|
||||||
github.com/asdine/storm/v3 v3.1.1
|
github.com/asdine/storm/v3 v3.1.1
|
||||||
github.com/caarlos0/env/v6 v6.2.1
|
github.com/caarlos0/env/v6 v6.2.1
|
||||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b // indirect
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
|
||||||
github.com/emersion/go-smtp v0.12.1
|
github.com/emersion/go-smtp v0.12.1
|
||||||
github.com/go-chi/chi v4.1.1+incompatible
|
github.com/go-chi/chi v4.1.1+incompatible
|
||||||
github.com/jhillyerd/enmime v0.8.0
|
github.com/jhillyerd/enmime v0.8.0
|
||||||
|
11
go.sum
11
go.sum
@ -51,10 +51,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
|
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
|
||||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e h1:ba7YsgX5OV8FjGi5ZWml8Jng6oBrJAb3ahqWMJ5Ce8Q=
|
|
||||||
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
||||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b h1:uhWtEWBHgop1rqEk2klKaxPAkVDCXexai6hSuRQ7Nvs=
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
|
||||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-smtp v0.12.1 h1:1R8BDqrR2HhlGwgFYcOi+BVTvK1bMjAB65QcVpJ5sNA=
|
github.com/emersion/go-smtp v0.12.1 h1:1R8BDqrR2HhlGwgFYcOi+BVTvK1bMjAB65QcVpJ5sNA=
|
||||||
github.com/emersion/go-smtp v0.12.1/go.mod h1:SD9V/xa4ndMw77lR3Mf7htkp8RBNYuPh9UeuBs9tpUQ=
|
github.com/emersion/go-smtp v0.12.1/go.mod h1:SD9V/xa4ndMw77lR3Mf7htkp8RBNYuPh9UeuBs9tpUQ=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
@ -104,9 +103,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
|||||||
github.com/gorilla/csrf v1.6.0/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI=
|
github.com/gorilla/csrf v1.6.0/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI=
|
||||||
github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
|
|
||||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
@ -159,7 +156,6 @@ github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02n
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
@ -211,7 +207,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
@ -235,7 +230,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
|
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -293,7 +287,6 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
|||||||
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
|
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
164
internal/command/relay_email.go
Normal file
164
internal/command/relay_email.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/emersion/go-sasl"
|
||||||
|
|
||||||
|
"github.com/emersion/go-smtp"
|
||||||
|
|
||||||
|
"forge.cadoles.com/wpetit/fake-smtp/internal/config"
|
||||||
|
"github.com/jhillyerd/enmime"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gitlab.com/wpetit/goweb/cqrs"
|
||||||
|
"gitlab.com/wpetit/goweb/middleware/container"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RelayEmailRequest struct {
|
||||||
|
Envelope *enmime.Envelope
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleRelayEmail(ctx context.Context, cmd cqrs.Command) error {
|
||||||
|
req, ok := cmd.Request().(*RelayEmailRequest)
|
||||||
|
if !ok {
|
||||||
|
return cqrs.ErrUnexpectedRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
ctn, err := container.From(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not retrieve service container")
|
||||||
|
}
|
||||||
|
|
||||||
|
conf, err := config.From(ctn)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "could not retrieve config service")
|
||||||
|
}
|
||||||
|
|
||||||
|
relay := conf.Relay
|
||||||
|
|
||||||
|
if err := forwardMail(req.Envelope, relay); err != nil {
|
||||||
|
return errors.Wrap(err, "could not forward mail")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func forwardMail(env *enmime.Envelope, conf config.RelayConfig) error {
|
||||||
|
var tlsConfig *tls.Config
|
||||||
|
|
||||||
|
if conf.InsecureSkipVerify {
|
||||||
|
tlsConfig = &tls.Config{
|
||||||
|
// nolint: gosec
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := conf.Address
|
||||||
|
|
||||||
|
var (
|
||||||
|
client *smtp.Client
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if conf.UseTLS {
|
||||||
|
client, err = smtp.DialTLS(addr, tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client, err = smtp.Dial(addr)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
if err = client.Hello("localhost"); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok, _ := client.Extension("STARTTLS"); ok {
|
||||||
|
if err = client.StartTLS(tlsConfig); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Username != "" || conf.Password != "" {
|
||||||
|
if ok, _ := client.Extension("AUTH"); ok {
|
||||||
|
var auth sasl.Client
|
||||||
|
if conf.Anonymous {
|
||||||
|
auth = sasl.NewAnonymousClient("fakesmtp")
|
||||||
|
} else {
|
||||||
|
auth = sasl.NewPlainClient(conf.Identity, conf.Username, conf.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Auth(auth); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var from string
|
||||||
|
|
||||||
|
if conf.FromOverride != "" {
|
||||||
|
from = conf.FromOverride
|
||||||
|
} else {
|
||||||
|
from = env.GetHeader("From")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = client.Mail(from, nil); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
to := env.GetHeaderValues("To")
|
||||||
|
|
||||||
|
for _, addr := range to {
|
||||||
|
if err = client.Rcpt(addr); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w, err := client.Data()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
defer pw.Close()
|
||||||
|
|
||||||
|
if err = env.Root.Encode(pw); err != nil {
|
||||||
|
err = errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = io.Copy(w, pr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = w.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Quit(); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -12,15 +12,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
HTTP HTTPConfig `yaml:"http"`
|
HTTP HTTPConfig `yaml:"http"`
|
||||||
SMTP SMTPConfig `yaml:"smtp"`
|
SMTP SMTPConfig `yaml:"smtp"`
|
||||||
Data DataConfig `ymal:"data"`
|
Data DataConfig `yaml:"data"`
|
||||||
|
Relay RelayConfig `yaml:"relay"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPConfig struct {
|
type HTTPConfig struct {
|
||||||
Address string `yaml:"address" env:"FAKESMTP_HTTP_ADDRESS"`
|
Address string `yaml:"address" env:"FAKESMTP_HTTP_ADDRESS"`
|
||||||
TemplateDir string `yaml:"templateDir" env:"FAKESMTP_HTTP_TEMPLATEDIR"`
|
TemplateDir string `yaml:"templateDir" env:"FAKESMTP_HTTP_TEMPLATEDIR"`
|
||||||
PublicDir string `yaml:"publicDir" env:"FAKESMTP_HTTP_PUBLICDIR"`
|
PublicDir string `yaml:"publicDir" env:"FAKESMTP_HTTP_PUBLICDIR"`
|
||||||
|
BaseURL string `yaml:"baseUrl" env:"FAKESMTP_HTTP_BASEURL"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SMTPConfig struct {
|
type SMTPConfig struct {
|
||||||
@ -36,6 +38,18 @@ type SMTPConfig struct {
|
|||||||
Debug bool `yaml:"debug" env:"FAKESMTP_SMTP_DEBUG"`
|
Debug bool `yaml:"debug" env:"FAKESMTP_SMTP_DEBUG"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RelayConfig struct {
|
||||||
|
Enabled bool `yaml:"enabled" env:"FAKESMTP_RELAY_ENABLED"`
|
||||||
|
Address string `yaml:"address" env:"FAKESMTP_RELAY_ADDRESS"`
|
||||||
|
Identity string `yaml:"identity" env:"FAKESMTP_RELAY_IDENTITY"`
|
||||||
|
Username string `yaml:"username" env:"FAKESMTP_RELAY_USERNAME"`
|
||||||
|
Password string `yaml:"password" env:"FAKESMTP_RELAY_PASSWORD"`
|
||||||
|
Anonymous bool `yaml:"anonymous" env:"FAKESMTP_RELAY_ANONYMOUS"`
|
||||||
|
UseTLS bool `yaml:"useTLS" env:"FAKESMTP_RELAY_USE_TLS"`
|
||||||
|
InsecureSkipVerify bool `yaml:"insecureSkipVerify" env:"FAKESMTP_RELAY_INSECURE_SKIP_VERIFY"`
|
||||||
|
FromOverride string `yaml:"fromOverride" env:"FAKESMTP_RELAY_FROM_OVERRIDE"`
|
||||||
|
}
|
||||||
|
|
||||||
type DataConfig struct {
|
type DataConfig struct {
|
||||||
Path string `yaml:"path" env:"FAKESMTP_DATA_PATH"`
|
Path string `yaml:"path" env:"FAKESMTP_DATA_PATH"`
|
||||||
}
|
}
|
||||||
@ -91,6 +105,9 @@ func NewDefault() *Config {
|
|||||||
Data: DataConfig{
|
Data: DataConfig{
|
||||||
Path: "fakesmtp.db",
|
Path: "fakesmtp.db",
|
||||||
},
|
},
|
||||||
|
Relay: RelayConfig{
|
||||||
|
Enabled: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
195
internal/query/filter_emails_test.go
Normal file
195
internal/query/filter_emails_test.go
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/mail"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"forge.cadoles.com/wpetit/fake-smtp/internal/model"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type emailMatcherTestCase struct {
|
||||||
|
Name string
|
||||||
|
Search *InboxSearch
|
||||||
|
Email *model.Email
|
||||||
|
Matcher emailMatcherFunc
|
||||||
|
Expect bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmailMatcher(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
johnDoeAddr, err := mail.ParseAddress("john.doe@test.local")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(errors.WithStack(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
adaLovelaceAddr, err := mail.ParseAddress("ada.lovelace@test.local")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(errors.WithStack(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
testCases := []emailMatcherTestCase{
|
||||||
|
{
|
||||||
|
Name: "Simple matching header",
|
||||||
|
Email: &model.Email{
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"X-Swift-To": {johnDoeAddr.Address},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
Headers: map[string]string{
|
||||||
|
"X-Swift-To": johnDoeAddr.Address,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Matcher: matchHeaders,
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Multiple matching header",
|
||||||
|
Email: &model.Email{
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"X-Swift-To": {johnDoeAddr.Address},
|
||||||
|
"Content-Type": {"multipart/alternative; boundary=\"_=_swift_1645181013_7b80ab8ab386ba4fcaff4f6f79593adb_=_\""},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
Headers: map[string]string{
|
||||||
|
"X-Swift-To": johnDoeAddr.Address,
|
||||||
|
"Content-Type": "multipart/alternative",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Matcher: matchHeaders,
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Simple non matching header",
|
||||||
|
Email: &model.Email{
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"X-Swift-To": {johnDoeAddr.Address},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
Headers: map[string]string{
|
||||||
|
"X-Swift-To": adaLovelaceAddr.Address,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Matcher: matchHeaders,
|
||||||
|
Expect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Multiple non matching headers",
|
||||||
|
Email: &model.Email{
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"X-Swift-To": {johnDoeAddr.Address},
|
||||||
|
"Content-Type": {"foo/bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
Headers: map[string]string{
|
||||||
|
"X-Swift-To": johnDoeAddr.Address,
|
||||||
|
"Content-Type": "multipart/alternative",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Matcher: matchHeaders,
|
||||||
|
Expect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Simple to",
|
||||||
|
Email: &model.Email{
|
||||||
|
To: []*mail.Address{johnDoeAddr},
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
To: johnDoeAddr.Address,
|
||||||
|
},
|
||||||
|
Matcher: matchTo,
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Simple from",
|
||||||
|
Email: &model.Email{
|
||||||
|
From: []*mail.Address{johnDoeAddr},
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
From: johnDoeAddr.Address,
|
||||||
|
},
|
||||||
|
Matcher: matchFrom,
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Simple after",
|
||||||
|
Email: &model.Email{
|
||||||
|
SentAt: now,
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
After: now.Add(-5 * time.Second),
|
||||||
|
},
|
||||||
|
Matcher: matchAfter,
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Simple before",
|
||||||
|
Email: &model.Email{
|
||||||
|
SentAt: now,
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
Before: now.Add(5 * time.Second),
|
||||||
|
},
|
||||||
|
Matcher: matchBefore,
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Matching composite",
|
||||||
|
Email: &model.Email{
|
||||||
|
SentAt: now,
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"X-Swift-To": {johnDoeAddr.Address},
|
||||||
|
"Content-Type": {"multipart/alternative; boundary=\"_=_swift_1645181013_7b80ab8ab386ba4fcaff4f6f79593adb_=_\""},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
Before: now.Add(5 * time.Second),
|
||||||
|
Headers: map[string]string{
|
||||||
|
"X-Swift-To": johnDoeAddr.Address,
|
||||||
|
"Content-Type": "multipart/alternative",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Matcher: and(matchBefore, matchHeaders),
|
||||||
|
Expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Non matching composite",
|
||||||
|
Email: &model.Email{
|
||||||
|
SentAt: now,
|
||||||
|
Headers: map[string][]string{
|
||||||
|
"X-Swift-To": {johnDoeAddr.Address},
|
||||||
|
"Content-Type": {"multipart/alternative; boundary=\"_=_swift_1645181013_7b80ab8ab386ba4fcaff4f6f79593adb_=_\""},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Search: &InboxSearch{
|
||||||
|
Before: now.Add(5 * time.Second),
|
||||||
|
Headers: map[string]string{
|
||||||
|
"X-Swift-To": adaLovelaceAddr.Address,
|
||||||
|
"Content-Type": "multipart/alternative",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Matcher: and(matchBefore, matchHeaders),
|
||||||
|
Expect: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
func(tc emailMatcherTestCase) {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if e, g := tc.Expect, tc.Matcher(tc.Email, tc.Search); e != g {
|
||||||
|
t.Errorf("'%s': expected '%v', got '%v'", tc.Name, e, g)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}(tc)
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ type InboxSearch struct {
|
|||||||
From string
|
From string
|
||||||
Body string
|
Body string
|
||||||
Subject string
|
Subject string
|
||||||
|
Headers map[string]string
|
||||||
After time.Time
|
After time.Time
|
||||||
Before time.Time
|
Before time.Time
|
||||||
}
|
}
|
||||||
@ -104,55 +105,135 @@ func HandleGetInbox(ctx context.Context, qry cqrs.Query) (interface{}, error) {
|
|||||||
return &InboxData{emails}, nil
|
return &InboxData{emails}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
filtered := make([]*model.Email, 0, len(emails))
|
filtered := filterEmails(emails, req.Search)
|
||||||
|
|
||||||
for _, eml := range emails {
|
|
||||||
match := true
|
|
||||||
|
|
||||||
if req.Search.To != "" {
|
|
||||||
found := false
|
|
||||||
|
|
||||||
for _, addr := range eml.To {
|
|
||||||
if strings.Contains(addr.Name, req.Search.To) || strings.Contains(addr.Address, req.Search.To) {
|
|
||||||
found = true
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
match = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Search.From != "" {
|
|
||||||
found := false
|
|
||||||
|
|
||||||
for _, addr := range eml.From {
|
|
||||||
if strings.Contains(addr.Name, req.Search.From) || strings.Contains(addr.Address, req.Search.From) {
|
|
||||||
found = true
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
match = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !req.Search.After.IsZero() && !eml.SentAt.After(req.Search.After) {
|
|
||||||
match = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !req.Search.Before.IsZero() && !eml.SentAt.Before(req.Search.Before) {
|
|
||||||
match = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if match {
|
|
||||||
filtered = append(filtered, eml)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &InboxData{filtered}, nil
|
return &InboxData{filtered}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var matchers = []emailMatcherFunc{
|
||||||
|
matchTo,
|
||||||
|
matchFrom,
|
||||||
|
matchBefore,
|
||||||
|
matchAfter,
|
||||||
|
matchHeaders,
|
||||||
|
}
|
||||||
|
|
||||||
|
type emailMatcherFunc func(*model.Email, *InboxSearch) bool
|
||||||
|
|
||||||
|
func matchTo(eml *model.Email, search *InboxSearch) bool {
|
||||||
|
if search.To == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
|
||||||
|
for _, addr := range eml.To {
|
||||||
|
if strings.Contains(addr.Name, search.To) || strings.Contains(addr.Address, search.To) {
|
||||||
|
found = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchFrom(eml *model.Email, search *InboxSearch) bool {
|
||||||
|
if search.From == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
|
||||||
|
for _, addr := range eml.From {
|
||||||
|
if strings.Contains(addr.Name, search.From) || strings.Contains(addr.Address, search.From) {
|
||||||
|
found = true
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchAfter(eml *model.Email, search *InboxSearch) bool {
|
||||||
|
if search.After.IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return eml.SentAt.After(search.After)
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchBefore(eml *model.Email, search *InboxSearch) bool {
|
||||||
|
if search.Before.IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return eml.SentAt.Before(search.Before)
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchHeaders(eml *model.Email, search *InboxSearch) bool {
|
||||||
|
if eml.Headers == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := true
|
||||||
|
|
||||||
|
for searchKey, searchValue := range search.Headers {
|
||||||
|
for headerKey, headerValues := range eml.Headers {
|
||||||
|
if searchKey != headerKey {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
matchesHeader := true
|
||||||
|
|
||||||
|
for _, hv := range headerValues {
|
||||||
|
if !strings.Contains(hv, searchValue) {
|
||||||
|
matchesHeader = false
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matchesHeader {
|
||||||
|
matches = false
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matches {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
func and(matchers ...emailMatcherFunc) emailMatcherFunc {
|
||||||
|
return func(eml *model.Email, search *InboxSearch) bool {
|
||||||
|
for _, match := range matchers {
|
||||||
|
if !match(eml, search) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterEmails(emails []*model.Email, search *InboxSearch) []*model.Email {
|
||||||
|
filtered := make([]*model.Email, 0)
|
||||||
|
|
||||||
|
match := and(matchers...)
|
||||||
|
|
||||||
|
for _, eml := range emails {
|
||||||
|
if !match(eml, search) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered = append(filtered, eml)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package route
|
package route
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"forge.cadoles.com/wpetit/fake-smtp/internal/config"
|
||||||
"forge.cadoles.com/wpetit/fake-smtp/internal/query"
|
"forge.cadoles.com/wpetit/fake-smtp/internal/query"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gitlab.com/wpetit/goweb/middleware/container"
|
"gitlab.com/wpetit/goweb/middleware/container"
|
||||||
|
"gitlab.com/wpetit/goweb/service"
|
||||||
"gitlab.com/wpetit/goweb/service/template"
|
"gitlab.com/wpetit/goweb/service/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,8 +18,8 @@ func extendTemplateData(w http.ResponseWriter, r *http.Request, data template.Da
|
|||||||
ctn := container.Must(r.Context())
|
ctn := container.Must(r.Context())
|
||||||
data, err := template.Extend(data,
|
data, err := template.Extend(data,
|
||||||
template.WithBuildInfo(w, r, ctn),
|
template.WithBuildInfo(w, r, ctn),
|
||||||
|
withBaseURL(ctn),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(errors.Wrap(err, "could not extend template data"))
|
panic(errors.Wrap(err, "could not extend template data"))
|
||||||
}
|
}
|
||||||
@ -73,6 +76,16 @@ func createInboxQueryFromRequest(r *http.Request) (*query.GetInboxRequest, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var headers map[string]string
|
||||||
|
|
||||||
|
rawHeaders := r.URL.Query().Get("headers")
|
||||||
|
if rawHeaders != "" {
|
||||||
|
headers = make(map[string]string)
|
||||||
|
if err := json.Unmarshal([]byte(rawHeaders), &headers); err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
search := &query.InboxSearch{}
|
search := &query.InboxSearch{}
|
||||||
if to != "" {
|
if to != "" {
|
||||||
search.To = to
|
search.To = to
|
||||||
@ -94,6 +107,10 @@ func createInboxQueryFromRequest(r *http.Request) (*query.GetInboxRequest, error
|
|||||||
search.Before = before
|
search.Before = before
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rawHeaders != "" {
|
||||||
|
search.Headers = headers
|
||||||
|
}
|
||||||
|
|
||||||
inboxRequest := &query.GetInboxRequest{
|
inboxRequest := &query.GetInboxRequest{
|
||||||
OrderBy: orderBy,
|
OrderBy: orderBy,
|
||||||
Reverse: reverse == "y",
|
Reverse: reverse == "y",
|
||||||
@ -104,3 +121,16 @@ func createInboxQueryFromRequest(r *http.Request) (*query.GetInboxRequest, error
|
|||||||
|
|
||||||
return inboxRequest, nil
|
return inboxRequest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withBaseURL(ctn *service.Container) template.DataExtFunc {
|
||||||
|
return func(data template.Data) (template.Data, error) {
|
||||||
|
conf, err := config.From(ctn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data["BaseURL"] = conf.HTTP.BaseURL
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
5
misc/api.http
Normal file
5
misc/api.http
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@baseURL = http://localhost:8080
|
||||||
|
|
||||||
|
### Filter emails via headers
|
||||||
|
|
||||||
|
GET {{ baseURL }}/api/v1/emails?headers={"Mime-Version":"1.0"}
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.13 AS build
|
FROM golang:1.19 AS build
|
||||||
|
|
||||||
ARG HTTP_PROXY=
|
ARG HTTP_PROXY=
|
||||||
ARG HTTPS_PROXY=
|
ARG HTTPS_PROXY=
|
||||||
@ -7,20 +7,15 @@ ARG https_proxy=
|
|||||||
|
|
||||||
RUN apt-get update && apt-get install -y build-essential git bash curl
|
RUN apt-get update && apt-get install -y build-essential git bash curl
|
||||||
|
|
||||||
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \
|
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
|
||||||
&& apt-get install -y nodejs
|
&& apt-get install -y nodejs
|
||||||
|
|
||||||
COPY . /src
|
COPY . /src
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
RUN cp -f misc/docker/config-patch.yml misc/release/config-patch.yml
|
RUN cp -f misc/docker/config-patch.txt misc/release/config-patch.txt \
|
||||||
|
&& npm install \
|
||||||
RUN go get github.com/krishicks/yaml-patch/cmd/yaml-patch
|
|
||||||
|
|
||||||
RUN npm install \
|
|
||||||
&& make vendor \
|
|
||||||
&& echo "---" > ./misc/release/config-patch.yml \
|
|
||||||
&& make ARCH_TARGETS=amd64 release
|
&& make ARCH_TARGETS=amd64 release
|
||||||
|
|
||||||
FROM busybox
|
FROM busybox
|
||||||
@ -31,4 +26,6 @@ WORKDIR /app
|
|||||||
|
|
||||||
RUN mkdir -p /app
|
RUN mkdir -p /app
|
||||||
|
|
||||||
|
EXPOSE 8080 2525
|
||||||
|
|
||||||
CMD ["bin/fake-smtp", "--config", "config.yml"]
|
CMD ["bin/fake-smtp", "--config", "config.yml"]
|
1
misc/docker/config-patch.txt
Normal file
1
misc/docker/config-patch.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
.smtp.debug = false
|
@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
- op: replace
|
|
||||||
path: /smtp/debug
|
|
||||||
value: false
|
|
3
misc/release/config-patch.txt
Normal file
3
misc/release/config-patch.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.data.path = "/var/lib/fake-smtp/data.db"
|
||||||
|
.smtp.address = "127.0.0.1:2525"
|
||||||
|
.smtp.debug = false
|
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
- op: replace
|
|
||||||
path: /data/path
|
|
||||||
value: /var/lib/fake-smtp/data.db
|
|
||||||
- op: replace
|
|
||||||
path: /smtp/address
|
|
||||||
value: 127.0.0.1:2525
|
|
||||||
- op: replace
|
|
||||||
path: /smtp/debug
|
|
||||||
value: false
|
|
@ -73,13 +73,17 @@ function dump_default_conf {
|
|||||||
local command=$1
|
local command=$1
|
||||||
local os=$2
|
local os=$2
|
||||||
local arch=$3
|
local arch=$3
|
||||||
local tmp_conf=$(mktemp)
|
|
||||||
local patched_conf=$(mktemp)
|
local patched_conf=$(mktemp)
|
||||||
|
|
||||||
go run "$PROJECT_DIR/cmd/$command" -dump-config > "$tmp_conf"
|
go run "$PROJECT_DIR/cmd/$command" -dump-config > "$patched_conf"
|
||||||
cat "$tmp_conf" | yaml-patch -o misc/release/config-patch.yml > "$patched_conf"
|
|
||||||
|
while IFS= read -r yq_cmd; do
|
||||||
|
echo "patching configuration with '$yq_cmd'..."
|
||||||
|
tools/yq/bin/yq -i "$yq_cmd" "$patched_conf"
|
||||||
|
done < misc/release/config-patch.txt
|
||||||
|
|
||||||
copy "$command" $os $arch "$patched_conf" "config.yml"
|
copy "$command" $os $arch "$patched_conf" "config.yml"
|
||||||
rm -f "$tmp_conf" "$patched_conf"
|
rm -f "$patched_conf"
|
||||||
}
|
}
|
||||||
|
|
||||||
function compress {
|
function compress {
|
||||||
|
@ -6,7 +6,7 @@ for i in {1..10}; do
|
|||||||
-s "Test ${i}" \
|
-s "Test ${i}" \
|
||||||
-a README.md \
|
-a README.md \
|
||||||
-a package.json \
|
-a package.json \
|
||||||
foo_${i}@bar_${i}.com \
|
foo_${i}@bar${i}.com \
|
||||||
<<EOF
|
<<EOF
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus eget metus ornare, placerat mi nec, ornare magna. Vivamus volutpat nisi et aliquet mollis. Phasellus eu malesuada erat, vel molestie ipsum. Etiam tincidunt ligula eget scelerisque blandit. Vivamus nec quam vitae felis rutrum cursus a eget elit. Integer vestibulum ultrices iaculis. Integer varius sapien ac ante accumsan euismod. Quisque fermentum dui nec porttitor pellentesque. Vivamus lobortis mauris eget metus sagittis tempor.
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus eget metus ornare, placerat mi nec, ornare magna. Vivamus volutpat nisi et aliquet mollis. Phasellus eu malesuada erat, vel molestie ipsum. Etiam tincidunt ligula eget scelerisque blandit. Vivamus nec quam vitae felis rutrum cursus a eget elit. Integer vestibulum ultrices iaculis. Integer varius sapien ac ante accumsan euismod. Quisque fermentum dui nec porttitor pellentesque. Vivamus lobortis mauris eget metus sagittis tempor.
|
||||||
|
|
||||||
|
358
package-lock.json
generated
358
package-lock.json
generated
@ -1398,9 +1398,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"aws4": {
|
"aws4": {
|
||||||
"version": "1.9.1",
|
"version": "1.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
|
||||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
|
"integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"babel-loader": {
|
"babel-loader": {
|
||||||
@ -1884,14 +1884,48 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cliui": {
|
"cliui": {
|
||||||
"version": "3.2.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||||
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
|
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"string-width": "^1.0.1",
|
"string-width": "^3.1.0",
|
||||||
"strip-ansi": "^3.0.1",
|
"strip-ansi": "^5.2.0",
|
||||||
"wrap-ansi": "^2.0.0"
|
"wrap-ansi": "^5.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"emoji-regex": "^7.0.1",
|
||||||
|
"is-fullwidth-code-point": "^2.0.0",
|
||||||
|
"strip-ansi": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^4.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"clone-deep": {
|
"clone-deep": {
|
||||||
@ -3506,9 +3540,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"get-caller-file": {
|
"get-caller-file": {
|
||||||
"version": "1.0.3",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"get-stdin": {
|
"get-stdin": {
|
||||||
@ -3618,13 +3652,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"globule": {
|
"globule": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz",
|
||||||
"integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
|
"integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"glob": "~7.1.1",
|
"glob": "~7.1.1",
|
||||||
"lodash": "~4.17.12",
|
"lodash": "~4.17.10",
|
||||||
"minimatch": "~3.0.2"
|
"minimatch": "~3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3641,13 +3675,27 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"har-validator": {
|
"har-validator": {
|
||||||
"version": "5.1.3",
|
"version": "5.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||||
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "^6.5.5",
|
"ajv": "^6.12.3",
|
||||||
"har-schema": "^2.0.0"
|
"har-schema": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ajv": {
|
||||||
|
"version": "6.12.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"json-schema-traverse": "^0.4.1",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"has-ansi": {
|
"has-ansi": {
|
||||||
@ -3750,9 +3798,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hosted-git-info": {
|
"hosted-git-info": {
|
||||||
"version": "2.8.8",
|
"version": "2.8.9",
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||||
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
|
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"http-signature": {
|
"http-signature": {
|
||||||
@ -3873,12 +3921,6 @@
|
|||||||
"loose-envify": "^1.0.0"
|
"loose-envify": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"invert-kv": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"is-accessor-descriptor": {
|
"is-accessor-descriptor": {
|
||||||
"version": "0.1.6",
|
"version": "0.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
|
||||||
@ -4085,9 +4127,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"js-base64": {
|
"js-base64": {
|
||||||
"version": "2.5.2",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
|
||||||
"integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==",
|
"integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
@ -4159,15 +4201,6 @@
|
|||||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"lcid": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"invert-kv": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"leven": {
|
"leven": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||||
@ -4399,18 +4432,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mime-db": {
|
"mime-db": {
|
||||||
"version": "1.43.0",
|
"version": "1.48.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz",
|
||||||
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
|
"integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"mime-types": {
|
"mime-types": {
|
||||||
"version": "2.1.26",
|
"version": "2.1.31",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz",
|
||||||
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
"integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"mime-db": "1.43.0"
|
"mime-db": "1.48.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mimic-fn": {
|
"mimic-fn": {
|
||||||
@ -4671,9 +4704,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node-sass": {
|
"node-sass": {
|
||||||
"version": "4.13.1",
|
"version": "4.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz",
|
||||||
"integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
|
"integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"async-foreach": "^0.1.3",
|
"async-foreach": "^0.1.3",
|
||||||
@ -4690,7 +4723,7 @@
|
|||||||
"node-gyp": "^3.8.0",
|
"node-gyp": "^3.8.0",
|
||||||
"npmlog": "^4.0.0",
|
"npmlog": "^4.0.0",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"sass-graph": "^2.2.4",
|
"sass-graph": "2.2.5",
|
||||||
"stdout-stream": "^1.4.0",
|
"stdout-stream": "^1.4.0",
|
||||||
"true-case-path": "^1.0.2"
|
"true-case-path": "^1.0.2"
|
||||||
},
|
},
|
||||||
@ -4894,15 +4927,6 @@
|
|||||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
|
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"os-locale": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
|
|
||||||
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"lcid": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"os-tmpdir": {
|
"os-tmpdir": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
@ -5635,9 +5659,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"require-main-filename": {
|
"require-main-filename": {
|
||||||
"version": "1.0.1",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||||
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
|
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"resolve": {
|
"resolve": {
|
||||||
@ -5851,15 +5875,15 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"sass-graph": {
|
"sass-graph": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
|
||||||
"integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
|
"integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"glob": "^7.0.0",
|
"glob": "^7.0.0",
|
||||||
"lodash": "^4.0.0",
|
"lodash": "^4.0.0",
|
||||||
"scss-tokenizer": "^0.2.3",
|
"scss-tokenizer": "^0.2.3",
|
||||||
"yargs": "^7.0.0"
|
"yargs": "^13.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sass-loader": {
|
"sass-loader": {
|
||||||
@ -6182,9 +6206,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"spdx-correct": {
|
"spdx-correct": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
|
||||||
"integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
|
"integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"spdx-expression-parse": "^3.0.0",
|
"spdx-expression-parse": "^3.0.0",
|
||||||
@ -6192,15 +6216,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spdx-exceptions": {
|
"spdx-exceptions": {
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
|
||||||
"integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
|
"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"spdx-expression-parse": {
|
"spdx-expression-parse": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
|
||||||
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
|
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"spdx-exceptions": "^2.1.0",
|
"spdx-exceptions": "^2.1.0",
|
||||||
@ -6208,9 +6232,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spdx-license-ids": {
|
"spdx-license-ids": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz",
|
||||||
"integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
|
"integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"split-string": {
|
"split-string": {
|
||||||
@ -7183,9 +7207,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"which-module": {
|
"which-module": {
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
||||||
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
|
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"wide-align": {
|
"wide-align": {
|
||||||
@ -7207,13 +7231,48 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"wrap-ansi": {
|
"wrap-ansi": {
|
||||||
"version": "2.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
|
||||||
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"string-width": "^1.0.1",
|
"ansi-styles": "^3.2.0",
|
||||||
"strip-ansi": "^3.0.1"
|
"string-width": "^3.0.0",
|
||||||
|
"strip-ansi": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"emoji-regex": "^7.0.1",
|
||||||
|
"is-fullwidth-code-point": "^2.0.0",
|
||||||
|
"strip-ansi": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^4.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"wrappy": {
|
"wrappy": {
|
||||||
@ -7229,9 +7288,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"y18n": {
|
"y18n": {
|
||||||
"version": "3.2.1",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||||
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
|
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
@ -7241,47 +7300,114 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "7.1.0",
|
"version": "13.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
|
||||||
"integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
|
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"camelcase": "^3.0.0",
|
"cliui": "^5.0.0",
|
||||||
"cliui": "^3.2.0",
|
"find-up": "^3.0.0",
|
||||||
"decamelize": "^1.1.1",
|
"get-caller-file": "^2.0.1",
|
||||||
"get-caller-file": "^1.0.1",
|
|
||||||
"os-locale": "^1.4.0",
|
|
||||||
"read-pkg-up": "^1.0.1",
|
|
||||||
"require-directory": "^2.1.1",
|
"require-directory": "^2.1.1",
|
||||||
"require-main-filename": "^1.0.1",
|
"require-main-filename": "^2.0.0",
|
||||||
"set-blocking": "^2.0.0",
|
"set-blocking": "^2.0.0",
|
||||||
"string-width": "^1.0.2",
|
"string-width": "^3.0.0",
|
||||||
"which-module": "^1.0.0",
|
"which-module": "^2.0.0",
|
||||||
"y18n": "^3.2.1",
|
"y18n": "^4.0.0",
|
||||||
"yargs-parser": "^5.0.0"
|
"yargs-parser": "^13.1.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"camelcase": {
|
"ansi-regex": {
|
||||||
"version": "3.0.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
|
||||||
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
|
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"find-up": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"locate-path": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"locate-path": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-locate": "^3.0.0",
|
||||||
|
"path-exists": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-locate": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-limit": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"emoji-regex": "^7.0.1",
|
||||||
|
"is-fullwidth-code-point": "^2.0.0",
|
||||||
|
"strip-ansi": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^4.1.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"yargs-parser": {
|
"yargs-parser": {
|
||||||
"version": "5.0.0",
|
"version": "13.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
|
||||||
"integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
|
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"camelcase": "^3.0.0"
|
"camelcase": "^5.0.0",
|
||||||
|
"decamelize": "^1.2.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"camelcase": {
|
"camelcase": {
|
||||||
"version": "3.0.0",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||||
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
|
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"css-loader": "^3.5.2",
|
"css-loader": "^3.5.2",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
"mini-css-extract-plugin": "^0.9.0",
|
"mini-css-extract-plugin": "^0.9.0",
|
||||||
"node-sass": "^4.10.0",
|
"node-sass": "^4.14.1",
|
||||||
"resolve-url-loader": "^3.0.0",
|
"resolve-url-loader": "^3.0.0",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"stimulus": "^1.1.0",
|
"stimulus": "^1.1.0",
|
||||||
|
Reference in New Issue
Block a user