From 1a8a8aea8c389a8f5e40cb7fd3f89687f8b6e61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Fri, 22 Nov 2019 01:39:15 +0100 Subject: [PATCH 01/23] migrate to go modules --- Dockerfile | 13 ++-- dev.Dockerfile | 9 +-- docker-compose.yml | 2 +- go.mod | 11 +++ go.sum | 189 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 211 insertions(+), 13 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/Dockerfile b/Dockerfile index 5d7833f..bd5aa77 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,16 +8,15 @@ RUN apk upgrade RUN apk add --update git # Copy sources -RUN mkdir -p $GOPATH/src/github.com/cthit/gotify -COPY . $GOPATH/src/github.com/cthit/gotify -WORKDIR $GOPATH/src/github.com/cthit/gotify/cmd +RUN mkdir /app +COPY . /app +WORKDIR /app/cmd # Grab dependencies -RUN go get -d -v ./... +RUN go mod download # build binary -RUN go install -v -RUN mkdir /app && mv $GOPATH/bin/cmd /app/gotify +RUN go build -o gotify ########################## # PRODUCTION STAGE # @@ -34,7 +33,7 @@ RUN adduser -S -G app -s /bin/bash app USER app:app # Copy binary -COPY --from=buildStage /app/gotify /app/gotify +COPY --from=buildStage /app/cmd/gotify /app/gotify # Set good defaults WORKDIR /app diff --git a/dev.Dockerfile b/dev.Dockerfile index c783e65..8f815e4 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -7,14 +7,13 @@ RUN apk update RUN apk upgrade RUN apk add --update git -RUN mkdir -p $GOPATH/bin && \ - go get github.com/codegangsta/gin +RUN go get github.com/codegangsta/gin # Add standard certificates RUN apk add ca-certificates && rm -rf /var/cache/apk/* # create dir -RUN mkdir -p /go/src/github.com/cthit/gotify -WORKDIR $GOPATH/src/github.com/cthit/gotify +RUN mkdir /app +WORKDIR /app -CMD go get -d -v ./... && gin -d cmd -a 8080 run main.go +CMD go mod download && gin -d cmd -a 8080 run main.go diff --git a/docker-compose.yml b/docker-compose.yml index 7e8f32b..ac5b542 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: ports: - "8080:3000" volumes: - - .:/go/src/github.com/cthit/gotify + - .:/app environment: GOTIFY_PRE-SHARED-KEY: "123abc" GOTIFY_DEBUG-MODE: "true" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..28440f3 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/cthit/gotify + +go 1.13 + +require ( + github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b + github.com/spf13/viper v1.5.0 + golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 + golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 + google.golang.org/api v0.14.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..127fd95 --- /dev/null +++ b/go.sum @@ -0,0 +1,189 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b h1:g2Qcs0B+vOQE1L3a7WQ/JUUSzJnHbTz14qkJSqEWcF4= +github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b/go.mod h1:Ag7UMbZNGrnHwaXPJOUKJIVgx4QOWMOWZngrvsN6qak= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4= +github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 h1:MlY3mEfbnWGmUi4rtHOtNnnnN4UJRGSyLPx+DXA5Sq4= +golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 9d63f0c9e7005681208dac387f4cc620346ea61b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Fri, 22 Nov 2019 21:04:45 +0100 Subject: [PATCH 02/23] refactor to standard project structure --- Dockerfile | 6 +- cmd/config.go | 24 -------- cmd/gotify/main.go | 16 +++++ cmd/main.go | 60 ------------------- dev.Dockerfile | 2 +- internal/app/config/config.go | 48 +++++++++++++++ internal/app/main.go | 60 +++++++++++++++++++ {web => internal/app/web}/auth.go | 0 {web => internal/app/web}/mail.go | 10 ++-- {web => internal/app/web}/router.go | 13 ++-- {web => internal/app/web}/util.go | 0 mock/mock_service.go | 26 -------- .../mail/gmail/service.go | 24 +++----- mail.go => pkg/mail/mail.go | 2 +- pkg/mail/mock/service.go | 24 ++++++++ mail_service.go => pkg/mail/service.go | 2 +- 16 files changed, 173 insertions(+), 144 deletions(-) delete mode 100644 cmd/config.go create mode 100644 cmd/gotify/main.go delete mode 100644 cmd/main.go create mode 100644 internal/app/config/config.go create mode 100644 internal/app/main.go rename {web => internal/app/web}/auth.go (100%) rename {web => internal/app/web}/mail.go (82%) rename {web => internal/app/web}/router.go (70%) rename {web => internal/app/web}/util.go (100%) delete mode 100644 mock/mock_service.go rename google_mail/google_service.go => pkg/mail/gmail/service.go (67%) rename mail.go => pkg/mail/mail.go (90%) create mode 100644 pkg/mail/mock/service.go rename mail_service.go => pkg/mail/service.go (88%) diff --git a/Dockerfile b/Dockerfile index bd5aa77..038f52a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,13 +10,13 @@ RUN apk add --update git # Copy sources RUN mkdir /app COPY . /app -WORKDIR /app/cmd +WORKDIR /app/cmd/gotify # Grab dependencies RUN go mod download # build binary -RUN go build -o gotify +RUN go build ########################## # PRODUCTION STAGE # @@ -33,7 +33,7 @@ RUN adduser -S -G app -s /bin/bash app USER app:app # Copy binary -COPY --from=buildStage /app/cmd/gotify /app/gotify +COPY --from=buildStage /app/cmd/gotify/gotify /app/gotify # Set good defaults WORKDIR /app diff --git a/cmd/config.go b/cmd/config.go deleted file mode 100644 index bcc0a14..0000000 --- a/cmd/config.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "github.com/spf13/viper" -) - -func loadConfig() error { - viper.SetDefault("port", "8080") - viper.SetDefault("debug-mode", false) - viper.SetDefault("google-mail.keyfile", "gapps.json") - viper.SetDefault("mock-mode", false) - - viper.SetEnvPrefix("gotify") - viper.AutomaticEnv() - - viper.SetConfigName("config") // name of config file (without extension) - viper.AddConfigPath("/etc/gotify/") // path to look for the config file in - viper.AddConfigPath("$HOME/.gotify/") // call multiple times to add many search paths - viper.AddConfigPath(".") // optionally look for config in the working directory - - err := viper.ReadInConfig() // Find and read the config file - return err - -} diff --git a/cmd/gotify/main.go b/cmd/gotify/main.go new file mode 100644 index 0000000..5b4e63d --- /dev/null +++ b/cmd/gotify/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + "github.com/cthit/gotify/internal/app" + "os" +) + +func main() { + err := app.Start() + if err != nil { + fmt.Printf("Crash: %v\n", err) + os.Exit(1) + } + os.Exit(0) +} diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index b252f7f..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "fmt" - "github.com/cthit/gotify" - "github.com/cthit/gotify/google_mail" - "github.com/cthit/gotify/mock" - "github.com/cthit/gotify/web" - "github.com/spf13/viper" - "log" - "net/http" -) - -func init() { - err := loadConfig() - if err != nil { - fmt.Println("Failed to load config.") - } else { - fmt.Println("Loaded config.") - - } -} - -func main() { - fmt.Printf("Debug mode is set to: %t \n", viper.GetBool("debug-mode")) - fmt.Printf("Mock mode is set to: %t \n", viper.GetBool("mock-mode")) - - fmt.Printf("Setting up services...") - - var mailServiceCreator func() gotify.MailService - var err error - - if !viper.GetBool("mock-mode") { - mailServiceCreator, err = google_mail.NewGoogleMailServiceCreator( - viper.GetString("google-mail.keyfile"), - viper.GetString("google-mail.admin-mail"), - viper.GetBool("debug-mode"), - ) - if err != nil { - panic(err) - } - } else { - mailServiceCreator, _ = mock.NewMockServiceCreator() - } - - preSharedKey := viper.GetString("pre-shared-key") - - fmt.Printf("Done! \n") - - fmt.Printf("Serving application on port %s \n", viper.GetString("port")) - log.Fatal( - http.ListenAndServe(":"+viper.GetString("port"), - web.Router( - preSharedKey, - mailServiceCreator, - viper.GetBool("debug-mode"), - ), - ), - ) -} diff --git a/dev.Dockerfile b/dev.Dockerfile index 8f815e4..5320492 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -16,4 +16,4 @@ RUN apk add ca-certificates && rm -rf /var/cache/apk/* RUN mkdir /app WORKDIR /app -CMD go mod download && gin -d cmd -a 8080 run main.go +CMD go mod download && gin -d cmd/gotify -a 8080 run main.go diff --git a/internal/app/config/config.go b/internal/app/config/config.go new file mode 100644 index 0000000..0e17463 --- /dev/null +++ b/internal/app/config/config.go @@ -0,0 +1,48 @@ +package config + +import ( + "github.com/spf13/viper" +) + +type Config struct {} + +func LoadConfig() (*Config, error) { + viper.SetDefault("port", "8080") + viper.SetDefault("debug-mode", false) + viper.SetDefault("google-mail.keyfile", "gapps.json") + viper.SetDefault("mock-mode", false) + + viper.SetEnvPrefix("gotify") + viper.AutomaticEnv() + + viper.SetConfigName("config") + viper.AddConfigPath("/etc/gotify/") + viper.AddConfigPath(".") + + err := viper.ReadInConfig() + return &Config{}, err +} + +func (*Config) Port() string { + return viper.GetString("port") +} + +func (*Config) Debug() bool { + return viper.GetBool("debug-mode") +} + +func (*Config) PreSharedKey() string { + return viper.GetString("pre-shared-key") +} + +func (*Config) Mock() bool { + return viper.GetBool("mock-mode") +} + +func (*Config) GmailKeyfile() string { + return viper.GetString("google-mail.keyfile") +} + +func (*Config) GmailAdminMail() string { + return viper.GetString("google-mail.admin-mail") +} \ No newline at end of file diff --git a/internal/app/main.go b/internal/app/main.go new file mode 100644 index 0000000..5c78b3b --- /dev/null +++ b/internal/app/main.go @@ -0,0 +1,60 @@ +package app + +import ( + "github.com/cthit/gotify/internal/app/config" + "github.com/cthit/gotify/internal/app/web" + "github.com/cthit/gotify/pkg/mail" + "github.com/cthit/gotify/pkg/mail/gmail" + "github.com/cthit/gotify/pkg/mail/mock" + + "fmt" + "log" + "net/http" +) + + +func Start() error { + c, err := config.LoadConfig() + if err != nil { + fmt.Println("Failed to load config.") + return err + } else { + fmt.Println("Loaded config.") + } + + fmt.Printf("Debug mode is set to: %t \n", c.Debug()) + fmt.Printf("Mock mode is set to: %t \n", c.Mock()) + + fmt.Printf("Setting up services...") + + var mailService mail.MailService + + if !c.Mock() { + mailService, err = gmail.NewService( + c.GmailKeyfile(), + c.GmailAdminMail(), + c.Debug(), + ) + if err != nil { + return err + } + } else { + mailService, _ = mock.NewService() + } + + preSharedKey := c.PreSharedKey() + + fmt.Printf("Done! \n") + + fmt.Printf("Serving application on port %s \n", c.Port()) + log.Fatal( + http.ListenAndServe(":"+c.Port(), + web.Router( + preSharedKey, + mailService, + c.Debug(), + ), + ), + ) + return nil +} diff --git a/web/auth.go b/internal/app/web/auth.go similarity index 100% rename from web/auth.go rename to internal/app/web/auth.go diff --git a/web/mail.go b/internal/app/web/mail.go similarity index 82% rename from web/mail.go rename to internal/app/web/mail.go index 58adbd5..41084f0 100644 --- a/web/mail.go +++ b/internal/app/web/mail.go @@ -2,14 +2,14 @@ package web import ( "encoding/json" - "github.com/cthit/gotify" + "github.com/cthit/gotify/pkg/mail" "github.com/gocraft/web" "io/ioutil" "net/http" ) func (c *Context) SendMail(rw web.ResponseWriter, req *web.Request) { - var mail gotify.Mail + var m mail.Mail // Read request body body, err := ioutil.ReadAll(req.Body) @@ -21,7 +21,7 @@ func (c *Context) SendMail(rw web.ResponseWriter, req *web.Request) { } // Parse json email - err = json.Unmarshal(body, &mail) + err = json.Unmarshal(body, &m) if err != nil { c.printError(err) rw.WriteHeader(http.StatusBadRequest) @@ -29,7 +29,7 @@ func (c *Context) SendMail(rw web.ResponseWriter, req *web.Request) { } // Send email - mail, err = c.MailService.SendMail(mail) + m, err = c.MailService.SendMail(m) if err != nil { c.printError(err) rw.WriteHeader(http.StatusInternalServerError) @@ -37,7 +37,7 @@ func (c *Context) SendMail(rw web.ResponseWriter, req *web.Request) { } // Build json email - data, err := json.Marshal(mail) + data, err := json.Marshal(m) if err != nil { c.printError(err) rw.WriteHeader(http.StatusInternalServerError) diff --git a/web/router.go b/internal/app/web/router.go similarity index 70% rename from web/router.go rename to internal/app/web/router.go index 0061a59..577f86f 100644 --- a/web/router.go +++ b/internal/app/web/router.go @@ -1,18 +1,18 @@ package web import ( - "github.com/cthit/gotify" + "github.com/cthit/gotify/pkg/mail" "github.com/gocraft/web" "net/http" ) type Context struct { - MailService gotify.MailService + MailService mail.MailService AuthKey string Debug bool } -func Router(authKey string, mailServiceCreator func() gotify.MailService, debug bool) http.Handler { +func Router(authKey string, mailService mail.MailService, debug bool) http.Handler { router := web.NewWithPrefix( Context{}, @@ -24,7 +24,7 @@ func Router(authKey string, mailServiceCreator func() gotify.MailService, debug } router.Middleware(setDebugMode(debug)) - router.Middleware(setMailServiceProvider(mailServiceCreator)) + router.Middleware(setMailServiceProvider(mailService)) router.Middleware(setAuthKey(authKey)) router.Middleware((*Context).Auth) @@ -32,11 +32,10 @@ func Router(authKey string, mailServiceCreator func() gotify.MailService, debug return router } -func setMailServiceProvider(mailServiceProvider func() gotify.MailService) func(*Context, web.ResponseWriter, *web.Request, web.NextMiddlewareFunc) { +func setMailServiceProvider(mailService mail.MailService) func(*Context, web.ResponseWriter, *web.Request, web.NextMiddlewareFunc) { return func(c *Context, rw web.ResponseWriter, req *web.Request, next web.NextMiddlewareFunc) { - c.MailService = mailServiceProvider() + c.MailService = mailService next(rw, req) - c.MailService.Destroy() } } diff --git a/web/util.go b/internal/app/web/util.go similarity index 100% rename from web/util.go rename to internal/app/web/util.go diff --git a/mock/mock_service.go b/mock/mock_service.go deleted file mode 100644 index 3d0d6f3..0000000 --- a/mock/mock_service.go +++ /dev/null @@ -1,26 +0,0 @@ -package mock - -import ( - "fmt" - "github.com/cthit/gotify" -) - -type mockService struct { -} - -func NewMockServiceCreator() (func() gotify.MailService, error) { - return func() gotify.MailService { - return &mockService{} - }, nil -} - -func (g *mockService) SendMail(mail gotify.Mail) (gotify.Mail, error) { - - fmt.Printf("Sending mail:\n %#v \n", mail) - - return mail, nil -} - -func (g *mockService) Destroy() error { - return nil -} diff --git a/google_mail/google_service.go b/pkg/mail/gmail/service.go similarity index 67% rename from google_mail/google_service.go rename to pkg/mail/gmail/service.go index 7b07737..6065705 100644 --- a/google_mail/google_service.go +++ b/pkg/mail/gmail/service.go @@ -1,15 +1,15 @@ -package google_mail +package gmail import ( - "google.golang.org/api/gmail/v1" // Imports as gmail + "github.com/cthit/gotify/pkg/mail" + + "google.golang.org/api/gmail/v1" "golang.org/x/net/context" "golang.org/x/oauth2/google" "encoding/base64" "io/ioutil" - - "github.com/cthit/gotify" ) type googleService struct { @@ -18,7 +18,7 @@ type googleService struct { debug bool } -func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) (func() gotify.MailService, error) { +func NewService(keyPath string, adminMail string, debug bool) (mail.MailService, error) { jsonKey, err := ioutil.ReadFile(keyPath) if err != nil { @@ -34,10 +34,7 @@ func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) ( // Why do I need this?? config.Subject = adminMail - // Create a http client - client := config.Client(context.Background()) - - mailService, err := gmail.New(client) + mailService, err := gmail.NewService(context.Background()) if err != nil { return nil, err } @@ -47,16 +44,11 @@ func NewGoogleMailServiceCreator(keyPath string, adminMail string, debug bool) ( adminMail: adminMail, debug: debug, } - if err != nil { - return nil, err - } - return func() gotify.MailService { - return gs - }, nil + return gs, err } -func (g *googleService) SendMail(mail gotify.Mail) (gotify.Mail, error) { +func (g *googleService) SendMail(mail mail.Mail) (mail.Mail, error) { mail.From = g.adminMail diff --git a/mail.go b/pkg/mail/mail.go similarity index 90% rename from mail.go rename to pkg/mail/mail.go index 7cf0d81..b393a60 100644 --- a/mail.go +++ b/pkg/mail/mail.go @@ -1,4 +1,4 @@ -package gotify +package mail type Mail struct { To string `json:"to"` diff --git a/pkg/mail/mock/service.go b/pkg/mail/mock/service.go new file mode 100644 index 0000000..8880dfc --- /dev/null +++ b/pkg/mail/mock/service.go @@ -0,0 +1,24 @@ +package mock + +import ( + "fmt" + "github.com/cthit/gotify/pkg/mail" +) + +type mockService struct { +} + +func NewService() (mail.MailService, error) { + return &mockService{}, nil +} + +func (g *mockService) SendMail(mail mail.Mail) (mail.Mail, error) { + + fmt.Printf("Sending mail:\n %#v \n", mail) + + return mail, nil +} + +func (g *mockService) Destroy() error { + return nil +} diff --git a/mail_service.go b/pkg/mail/service.go similarity index 88% rename from mail_service.go rename to pkg/mail/service.go index 4dfcbc2..57e278e 100644 --- a/mail_service.go +++ b/pkg/mail/service.go @@ -1,4 +1,4 @@ -package gotify +package mail type MailService interface { SendMail(mail Mail) (Mail, error) // Returns the actually sent email From b10f468946c693f899ecefd361d2b1ca5da1a87b Mon Sep 17 00:00:00 2001 From: William Date: Tue, 3 Mar 2020 23:25:15 +0100 Subject: [PATCH 03/23] use new api aendpoint and allow for utf-8 in subject resolves #6 --- README.md | 3 +- docker-compose.yml | 3 +- go.sum | 10 +++++ internal/app/config/config.go | 14 ++++--- internal/app/main.go | 1 - pkg/mail/gmail/service.go | 71 +++++++++++++++++++++-------------- pkg/mail/util.go | 7 ++++ 7 files changed, 71 insertions(+), 38 deletions(-) create mode 100644 pkg/mail/util.go diff --git a/README.md b/README.md index 7a3a139..b8be515 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,6 @@ mock-mode = false [google-mail] keyfile = "gapps.json" - admin-mail = "admin@example.ex" ``` See [Environment Variables](#environment-variables) for config explanation @@ -52,7 +51,6 @@ See [Environment Variables](#environment-variables) for config explanation * `GOTIFY_PRE-SHARED-KEY`*: Random string used by other apps to authenticate * `GOTIFY_DEBUG-MODE`: Bool indicating debug mode defaults to `false` * `GOTIFY_GOOGLE-MAIL.KEYFILE`: the file described in [Google config file](#google-config-file) defaults to `gapps.json` -* `GOTIFY_GOOGLE-MAIL.ADMIN-MAIL`*: The google administrator email. ### Google config file This file (gapps.json by default config) should be placed in the working directory @@ -63,6 +61,7 @@ This file (gapps.json by default config) should be placed in the working directo Go to [Google developer console](https://console.developers.google.com) to retrieve this file * go to credentials +* create a project for this app if you don't already have one * create new service account för this app * use the downloaded file diff --git a/docker-compose.yml b/docker-compose.yml index ac5b542..2650616 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,5 +11,4 @@ services: environment: GOTIFY_PRE-SHARED-KEY: "123abc" GOTIFY_DEBUG-MODE: "true" - GOTIFY_GOOGLE-MAIL.KEYFILE: "gapps.json" - GOTIFY_GOOGLE-MAIL.ADMIN-MAIL: "admin@chalmers.it" \ No newline at end of file + GOTIFY_GOOGLE-MAIL.KEYFILE: "gapps.json" \ No newline at end of file diff --git a/go.sum b/go.sum index 127fd95..60049f1 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -16,6 +17,7 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= @@ -30,6 +32,7 @@ github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b h1:g2Qcs0B+vOQE1L3a7WQ github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b/go.mod h1:Ag7UMbZNGrnHwaXPJOUKJIVgx4QOWMOWZngrvsN6qak= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -40,6 +43,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -62,8 +66,10 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -75,6 +81,7 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -100,6 +107,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4= github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -166,6 +174,7 @@ google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -178,6 +187,7 @@ google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/internal/app/config/config.go b/internal/app/config/config.go index 0e17463..4104f10 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "github.com/spf13/viper" ) @@ -20,7 +21,14 @@ func LoadConfig() (*Config, error) { viper.AddConfigPath(".") err := viper.ReadInConfig() - return &Config{}, err + if err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + fmt.Println("Failed to read config from file") + } else { + return &Config{}, err + } + } + return &Config{}, nil } func (*Config) Port() string { @@ -41,8 +49,4 @@ func (*Config) Mock() bool { func (*Config) GmailKeyfile() string { return viper.GetString("google-mail.keyfile") -} - -func (*Config) GmailAdminMail() string { - return viper.GetString("google-mail.admin-mail") } \ No newline at end of file diff --git a/internal/app/main.go b/internal/app/main.go index 5c78b3b..7cd2f85 100644 --- a/internal/app/main.go +++ b/internal/app/main.go @@ -32,7 +32,6 @@ func Start() error { if !c.Mock() { mailService, err = gmail.NewService( c.GmailKeyfile(), - c.GmailAdminMail(), c.Debug(), ) if err != nil { diff --git a/pkg/mail/gmail/service.go b/pkg/mail/gmail/service.go index 6065705..7892f55 100644 --- a/pkg/mail/gmail/service.go +++ b/pkg/mail/gmail/service.go @@ -1,25 +1,40 @@ package gmail import ( - "github.com/cthit/gotify/pkg/mail" - - "google.golang.org/api/gmail/v1" + "fmt" + "io/ioutil" + "strings" - "golang.org/x/net/context" + "github.com/cthit/gotify/pkg/mail" "golang.org/x/oauth2/google" + "golang.org/x/oauth2/jwt" + "google.golang.org/api/gmail/v1" + "google.golang.org/api/option" "encoding/base64" - "io/ioutil" + + "golang.org/x/net/context" ) +const googleInvalidEmailErrorMessage = `Response: { + "error": "invalid_grant", + "error_description": "Invalid email or User ID" +}` + type googleService struct { - mailService *gmail.Service - adminMail string - debug bool + config jwt.Config + adminMail string + debug bool } -func NewService(keyPath string, adminMail string, debug bool) (mail.MailService, error) { +func (g *googleService) mailService(from string) (*gmail.Service, error) { + // make sure to not edit the original config + c := g.config + c.Subject = from + return gmail.NewService(context.TODO(), option.WithScopes(gmail.GmailSendScope), option.WithTokenSource(c.TokenSource(context.TODO()))) +} +func NewService(keyPath string, debug bool) (mail.MailService, error) { jsonKey, err := ioutil.ReadFile(keyPath) if err != nil { return nil, err @@ -31,38 +46,38 @@ func NewService(keyPath string, adminMail string, debug bool) (mail.MailService, return nil, err } - // Why do I need this?? - config.Subject = adminMail - - mailService, err := gmail.NewService(context.Background()) - if err != nil { - return nil, err - } - gs := &googleService{ - mailService: mailService, - adminMail: adminMail, - debug: debug, + config: *config, + debug: debug, } return gs, err } -func (g *googleService) SendMail(mail mail.Mail) (mail.Mail, error) { +func (g *googleService) SendMail(m mail.Mail) (mail.Mail, error) { - mail.From = g.adminMail + mailService, err := g.mailService(m.From) + if err != nil { + return m, err + } - msgRaw := "From: " + mail.From + "\r\n" + - "To: " + mail.To + "\r\n" + - "Subject: " + mail.Subject + "\r\n\r\n" + - mail.Body + "\r\n" + msgRaw := "From: " + m.From + "\r\n" + + "To: " + m.To + "\r\n" + + "Subject: " + mail.EncodeHeader(m.Subject) + "\r\n\r\n" + + m.Body + "\r\n" msg := &gmail.Message{ Raw: base64.RawURLEncoding.EncodeToString([]byte(msgRaw)), } - _, err := g.mailService.Users.Messages.Send(mail.From, msg).Do() + _, err = mailService.Users.Messages.Send(m.From, msg).Context(context.Background()).Do() + if err != nil { + if strings.Contains(err.Error(), googleInvalidEmailErrorMessage) { + return m, fmt.Errorf("Invalid from email, email must exists") + } + return m, err + } - return mail, err + return m, nil } func (g *googleService) Destroy() error { diff --git a/pkg/mail/util.go b/pkg/mail/util.go new file mode 100644 index 0000000..3a13e4d --- /dev/null +++ b/pkg/mail/util.go @@ -0,0 +1,7 @@ +package mail + +import "encoding/base64" + +func EncodeHeader(header string) string { + return "=?utf-8?B?" + base64.RawURLEncoding.EncodeToString([]byte(header)) + "?=" +} From d49a0dd6622bc247dc9df81abf20f6561b7385cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Thu, 5 Mar 2020 16:22:33 +0100 Subject: [PATCH 04/23] Replace gocraft webserver with gin --- docker-compose.yml | 1 + go.mod | 2 +- go.sum | 39 +++++++++++++++++++++++-- internal/app/config/config.go | 4 +-- internal/app/main.go | 26 ++++++++--------- internal/app/web/auth.go | 10 +++---- internal/app/web/mail.go | 38 ++++++------------------ internal/app/web/router.go | 54 ----------------------------------- internal/app/web/server.go | 39 +++++++++++++++++++++++++ internal/app/web/util.go | 11 ------- pkg/mail/mock/service.go | 2 +- 11 files changed, 105 insertions(+), 121 deletions(-) delete mode 100644 internal/app/web/router.go create mode 100644 internal/app/web/server.go delete mode 100644 internal/app/web/util.go diff --git a/docker-compose.yml b/docker-compose.yml index 2650616..2b2ffed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,4 +11,5 @@ services: environment: GOTIFY_PRE-SHARED-KEY: "123abc" GOTIFY_DEBUG-MODE: "true" + GOTIFY_MOCK-MODE: "true" GOTIFY_GOOGLE-MAIL.KEYFILE: "gapps.json" \ No newline at end of file diff --git a/go.mod b/go.mod index 28440f3..c05a190 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cthit/gotify go 1.13 require ( - github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b + github.com/gin-gonic/gin v1.5.0 github.com/spf13/viper v1.5.0 golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 diff --git a/go.sum b/go.sum index 60049f1..3f64596 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,7 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -24,12 +25,18 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc= +github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b h1:g2Qcs0B+vOQE1L3a7WQ/JUUSzJnHbTz14qkJSqEWcF4= -github.com/gocraft/web v0.0.0-20190207150652-9707327fb69b/go.mod h1:Ag7UMbZNGrnHwaXPJOUKJIVgx4QOWMOWZngrvsN6qak= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= @@ -40,11 +47,14 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -60,6 +70,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -71,11 +83,19 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= +github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= @@ -106,13 +126,21 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4= github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -156,6 +184,8 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -189,9 +219,14 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks 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/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= +gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/app/config/config.go b/internal/app/config/config.go index 4104f10..8dd0feb 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -5,7 +5,7 @@ import ( "github.com/spf13/viper" ) -type Config struct {} +type Config struct{} func LoadConfig() (*Config, error) { viper.SetDefault("port", "8080") @@ -49,4 +49,4 @@ func (*Config) Mock() bool { func (*Config) GmailKeyfile() string { return viper.GetString("google-mail.keyfile") -} \ No newline at end of file +} diff --git a/internal/app/main.go b/internal/app/main.go index 7cd2f85..af6cfe7 100644 --- a/internal/app/main.go +++ b/internal/app/main.go @@ -8,11 +8,8 @@ import ( "github.com/cthit/gotify/pkg/mail/mock" "fmt" - "log" - "net/http" ) - func Start() error { c, err := config.LoadConfig() if err != nil { @@ -41,19 +38,20 @@ func Start() error { mailService, _ = mock.NewService() } - preSharedKey := c.PreSharedKey() - fmt.Printf("Done! \n") fmt.Printf("Serving application on port %s \n", c.Port()) - log.Fatal( - http.ListenAndServe(":"+c.Port(), - web.Router( - preSharedKey, - mailService, - c.Debug(), - ), - ), + server, err := web.NewServer( + c.Port(), + c.PreSharedKey(), + c.Debug(), + mailService, ) - return nil + if err != nil { + fmt.Println("Failed to create webserver.") + return err + } + + err = server.Start() + return err } diff --git a/internal/app/web/auth.go b/internal/app/web/auth.go index f6af08a..af8a71e 100644 --- a/internal/app/web/auth.go +++ b/internal/app/web/auth.go @@ -2,14 +2,12 @@ package web import ( "fmt" - "github.com/gocraft/web" + "github.com/gin-gonic/gin" "net/http" ) -func (c *Context) Auth(rw web.ResponseWriter, req *web.Request, next web.NextMiddlewareFunc) { - if req.Header.Get("Authorization") == fmt.Sprintf("pre-shared: %s", c.AuthKey) { - next(rw, req) - } else { - rw.WriteHeader(http.StatusUnauthorized) +func (s *Server) MustAuthorize(c *gin.Context) { + if c.GetHeader("Authorization") != fmt.Sprintf("pre-shared: %s", s.preSharedKey) { + c.AbortWithStatus(http.StatusUnauthorized) } } diff --git a/internal/app/web/mail.go b/internal/app/web/mail.go index 41084f0..9530d88 100644 --- a/internal/app/web/mail.go +++ b/internal/app/web/mail.go @@ -1,50 +1,28 @@ package web import ( - "encoding/json" + "fmt" "github.com/cthit/gotify/pkg/mail" - "github.com/gocraft/web" - "io/ioutil" + "github.com/gin-gonic/gin" "net/http" ) -func (c *Context) SendMail(rw web.ResponseWriter, req *web.Request) { +func (s *Server) MailHandler(c *gin.Context) { var m mail.Mail - // Read request body - body, err := ioutil.ReadAll(req.Body) - req.Body.Close() + err := c.Bind(&m) if err != nil { - c.printError(err) - rw.WriteHeader(http.StatusBadRequest) - return - } - - // Parse json email - err = json.Unmarshal(body, &m) - if err != nil { - c.printError(err) - rw.WriteHeader(http.StatusBadRequest) + fmt.Println(err) return } // Send email - m, err = c.MailService.SendMail(m) - if err != nil { - c.printError(err) - rw.WriteHeader(http.StatusInternalServerError) - return - } - - // Build json email - data, err := json.Marshal(m) + m, err = s.mailService.SendMail(m) if err != nil { - c.printError(err) - rw.WriteHeader(http.StatusInternalServerError) + c.String(http.StatusInternalServerError, err.Error()) return } // Return the sent email - rw.WriteHeader(http.StatusOK) - rw.Write(data) + c.JSON(http.StatusAccepted, m) } diff --git a/internal/app/web/router.go b/internal/app/web/router.go deleted file mode 100644 index 577f86f..0000000 --- a/internal/app/web/router.go +++ /dev/null @@ -1,54 +0,0 @@ -package web - -import ( - "github.com/cthit/gotify/pkg/mail" - "github.com/gocraft/web" - "net/http" -) - -type Context struct { - MailService mail.MailService - AuthKey string - Debug bool -} - -func Router(authKey string, mailService mail.MailService, debug bool) http.Handler { - - router := web.NewWithPrefix( - Context{}, - "") - - router.Middleware(web.LoggerMiddleware) - if debug { - router.Middleware(web.ShowErrorsMiddleware) - } - - router.Middleware(setDebugMode(debug)) - router.Middleware(setMailServiceProvider(mailService)) - router.Middleware(setAuthKey(authKey)) - router.Middleware((*Context).Auth) - - router.Post("/mail", (*Context).SendMail) - return router -} - -func setMailServiceProvider(mailService mail.MailService) func(*Context, web.ResponseWriter, *web.Request, web.NextMiddlewareFunc) { - return func(c *Context, rw web.ResponseWriter, req *web.Request, next web.NextMiddlewareFunc) { - c.MailService = mailService - next(rw, req) - } -} - -func setAuthKey(authKey string) func(*Context, web.ResponseWriter, *web.Request, web.NextMiddlewareFunc) { - return func(c *Context, rw web.ResponseWriter, req *web.Request, next web.NextMiddlewareFunc) { - c.AuthKey = authKey - next(rw, req) - } -} - -func setDebugMode(debug bool) func(*Context, web.ResponseWriter, *web.Request, web.NextMiddlewareFunc) { - return func(c *Context, rw web.ResponseWriter, req *web.Request, next web.NextMiddlewareFunc) { - c.Debug = debug - next(rw, req) - } -} diff --git a/internal/app/web/server.go b/internal/app/web/server.go new file mode 100644 index 0000000..6c4b49c --- /dev/null +++ b/internal/app/web/server.go @@ -0,0 +1,39 @@ +package web + +import ( + "github.com/cthit/gotify/pkg/mail" + "github.com/gin-gonic/gin" +) + +type Server struct { + port string + preSharedKey string + debug bool + mailService mail.MailService +} + +func NewServer(port, preSharedKey string, debug bool, mailService mail.MailService) (*Server, error) { + return &Server{ + port: port, + preSharedKey: preSharedKey, + debug: debug, + mailService: mailService, + }, nil +} + +func (s *Server) Start() error { + if s.debug { + gin.SetMode(gin.DebugMode) + } else { + gin.SetMode(gin.ReleaseMode) + } + router := gin.New() + router.Use(gin.Logger()) + router.Use(gin.Recovery()) + authorized := router.Group("/") + authorized.Use(s.MustAuthorize) + { + authorized.POST("/mail", s.MailHandler) + } + return router.Run(":" + s.port) +} diff --git a/internal/app/web/util.go b/internal/app/web/util.go deleted file mode 100644 index cb859a0..0000000 --- a/internal/app/web/util.go +++ /dev/null @@ -1,11 +0,0 @@ -package web - -import "fmt" - -func (c *Context) printError(err error) { - if c.Debug { - fmt.Println(err) - } else { - fmt.Println("An error occurred, turn on debug mode for more info") - } -} diff --git a/pkg/mail/mock/service.go b/pkg/mail/mock/service.go index 8880dfc..bf710d8 100644 --- a/pkg/mail/mock/service.go +++ b/pkg/mail/mock/service.go @@ -9,7 +9,7 @@ type mockService struct { } func NewService() (mail.MailService, error) { - return &mockService{}, nil + return &mockService{}, nil } func (g *mockService) SendMail(mail mail.Mail) (mail.Mail, error) { From 58a8216b246bc6c8b1d62cfca7ad7c4471b3bc6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Fri, 6 Mar 2020 19:36:27 +0100 Subject: [PATCH 05/23] Protobuf file --- prototool.yaml | 190 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 prototool.yaml diff --git a/prototool.yaml b/prototool.yaml new file mode 100644 index 0000000..4ac51bf --- /dev/null +++ b/prototool.yaml @@ -0,0 +1,190 @@ +# Paths to exclude when searching for Protobuf files. +# These can either be file or directory names. +# If there is a directory name, that directory and all sub-directories will be excluded. +#excludes: +# - path/to/a +# - path/to/b/file.proto + +# Protoc directives. +protoc: + # The Protobuf version to use from https://github.com/protocolbuffers/protobuf/releases. + # By default use 3.8.0. + # You probably want to set this to make your builds completely reproducible. + version: 3.8.0 + + # Additional paths to include with -I to protoc. + # By default, the directory of the config file is included, + # or the current directory if there is no config file. +# includes: +# - ../../vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis + + + # If not set, compile will fail if there are unused imports. + # Setting this will ignore unused imports. +# allow_unused_imports: true + +# Create directives. +create: + # List of mappings from relative directory to base package. + # This affects how packages are generated with create. + packages: + # This means that a file created "foo.proto" in the current directory will have package "bar". + # A file created "a/b/foo.proto" will have package "bar.a.b". +# - directory: . +# name: bar + # This means that a file created "idl/code.uber/a/b/c.proto" will have package "uber.a.b". +# - directory: idl/code.uber +# name: uber + +# Lint directives. +lint: + # The lint group to use. + # Available groups: "uber1", "uber2", "google", "empty". + # The default group is the "uber1" lint group for backwards compatibility reasons, + # however we recommend using the "uber2" lint group. + # The special group "empty" has no linters, allowing you to manually specify all + # lint rules in lint.rules.add. + # Run prototool lint --list-all-lint-groups to see all available lint groups. + # Run prototool lint --list-lint-group GROUP to list the linters in the given lint group. + group: uber2 + + # Linter files to ignore. + # These can either be file or directory names. + # If there is a directory name, that directory and all sub-directories will be ignored. +# ignores: +# - id: RPC_NAMES_CAMEL_CASE +# files: +# - path/to/foo.proto +# - path/to/bar.proto +# - id: SYNTAX_PROTO3 +# files: +# - path/to/dir + + # Linter rules. + # Run prototool lint --list-all-linters to see all available linters. + # Run prototool lint --list-linters to see the currently configured linters. + rules: + + # The specific linters to add. + add: + - ENUM_NAMES_CAMEL_CASE + - ENUM_NAMES_CAPITALIZED + + # The specific linters to remove. +# remove: +# - ENUM_NAMES_CAMEL_CASE + + # The path to the file header or the file header content for all Protobuf files. + # If either path or content is set and the FILE_HEADER linter is turned on, + # files will be checked to begin with the given header, and format --fix + # will place this header before the syntax declaration. Note that + # format --fix will delete anything before the syntax declaration + # if this is set. + # + # Set path to use a file's contents for the header. Path must be relative. + # Set content to directly specify the header. + # **Both path and content cannot be set at the same time. They are only done + # so here for example purposes.** + # + # If is_commented is set, this file is assumed to already have comments + # and will be added directly. If is_commented is not set, "// " will be + # added before every line. +# file_header: +# path: path/to/protobuf_file_header.txt +# content: | +# // +# // Acme, Inc. (c) 2019 +# // +# is_commented: true + # Override the default java_package file option prefix of "com". + # If this is set, this will affect lint, create, and format --fix to use. + # this prefix instead of "com". +# java_package_prefix: au.com + +# Breaking change detector directives. +#break: + # Include beta packages in breaking change detection. + # Beta packages have the form "foo.bar.vMAJORbetaBETA" where MAJOR > 0 and BETA > 0. + # By default, beta packages are ignored. +# include_beta: true + # Allow stable packages to depend on beta packages. + # By default, the breaking change detector will error if a stable package + # depends on a breaking package. + # If include_beta is true, this is implicitly set. +# allow_beta_deps: true + +# Code generation directives. +generate: + # Options that will apply to all plugins of type go and gogo. + go_options: + # The base import path. This should be the go path of the prototool.yaml file. + # This is required if you have any go plugins. + import_path: github.com/cthit/gotify + + # Extra modifiers to include with Mfile=package. +# extra_modifiers: +# google/api/annotations.proto: google.golang.org/genproto/googleapis/api/annotations +# google/api/http.proto: google.golang.org/genproto/googleapis/api/annotations + + # The list of plugins. + plugins: + # The plugin name. This will go to protoc with --name_out, so it either needs + # to be a built-in name (like java), or a plugin name with a binary + # protoc-gen-name. + - name: gogo + + # The type, if any. Valid types are go, gogo. + # Use go if your plugin is a standard Golang plugin + # that uses github.com/golang/protobuf imports, use gogo + # if it uses github.com/gogo/protobuf imports. For protoc-gen-go + # use go, For protoc-gen-gogo, protoc-gen-gogoslick, etc, use gogo. + type: gogo + + # Extra flags to specify. + # The only flag you will generally set is plugins=grpc for Golang. + # The Mfile=package flags are automatically set. + # ** Otherwise, generally do not set this unless you know what you are doing. ** + flags: plugins=grpc + + # The path to output generated files to. + # If the directory does not exist, it will be created when running generation. + # This needs to be a relative path. + output: ../../.gen/proto/go + + # Optional override for the plugin path. For example, if you set set path to + # /usr/local/bin/gogo_plugin", prototool will add the + # "--plugin=protoc-gen-gogo=/usr/local/bin/gogo_plugin" flag to protoc calls. + # If set to "gogo_plugin", prototool will search your path for "gogo_plugin",. + # and fail if "gogo_plugin" cannot be found. +# path: gogo_plugin + +# - name: yarpc-go +# type: gogo +# output: ../../.gen/proto/go + + - name: grpc-gateway + type: go + output: ../../.gen/proto/go + +# - name: java +# output: ../../.gen/proto/java + + # Optional file suffix for plugins that output a single file as opposed + # to writing a set of files to a directory. This is only valid in two + # known cases: + # - For the java plugin, set this to "jar" to produce jars + # https://developers.google.com/protocol-buffers/docs/reference/java-generated#invocation + # - For the descriptor_set plugin, this is required as using descriptor_set + # requires a file to be given instead of a directory. +# file_suffix: jar + + # descriptor_set is special, and uses the --descriptor_set_out flag on protoc. + # file_suffix is required, and the options include_imports and include_source_info + # can be optionally set to add the flags --include_imports and --include_source-info. + # The include_imports and include_source_info options are not valid for any + # other plugin name. +# - name: descriptor_set +# output: ../../.gen/proto/descriptor +# file_suffix: bin +# include_imports: true +# include_source_info: true \ No newline at end of file From 3ba767f554b79449a14f09019cde98ce630d513f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Fri, 6 Mar 2020 19:41:29 +0100 Subject: [PATCH 06/23] The REAL proto file --- api/proto/v1/mail.proto | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 api/proto/v1/mail.proto diff --git a/api/proto/v1/mail.proto b/api/proto/v1/mail.proto new file mode 100644 index 0000000..14035ce --- /dev/null +++ b/api/proto/v1/mail.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package gotify; + +service Mailer { + rpc SendMail(SendMailRequest) returns (SendMailResponse) {} +} + +message SendMailRequest { + Mail mail = 1; +} + +message SendMailResponse { + Mail mail = 1; + bool success = 2; + string error = 3; +} + +message Mail { + string to = 1; + string from = 2; + string reply_to = 3; + string subject = 4; + string body = 5; +} \ No newline at end of file From d02ac3ec042a54a559de3a9961c1713d98d2d6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Fri, 6 Mar 2020 20:08:59 +0100 Subject: [PATCH 07/23] Protobuf fix --- api/swagger/v1/.keep | 0 pkg/api/v1/.keep | 0 prototool.yaml | 190 ------------------------------------------ scripts/protoc-gen.sh | 5 ++ 4 files changed, 5 insertions(+), 190 deletions(-) create mode 100644 api/swagger/v1/.keep create mode 100644 pkg/api/v1/.keep delete mode 100644 prototool.yaml create mode 100755 scripts/protoc-gen.sh diff --git a/api/swagger/v1/.keep b/api/swagger/v1/.keep new file mode 100644 index 0000000..e69de29 diff --git a/pkg/api/v1/.keep b/pkg/api/v1/.keep new file mode 100644 index 0000000..e69de29 diff --git a/prototool.yaml b/prototool.yaml deleted file mode 100644 index 4ac51bf..0000000 --- a/prototool.yaml +++ /dev/null @@ -1,190 +0,0 @@ -# Paths to exclude when searching for Protobuf files. -# These can either be file or directory names. -# If there is a directory name, that directory and all sub-directories will be excluded. -#excludes: -# - path/to/a -# - path/to/b/file.proto - -# Protoc directives. -protoc: - # The Protobuf version to use from https://github.com/protocolbuffers/protobuf/releases. - # By default use 3.8.0. - # You probably want to set this to make your builds completely reproducible. - version: 3.8.0 - - # Additional paths to include with -I to protoc. - # By default, the directory of the config file is included, - # or the current directory if there is no config file. -# includes: -# - ../../vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis - - - # If not set, compile will fail if there are unused imports. - # Setting this will ignore unused imports. -# allow_unused_imports: true - -# Create directives. -create: - # List of mappings from relative directory to base package. - # This affects how packages are generated with create. - packages: - # This means that a file created "foo.proto" in the current directory will have package "bar". - # A file created "a/b/foo.proto" will have package "bar.a.b". -# - directory: . -# name: bar - # This means that a file created "idl/code.uber/a/b/c.proto" will have package "uber.a.b". -# - directory: idl/code.uber -# name: uber - -# Lint directives. -lint: - # The lint group to use. - # Available groups: "uber1", "uber2", "google", "empty". - # The default group is the "uber1" lint group for backwards compatibility reasons, - # however we recommend using the "uber2" lint group. - # The special group "empty" has no linters, allowing you to manually specify all - # lint rules in lint.rules.add. - # Run prototool lint --list-all-lint-groups to see all available lint groups. - # Run prototool lint --list-lint-group GROUP to list the linters in the given lint group. - group: uber2 - - # Linter files to ignore. - # These can either be file or directory names. - # If there is a directory name, that directory and all sub-directories will be ignored. -# ignores: -# - id: RPC_NAMES_CAMEL_CASE -# files: -# - path/to/foo.proto -# - path/to/bar.proto -# - id: SYNTAX_PROTO3 -# files: -# - path/to/dir - - # Linter rules. - # Run prototool lint --list-all-linters to see all available linters. - # Run prototool lint --list-linters to see the currently configured linters. - rules: - - # The specific linters to add. - add: - - ENUM_NAMES_CAMEL_CASE - - ENUM_NAMES_CAPITALIZED - - # The specific linters to remove. -# remove: -# - ENUM_NAMES_CAMEL_CASE - - # The path to the file header or the file header content for all Protobuf files. - # If either path or content is set and the FILE_HEADER linter is turned on, - # files will be checked to begin with the given header, and format --fix - # will place this header before the syntax declaration. Note that - # format --fix will delete anything before the syntax declaration - # if this is set. - # - # Set path to use a file's contents for the header. Path must be relative. - # Set content to directly specify the header. - # **Both path and content cannot be set at the same time. They are only done - # so here for example purposes.** - # - # If is_commented is set, this file is assumed to already have comments - # and will be added directly. If is_commented is not set, "// " will be - # added before every line. -# file_header: -# path: path/to/protobuf_file_header.txt -# content: | -# // -# // Acme, Inc. (c) 2019 -# // -# is_commented: true - # Override the default java_package file option prefix of "com". - # If this is set, this will affect lint, create, and format --fix to use. - # this prefix instead of "com". -# java_package_prefix: au.com - -# Breaking change detector directives. -#break: - # Include beta packages in breaking change detection. - # Beta packages have the form "foo.bar.vMAJORbetaBETA" where MAJOR > 0 and BETA > 0. - # By default, beta packages are ignored. -# include_beta: true - # Allow stable packages to depend on beta packages. - # By default, the breaking change detector will error if a stable package - # depends on a breaking package. - # If include_beta is true, this is implicitly set. -# allow_beta_deps: true - -# Code generation directives. -generate: - # Options that will apply to all plugins of type go and gogo. - go_options: - # The base import path. This should be the go path of the prototool.yaml file. - # This is required if you have any go plugins. - import_path: github.com/cthit/gotify - - # Extra modifiers to include with Mfile=package. -# extra_modifiers: -# google/api/annotations.proto: google.golang.org/genproto/googleapis/api/annotations -# google/api/http.proto: google.golang.org/genproto/googleapis/api/annotations - - # The list of plugins. - plugins: - # The plugin name. This will go to protoc with --name_out, so it either needs - # to be a built-in name (like java), or a plugin name with a binary - # protoc-gen-name. - - name: gogo - - # The type, if any. Valid types are go, gogo. - # Use go if your plugin is a standard Golang plugin - # that uses github.com/golang/protobuf imports, use gogo - # if it uses github.com/gogo/protobuf imports. For protoc-gen-go - # use go, For protoc-gen-gogo, protoc-gen-gogoslick, etc, use gogo. - type: gogo - - # Extra flags to specify. - # The only flag you will generally set is plugins=grpc for Golang. - # The Mfile=package flags are automatically set. - # ** Otherwise, generally do not set this unless you know what you are doing. ** - flags: plugins=grpc - - # The path to output generated files to. - # If the directory does not exist, it will be created when running generation. - # This needs to be a relative path. - output: ../../.gen/proto/go - - # Optional override for the plugin path. For example, if you set set path to - # /usr/local/bin/gogo_plugin", prototool will add the - # "--plugin=protoc-gen-gogo=/usr/local/bin/gogo_plugin" flag to protoc calls. - # If set to "gogo_plugin", prototool will search your path for "gogo_plugin",. - # and fail if "gogo_plugin" cannot be found. -# path: gogo_plugin - -# - name: yarpc-go -# type: gogo -# output: ../../.gen/proto/go - - - name: grpc-gateway - type: go - output: ../../.gen/proto/go - -# - name: java -# output: ../../.gen/proto/java - - # Optional file suffix for plugins that output a single file as opposed - # to writing a set of files to a directory. This is only valid in two - # known cases: - # - For the java plugin, set this to "jar" to produce jars - # https://developers.google.com/protocol-buffers/docs/reference/java-generated#invocation - # - For the descriptor_set plugin, this is required as using descriptor_set - # requires a file to be given instead of a directory. -# file_suffix: jar - - # descriptor_set is special, and uses the --descriptor_set_out flag on protoc. - # file_suffix is required, and the options include_imports and include_source_info - # can be optionally set to add the flags --include_imports and --include_source-info. - # The include_imports and include_source_info options are not valid for any - # other plugin name. -# - name: descriptor_set -# output: ../../.gen/proto/descriptor -# file_suffix: bin -# include_imports: true -# include_source_info: true \ No newline at end of file diff --git a/scripts/protoc-gen.sh b/scripts/protoc-gen.sh new file mode 100755 index 0000000..c139f58 --- /dev/null +++ b/scripts/protoc-gen.sh @@ -0,0 +1,5 @@ +#!/usr/bin/bash + +protoc --proto_path=api/proto/v1 --go_out=plugins=grpc:pkg/api/v1 mail.proto +protoc --proto_path=api/proto/v1 --grpc-gateway_out=logtostderr=true:pkg/api/v1 mail.proto +protoc --proto_path=api/proto/v1 --swagger_out=logtostderr=true:api/swagger/v1 mail.proto \ No newline at end of file From 3cdd9f26125a9984b85b89aa2f81c245a10596ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Fri, 6 Mar 2020 21:58:43 +0100 Subject: [PATCH 08/23] Breaking but close --- .gitignore | 4 +- Makefile | 8 + api/proto/v1/mail.proto | 8 +- api/swagger/v1/.keep | 0 go.mod | 4 + go.sum | 21 +++ internal/app/config/config.go | 11 +- internal/app/grpc/sever.go | 92 +++++++++++ internal/app/main.go | 15 +- mail.pb.validate.go | 282 ++++++++++++++++++++++++++++++++++ pkg/api/v1/.keep | 0 scripts/protoc-gen.sh | 17 +- 12 files changed, 445 insertions(+), 17 deletions(-) create mode 100644 Makefile delete mode 100644 api/swagger/v1/.keep create mode 100644 internal/app/grpc/sever.go create mode 100644 mail.pb.validate.go delete mode 100644 pkg/api/v1/.keep diff --git a/.gitignore b/.gitignore index a7cf351..22ad477 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ config.toml .idea/ *.json -gin-bin \ No newline at end of file +gin-bin +api/swagger +pkg/api \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..00d56b9 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +.PHONY: gen +gen: + ./scripts/protoc-gen.sh + +.PHONY: clean +clean: + rm -r pkg/api + rm -r api/swagger \ No newline at end of file diff --git a/api/proto/v1/mail.proto b/api/proto/v1/mail.proto index 14035ce..7cbf570 100644 --- a/api/proto/v1/mail.proto +++ b/api/proto/v1/mail.proto @@ -2,8 +2,14 @@ syntax = "proto3"; package gotify; +import "google/api/annotations.proto"; + service Mailer { - rpc SendMail(SendMailRequest) returns (SendMailResponse) {} + rpc SendMail(SendMailRequest) returns (SendMailResponse) { + option(google.api.http) = { + post: "/mail" + }; + } } message SendMailRequest { diff --git a/api/swagger/v1/.keep b/api/swagger/v1/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/go.mod b/go.mod index c05a190..2386712 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,12 @@ go 1.13 require ( github.com/gin-gonic/gin v1.5.0 + github.com/golang/protobuf v1.3.4 + github.com/grpc-ecosystem/grpc-gateway v1.9.0 github.com/spf13/viper v1.5.0 golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 google.golang.org/api v0.14.0 + google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 + google.golang.org/grpc v1.21.0 ) diff --git a/go.sum b/go.sum index 3f64596..f402f25 100644 --- a/go.sum +++ b/go.sum @@ -38,7 +38,10 @@ github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rm github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -49,11 +52,20 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.3 h1:OzhhLcnXjTNb98owNUFibnt+cUqC2RSAGyFqxU4x3ok= +github.com/golang/protobuf v1.4.0-rc.3/go.mod h1:57iy8tErfL8eYKb9AjvkwLdYn7fzOM4yLPLUGc/0Cew= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -63,6 +75,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= @@ -75,6 +88,7 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -194,11 +208,13 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= @@ -215,6 +231,11 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.0 h1:SsQNHvKpk2VTiWoQ5Pqkt3Go/c2ly77C+v2Lggu5Qek= +google.golang.org/protobuf v1.20.0/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= diff --git a/internal/app/config/config.go b/internal/app/config/config.go index 8dd0feb..5b11479 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -8,7 +8,8 @@ import ( type Config struct{} func LoadConfig() (*Config, error) { - viper.SetDefault("port", "8080") + viper.SetDefault("web-port", "8080") + viper.SetDefault("rpc-port", "8090") viper.SetDefault("debug-mode", false) viper.SetDefault("google-mail.keyfile", "gapps.json") viper.SetDefault("mock-mode", false) @@ -31,16 +32,16 @@ func LoadConfig() (*Config, error) { return &Config{}, nil } -func (*Config) Port() string { - return viper.GetString("port") +func (*Config) WebPort() string { + return viper.GetString("web-port") } func (*Config) Debug() bool { return viper.GetBool("debug-mode") } -func (*Config) PreSharedKey() string { - return viper.GetString("pre-shared-key") +func (*Config) RPCPort() string { + return viper.GetString("rpc-port") } func (*Config) Mock() bool { diff --git a/internal/app/grpc/sever.go b/internal/app/grpc/sever.go new file mode 100644 index 0000000..af714b9 --- /dev/null +++ b/internal/app/grpc/sever.go @@ -0,0 +1,92 @@ +package grpc + +import ( + "context" + "github.com/cthit/gotify/pkg/api/v1" + "github.com/cthit/gotify/pkg/mail" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + "log" + "net" + "net/http" + "sync" +) + +type Server struct { + rpcPort, webPort string + debug bool + mailService mail.MailService + wg sync.WaitGroup +} + +func (s *Server) SendMail(_ context.Context, r *gotify.SendMailRequest) (*gotify.SendMailResponse, error) { + //validate yo + m, err := s.mailService.SendMail(mail.Mail{ + To: r.Mail.To, + From: r.Mail.From, + Subject: r.Mail.Subject, + Body: r.Mail.Body, + }) + if err != nil { + return nil, err + } + //handle them errors + return &gotify.SendMailResponse{ + Mail: &gotify.Mail{ + To: m.To, + From: m.From, + Subject: m.Subject, + Body: m.Body, + }, + Success: true, + Error: "", + }, nil +} + +func NewServer(rpcPort, webPort string, debug bool, mailService mail.MailService) (*Server, error) { + return &Server{ + rpcPort: rpcPort, + webPort: webPort, + debug: debug, + mailService: mailService, + }, nil +} + +func (s *Server) Start() { + s.wg.Add(1) + go func() { + log.Fatal(s.startGRPC()) + s.wg.Done() + }() + s.wg.Add(1) + go func() { + log.Fatal(s.startREST()) + s.wg.Done() + }() + s.wg.Wait() +} + +func (s *Server) startGRPC() error { + lis, err := net.Listen("tcp", ":" + s.rpcPort) + if err != nil { + return err + } + grpcServer := grpc.NewServer() + gotify.RegisterMailerServer(grpcServer, s) + grpcServer.Serve(lis) + return nil +} + +func (s *Server) startREST() error { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + mux := runtime.NewServeMux() + opts := []grpc.DialOption{grpc.WithInsecure()} + err := gotify.RegisterMailerHandlerFromEndpoint(ctx, mux, ":" + s.rpcPort, opts) + if err != nil { + return err + } + return http.ListenAndServe(":" + s.webPort, mux) +} diff --git a/internal/app/main.go b/internal/app/main.go index af6cfe7..d8c8562 100644 --- a/internal/app/main.go +++ b/internal/app/main.go @@ -2,7 +2,7 @@ package app import ( "github.com/cthit/gotify/internal/app/config" - "github.com/cthit/gotify/internal/app/web" + "github.com/cthit/gotify/internal/app/grpc" "github.com/cthit/gotify/pkg/mail" "github.com/cthit/gotify/pkg/mail/gmail" "github.com/cthit/gotify/pkg/mail/mock" @@ -40,10 +40,11 @@ func Start() error { fmt.Printf("Done! \n") - fmt.Printf("Serving application on port %s \n", c.Port()) - server, err := web.NewServer( - c.Port(), - c.PreSharedKey(), + fmt.Printf("Serving application on port %s \n", c.WebPort()) + fmt.Printf("Serving rpc on port %s \n", c.RPCPort()) + server, err := grpc.NewServer( + c.RPCPort(), + c.WebPort(), c.Debug(), mailService, ) @@ -52,6 +53,6 @@ func Start() error { return err } - err = server.Start() - return err + server.Start() + return nil } diff --git a/mail.pb.validate.go b/mail.pb.validate.go new file mode 100644 index 0000000..0664fe1 --- /dev/null +++ b/mail.pb.validate.go @@ -0,0 +1,282 @@ +// Code generated by protoc-gen-validate. DO NOT EDIT. +// source: mail.proto + +package gotify + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "strings" + "time" + "unicode/utf8" + + "github.com/gogo/protobuf/types" +) + +// ensure the imports are used +var ( + _ = bytes.MinRead + _ = errors.New("") + _ = fmt.Print + _ = utf8.UTFMax + _ = (*regexp.Regexp)(nil) + _ = (*strings.Reader)(nil) + _ = net.IPv4len + _ = time.Duration(0) + _ = (*url.URL)(nil) + _ = (*mail.Address)(nil) + _ = types.DynamicAny{} +) + +// define the regex for a UUID once up-front +var _mail_uuidPattern = regexp.MustCompile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") + +// Validate checks the field values on SendMailRequest with the rules defined +// in the proto definition for this message. If any rules are violated, an +// error is returned. +func (m *SendMailRequest) Validate() error { + if m == nil { + return nil + } + + { + tmp := m.GetMail() + + if v, ok := interface{}(tmp).(interface{ Validate() error }); ok { + + if err := v.Validate(); err != nil { + return SendMailRequestValidationError{ + field: "Mail", + reason: "embedded message failed validation", + cause: err, + } + } + } + } + + return nil +} + +// SendMailRequestValidationError is the validation error returned by +// SendMailRequest.Validate if the designated constraints aren't met. +type SendMailRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e SendMailRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e SendMailRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e SendMailRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e SendMailRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e SendMailRequestValidationError) ErrorName() string { return "SendMailRequestValidationError" } + +// Error satisfies the builtin error interface +func (e SendMailRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sSendMailRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = SendMailRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = SendMailRequestValidationError{} + +// Validate checks the field values on SendMailResponse with the rules defined +// in the proto definition for this message. If any rules are violated, an +// error is returned. +func (m *SendMailResponse) Validate() error { + if m == nil { + return nil + } + + if m.GetMail() == nil { + return SendMailResponseValidationError{ + field: "Mail", + reason: "value is required", + } + } + + { + tmp := m.GetMail() + + if v, ok := interface{}(tmp).(interface{ Validate() error }); ok { + + if err := v.Validate(); err != nil { + return SendMailResponseValidationError{ + field: "Mail", + reason: "embedded message failed validation", + cause: err, + } + } + } + } + + // no validation rules for Success + + // no validation rules for Error + + return nil +} + +// SendMailResponseValidationError is the validation error returned by +// SendMailResponse.Validate if the designated constraints aren't met. +type SendMailResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e SendMailResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e SendMailResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e SendMailResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e SendMailResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e SendMailResponseValidationError) ErrorName() string { return "SendMailResponseValidationError" } + +// Error satisfies the builtin error interface +func (e SendMailResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sSendMailResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = SendMailResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = SendMailResponseValidationError{} + +// Validate checks the field values on Mail with the rules defined in the proto +// definition for this message. If any rules are violated, an error is returned. +func (m *Mail) Validate() error { + if m == nil { + return nil + } + + // no validation rules for To + + // no validation rules for From + + // no validation rules for ReplyTo + + // no validation rules for Subject + + // no validation rules for Body + + return nil +} + +// MailValidationError is the validation error returned by Mail.Validate if the +// designated constraints aren't met. +type MailValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e MailValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e MailValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e MailValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e MailValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e MailValidationError) ErrorName() string { return "MailValidationError" } + +// Error satisfies the builtin error interface +func (e MailValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sMail.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = MailValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = MailValidationError{} diff --git a/pkg/api/v1/.keep b/pkg/api/v1/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/protoc-gen.sh b/scripts/protoc-gen.sh index c139f58..e341ef8 100755 --- a/scripts/protoc-gen.sh +++ b/scripts/protoc-gen.sh @@ -1,5 +1,16 @@ #!/usr/bin/bash -protoc --proto_path=api/proto/v1 --go_out=plugins=grpc:pkg/api/v1 mail.proto -protoc --proto_path=api/proto/v1 --grpc-gateway_out=logtostderr=true:pkg/api/v1 mail.proto -protoc --proto_path=api/proto/v1 --swagger_out=logtostderr=true:api/swagger/v1 mail.proto \ No newline at end of file +function generate_for_for_version { + PROTOFILES=api/proto/$1/* + for f in $PROTOFILES; do + mkdir -p api/swagger/$1 + mkdir -p pkg/api/$1 + protoc --proto_path=api/proto/$1 -I/home/nan/go/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --go_out=plugins=grpc:pkg/api/$1 $(basename $f) + protoc --proto_path=api/proto/$1 -I/home/nan/go/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --grpc-gateway_out=logtostderr=true:pkg/api/$1 $(basename $f) + protoc --proto_path=api/proto/$1 -I/home/nan/go/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --swagger_out=logtostderr=true:api/swagger/$1 $(basename $f) + done +} + +for d in $(find api/proto/* -type d); do + generate_for_for_version $(basename $d) +done From 39ddc8c6ba19e35c7234b9b8b72ebeca9f7f13e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Sat, 7 Mar 2020 00:36:47 +0100 Subject: [PATCH 09/23] Make stuff work --- go.mod | 6 +- go.sum | 31 ++-- internal/app/tools/tools.go | 10 ++ mail.pb.validate.go | 282 ------------------------------------ scripts/protoc-gen.sh | 8 +- 5 files changed, 30 insertions(+), 307 deletions(-) create mode 100644 internal/app/tools/tools.go delete mode 100644 mail.pb.validate.go diff --git a/go.mod b/go.mod index 2386712..3a1e235 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,12 @@ go 1.13 require ( github.com/gin-gonic/gin v1.5.0 - github.com/golang/protobuf v1.3.4 + github.com/golang/protobuf v1.3.2 github.com/grpc-ecosystem/grpc-gateway v1.9.0 github.com/spf13/viper v1.5.0 golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 google.golang.org/api v0.14.0 - google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 - google.golang.org/grpc v1.21.0 + google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 + google.golang.org/grpc v1.27.1 ) diff --git a/go.sum b/go.sum index f402f25..56cf3b8 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -22,8 +23,11 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -40,8 +44,6 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -52,20 +54,11 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.3 h1:OzhhLcnXjTNb98owNUFibnt+cUqC2RSAGyFqxU4x3ok= -github.com/golang/protobuf v1.4.0-rc.3/go.mod h1:57iy8tErfL8eYKb9AjvkwLdYn7fzOM4yLPLUGc/0Cew= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -88,7 +81,6 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -121,6 +113,7 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -208,13 +201,12 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= @@ -227,15 +219,15 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.0 h1:SsQNHvKpk2VTiWoQ5Pqkt3Go/c2ly77C+v2Lggu5Qek= -google.golang.org/protobuf v1.20.0/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= @@ -253,3 +245,4 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/app/tools/tools.go b/internal/app/tools/tools.go new file mode 100644 index 0000000..4283b20 --- /dev/null +++ b/internal/app/tools/tools.go @@ -0,0 +1,10 @@ +// +build tools + +package tools + +// Makes sure the right packages are imported +import ( +_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" +_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" +_ "github.com/golang/protobuf/protoc-gen-go" +) \ No newline at end of file diff --git a/mail.pb.validate.go b/mail.pb.validate.go deleted file mode 100644 index 0664fe1..0000000 --- a/mail.pb.validate.go +++ /dev/null @@ -1,282 +0,0 @@ -// Code generated by protoc-gen-validate. DO NOT EDIT. -// source: mail.proto - -package gotify - -import ( - "bytes" - "errors" - "fmt" - "net" - "net/mail" - "net/url" - "regexp" - "strings" - "time" - "unicode/utf8" - - "github.com/gogo/protobuf/types" -) - -// ensure the imports are used -var ( - _ = bytes.MinRead - _ = errors.New("") - _ = fmt.Print - _ = utf8.UTFMax - _ = (*regexp.Regexp)(nil) - _ = (*strings.Reader)(nil) - _ = net.IPv4len - _ = time.Duration(0) - _ = (*url.URL)(nil) - _ = (*mail.Address)(nil) - _ = types.DynamicAny{} -) - -// define the regex for a UUID once up-front -var _mail_uuidPattern = regexp.MustCompile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") - -// Validate checks the field values on SendMailRequest with the rules defined -// in the proto definition for this message. If any rules are violated, an -// error is returned. -func (m *SendMailRequest) Validate() error { - if m == nil { - return nil - } - - { - tmp := m.GetMail() - - if v, ok := interface{}(tmp).(interface{ Validate() error }); ok { - - if err := v.Validate(); err != nil { - return SendMailRequestValidationError{ - field: "Mail", - reason: "embedded message failed validation", - cause: err, - } - } - } - } - - return nil -} - -// SendMailRequestValidationError is the validation error returned by -// SendMailRequest.Validate if the designated constraints aren't met. -type SendMailRequestValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e SendMailRequestValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e SendMailRequestValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e SendMailRequestValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e SendMailRequestValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e SendMailRequestValidationError) ErrorName() string { return "SendMailRequestValidationError" } - -// Error satisfies the builtin error interface -func (e SendMailRequestValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sSendMailRequest.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = SendMailRequestValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = SendMailRequestValidationError{} - -// Validate checks the field values on SendMailResponse with the rules defined -// in the proto definition for this message. If any rules are violated, an -// error is returned. -func (m *SendMailResponse) Validate() error { - if m == nil { - return nil - } - - if m.GetMail() == nil { - return SendMailResponseValidationError{ - field: "Mail", - reason: "value is required", - } - } - - { - tmp := m.GetMail() - - if v, ok := interface{}(tmp).(interface{ Validate() error }); ok { - - if err := v.Validate(); err != nil { - return SendMailResponseValidationError{ - field: "Mail", - reason: "embedded message failed validation", - cause: err, - } - } - } - } - - // no validation rules for Success - - // no validation rules for Error - - return nil -} - -// SendMailResponseValidationError is the validation error returned by -// SendMailResponse.Validate if the designated constraints aren't met. -type SendMailResponseValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e SendMailResponseValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e SendMailResponseValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e SendMailResponseValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e SendMailResponseValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e SendMailResponseValidationError) ErrorName() string { return "SendMailResponseValidationError" } - -// Error satisfies the builtin error interface -func (e SendMailResponseValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sSendMailResponse.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = SendMailResponseValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = SendMailResponseValidationError{} - -// Validate checks the field values on Mail with the rules defined in the proto -// definition for this message. If any rules are violated, an error is returned. -func (m *Mail) Validate() error { - if m == nil { - return nil - } - - // no validation rules for To - - // no validation rules for From - - // no validation rules for ReplyTo - - // no validation rules for Subject - - // no validation rules for Body - - return nil -} - -// MailValidationError is the validation error returned by Mail.Validate if the -// designated constraints aren't met. -type MailValidationError struct { - field string - reason string - cause error - key bool -} - -// Field function returns field value. -func (e MailValidationError) Field() string { return e.field } - -// Reason function returns reason value. -func (e MailValidationError) Reason() string { return e.reason } - -// Cause function returns cause value. -func (e MailValidationError) Cause() error { return e.cause } - -// Key function returns key value. -func (e MailValidationError) Key() bool { return e.key } - -// ErrorName returns error name. -func (e MailValidationError) ErrorName() string { return "MailValidationError" } - -// Error satisfies the builtin error interface -func (e MailValidationError) Error() string { - cause := "" - if e.cause != nil { - cause = fmt.Sprintf(" | caused by: %v", e.cause) - } - - key := "" - if e.key { - key = "key for " - } - - return fmt.Sprintf( - "invalid %sMail.%s: %s%s", - key, - e.field, - e.reason, - cause) -} - -var _ error = MailValidationError{} - -var _ interface { - Field() string - Reason() string - Key() bool - Cause() error - ErrorName() string -} = MailValidationError{} diff --git a/scripts/protoc-gen.sh b/scripts/protoc-gen.sh index e341ef8..c1c66f8 100755 --- a/scripts/protoc-gen.sh +++ b/scripts/protoc-gen.sh @@ -1,13 +1,15 @@ #!/usr/bin/bash +# TODO: make sure gopath is set + function generate_for_for_version { PROTOFILES=api/proto/$1/* for f in $PROTOFILES; do mkdir -p api/swagger/$1 mkdir -p pkg/api/$1 - protoc --proto_path=api/proto/$1 -I/home/nan/go/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --go_out=plugins=grpc:pkg/api/$1 $(basename $f) - protoc --proto_path=api/proto/$1 -I/home/nan/go/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --grpc-gateway_out=logtostderr=true:pkg/api/$1 $(basename $f) - protoc --proto_path=api/proto/$1 -I/home/nan/go/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --swagger_out=logtostderr=true:api/swagger/$1 $(basename $f) + protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --go_out=plugins=grpc:pkg/api/$1 $(basename $f) + protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --grpc-gateway_out=logtostderr=true:pkg/api/$1 $(basename $f) + protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --swagger_out=logtostderr=true:api/swagger/$1 $(basename $f) done } From 30fd7c4e09d3c65f2db1cac7d31d97687a881a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Sat, 7 Mar 2020 23:25:10 +0100 Subject: [PATCH 10/23] add dev and build files for protoc --- .gitignore | 4 ++-- Dockerfile | 30 +++++++++++++++++++++++++++++- Makefile | 26 +++++++++++++++++++++++--- dev.Dockerfile | 34 +++++++++++++++++++++++++++++++--- docker-compose.yml | 27 +++++++++++++++++++++++---- scripts/proto-watcher.sh | 3 +++ scripts/protoc-gen.sh | 6 ++---- 7 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 scripts/proto-watcher.sh diff --git a/.gitignore b/.gitignore index 22ad477..906386c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ config.toml .idea/ -*.json -gin-bin +gapps.json +*-bin api/swagger pkg/api \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 038f52a..e836ac3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,31 @@ +# Dockerfile for protobuf generation +FROM znly/protoc:0.4.0 AS protocGenerator +MAINTAINER digIT + +RUN apk update +RUN apk upgrade +RUN apk add --update git + +# Add standard certificates +RUN apk add ca-certificates && rm -rf /var/cache/apk/* + +# Add proto imports +RUN mkdir -p /src/github.com/grpc-ecosystem +WORKDIR /src/github.com/grpc-ecosystem +RUN git clone https://github.com/grpc-ecosystem/grpc-gateway.git + +# create dir +RUN mkdir /app +WORKDIR /app + +ENTRYPOINT ["/bin/sh"] + +FROM protocGenerator AS protocGen + +COPY . /app + +RUN ./scripts/protoc-gen.sh + # Dockerfile for gotify production FROM golang:alpine AS buildStage MAINTAINER digIT @@ -9,7 +37,7 @@ RUN apk add --update git # Copy sources RUN mkdir /app -COPY . /app +COPY --from=protocGen /app /app WORKDIR /app/cmd/gotify # Grab dependencies diff --git a/Makefile b/Makefile index 00d56b9..fd72804 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,28 @@ +.PHONY: setup +setup: gen-setup gen + go mod download + +.PHONY: gen-setup +gen-setup: + docker build --target protocGenerator -t gotify-protoc-generator . + .PHONY: gen gen: - ./scripts/protoc-gen.sh + docker run -v `pwd`:/app gotify-protoc-generator ./scripts/protoc-gen.sh + +.PHONY: build +build: gen + go build -o gotify-bin ./cmd/gotify + +.PHONY: run +run: gen + go run ./cmd/gotify + +.PHONY: dev +dev: + docker-compose up .PHONY: clean clean: - rm -r pkg/api - rm -r api/swagger \ No newline at end of file + rm -rf pkg/api + rm -rf api/swagger diff --git a/dev.Dockerfile b/dev.Dockerfile index 5320492..0efeea3 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -1,5 +1,5 @@ # Dockerfile for gotify development -FROM golang:alpine +FROM golang:alpine as dev MAINTAINER digIT # Install git @@ -7,7 +7,7 @@ RUN apk update RUN apk upgrade RUN apk add --update git -RUN go get github.com/codegangsta/gin +RUN go get -u github.com/cespare/reflex # Add standard certificates RUN apk add ca-certificates && rm -rf /var/cache/apk/* @@ -16,4 +16,32 @@ RUN apk add ca-certificates && rm -rf /var/cache/apk/* RUN mkdir /app WORKDIR /app -CMD go mod download && gin -d cmd/gotify -a 8080 run main.go +RUN which reflex + +CMD reflex -r '\.go$' -s -- go run ./cmd/gotify + +# Dockerfile for protobuf generation +FROM znly/protoc:0.4.0 as dev_gen +MAINTAINER digIT + +RUN apk update +RUN apk upgrade +RUN apk add --update git + +# Add standard certificates +RUN apk add ca-certificates && rm -rf /var/cache/apk/* + +# Add proto imports +RUN mkdir -p /src/github.com/grpc-ecosystem +WORKDIR /src/github.com/grpc-ecosystem +RUN git clone https://github.com/grpc-ecosystem/grpc-gateway.git + +COPY --from=dev /go/bin/reflex /bin/reflex + +# create dir +RUN mkdir /app +WORKDIR /app + + +ENTRYPOINT ["/bin/sh"] + diff --git a/docker-compose.yml b/docker-compose.yml index 2b2ffed..a7f00a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,34 @@ -version: '3' +version: '3.4' services: - web: + dev: build: dockerfile: dev.Dockerfile context: ./ + target: dev ports: - - "8080:3000" + - "8080:8080" + - "8090:8090" volumes: - .:/app environment: GOTIFY_PRE-SHARED-KEY: "123abc" GOTIFY_DEBUG-MODE: "true" GOTIFY_MOCK-MODE: "true" - GOTIFY_GOOGLE-MAIL.KEYFILE: "gapps.json" \ No newline at end of file + GOTIFY_GOOGLE-MAIL.KEYFILE: "gapps.json" + dev-gen: + build: + dockerfile: dev.Dockerfile + context: ./ + target: dev_gen + volumes: + - .:/app + command: ./scripts/proto-watcher.sh + swagger: # Generated swagger file i quite bad at the time of writing + image: swaggerapi/swagger-ui + ports: + - "8000:8000" + environment: + PORT: "8000" + SWAGGER_JSON: "/app/v1/apidocs.swagger.json" + volumes: + - ./api/swagger:/app \ No newline at end of file diff --git a/scripts/proto-watcher.sh b/scripts/proto-watcher.sh new file mode 100644 index 0000000..f52eb08 --- /dev/null +++ b/scripts/proto-watcher.sh @@ -0,0 +1,3 @@ +#!/bin/sh +./scripts/protoc-gen.sh || true +reflex -r '\.proto$' -- ./scripts/protoc-gen.sh \ No newline at end of file diff --git a/scripts/protoc-gen.sh b/scripts/protoc-gen.sh index c1c66f8..8c37efb 100755 --- a/scripts/protoc-gen.sh +++ b/scripts/protoc-gen.sh @@ -1,6 +1,4 @@ -#!/usr/bin/bash - -# TODO: make sure gopath is set +#!/bin/sh function generate_for_for_version { PROTOFILES=api/proto/$1/* @@ -9,7 +7,7 @@ function generate_for_for_version { mkdir -p pkg/api/$1 protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --go_out=plugins=grpc:pkg/api/$1 $(basename $f) protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --grpc-gateway_out=logtostderr=true:pkg/api/$1 $(basename $f) - protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --swagger_out=logtostderr=true:api/swagger/$1 $(basename $f) + protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --swagger_out=allow_merge=true,logtostderr=true:api/swagger/$1 $(basename $f) done } From cf7bb4db257cb78a06af741b3d2cb00dce2c11ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Sat, 7 Mar 2020 23:34:15 +0100 Subject: [PATCH 11/23] remove web package --- go.mod | 3 ++- go.sum | 28 --------------------------- internal/app/web/auth.go | 13 ------------- internal/app/web/mail.go | 28 --------------------------- internal/app/web/server.go | 39 -------------------------------------- 5 files changed, 2 insertions(+), 109 deletions(-) delete mode 100644 internal/app/web/auth.go delete mode 100644 internal/app/web/mail.go delete mode 100644 internal/app/web/server.go diff --git a/go.mod b/go.mod index 3a1e235..ff4f94b 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,13 @@ module github.com/cthit/gotify go 1.13 require ( - github.com/gin-gonic/gin v1.5.0 github.com/golang/protobuf v1.3.2 github.com/grpc-ecosystem/grpc-gateway v1.9.0 github.com/spf13/viper v1.5.0 + github.com/stretchr/testify v1.4.0 // indirect golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 + golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect google.golang.org/api v0.14.0 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 google.golang.org/grpc v1.27.1 diff --git a/go.sum b/go.sum index 56cf3b8..faf19de 100644 --- a/go.sum +++ b/go.sum @@ -29,17 +29,9 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc= -github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= -github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= -github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= -github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= @@ -59,7 +51,6 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -76,8 +67,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -89,19 +78,11 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= -github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= @@ -137,17 +118,12 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -232,10 +208,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks 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/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= -gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/app/web/auth.go b/internal/app/web/auth.go deleted file mode 100644 index af8a71e..0000000 --- a/internal/app/web/auth.go +++ /dev/null @@ -1,13 +0,0 @@ -package web - -import ( - "fmt" - "github.com/gin-gonic/gin" - "net/http" -) - -func (s *Server) MustAuthorize(c *gin.Context) { - if c.GetHeader("Authorization") != fmt.Sprintf("pre-shared: %s", s.preSharedKey) { - c.AbortWithStatus(http.StatusUnauthorized) - } -} diff --git a/internal/app/web/mail.go b/internal/app/web/mail.go deleted file mode 100644 index 9530d88..0000000 --- a/internal/app/web/mail.go +++ /dev/null @@ -1,28 +0,0 @@ -package web - -import ( - "fmt" - "github.com/cthit/gotify/pkg/mail" - "github.com/gin-gonic/gin" - "net/http" -) - -func (s *Server) MailHandler(c *gin.Context) { - var m mail.Mail - - err := c.Bind(&m) - if err != nil { - fmt.Println(err) - return - } - - // Send email - m, err = s.mailService.SendMail(m) - if err != nil { - c.String(http.StatusInternalServerError, err.Error()) - return - } - - // Return the sent email - c.JSON(http.StatusAccepted, m) -} diff --git a/internal/app/web/server.go b/internal/app/web/server.go deleted file mode 100644 index 6c4b49c..0000000 --- a/internal/app/web/server.go +++ /dev/null @@ -1,39 +0,0 @@ -package web - -import ( - "github.com/cthit/gotify/pkg/mail" - "github.com/gin-gonic/gin" -) - -type Server struct { - port string - preSharedKey string - debug bool - mailService mail.MailService -} - -func NewServer(port, preSharedKey string, debug bool, mailService mail.MailService) (*Server, error) { - return &Server{ - port: port, - preSharedKey: preSharedKey, - debug: debug, - mailService: mailService, - }, nil -} - -func (s *Server) Start() error { - if s.debug { - gin.SetMode(gin.DebugMode) - } else { - gin.SetMode(gin.ReleaseMode) - } - router := gin.New() - router.Use(gin.Logger()) - router.Use(gin.Recovery()) - authorized := router.Group("/") - authorized.Use(s.MustAuthorize) - { - authorized.POST("/mail", s.MailHandler) - } - return router.Run(":" + s.port) -} From 2ab69ada24222ced47dec37dc09aafa62edb2ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Sat, 7 Mar 2020 23:34:48 +0100 Subject: [PATCH 12/23] rename main to app --- internal/app/{main.go => app.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/app/{main.go => app.go} (100%) diff --git a/internal/app/main.go b/internal/app/app.go similarity index 100% rename from internal/app/main.go rename to internal/app/app.go From cc91970f9b4dcb707ca7066b9cab342757388391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Lev=C3=A9n?= Date: Mon, 9 Mar 2020 09:29:33 +0100 Subject: [PATCH 13/23] grps stuff and api changes --- README.md | 43 +++---------------------- api/proto/v1/mail.proto | 13 ++------ docker-compose.yml | 1 - go.mod | 1 + go.sum | 1 + internal/app/app.go | 2 ++ internal/app/config/config.go | 10 ++++++ internal/app/grpc/mail.go | 43 +++++++++++++++++++++++++ internal/app/grpc/sever.go | 60 ++++++++++++----------------------- pkg/mail/gmail/service.go | 10 +++--- pkg/mail/mail.go | 1 + scripts/proto-watcher.sh | 4 +-- 12 files changed, 93 insertions(+), 96 deletions(-) create mode 100644 internal/app/grpc/mail.go diff --git a/README.md b/README.md index b8be515..5d56f24 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,6 @@ Currently supports the following notification types: ## Usage How to use the running application -All request must inclue a header with the preshared key. - -`Authorization`: `pre-shared: your...key` - ### Mail POST `/mail` @@ -19,7 +15,8 @@ Json Request: ``` { "to": "....", - "from": "....", + "from": "....", (optional) + "reply_to": "....", (optional) "subject": "....", "body": "...." } @@ -30,25 +27,10 @@ Steps to run the application. this include configuration and key files at the moment ### Config -The application can be configured through a config file or environment variables. Environment variables take precedence. - -#### config.toml -config.toml can reside in your working directory, `/etc/gotify/` or `$HOME/.gotify/` - -``` -port = "8080" -pre-shared-key = "......" -debug-mode = false -mock-mode = false - -[google-mail] - keyfile = "gapps.json" -``` -See [Environment Variables](#environment-variables) for config explanation +The application is configured through environment variables. #### Environment Variables * `GOTIFY_PORT`: Port for the web service, defaults to `8080` (string) -* `GOTIFY_PRE-SHARED-KEY`*: Random string used by other apps to authenticate * `GOTIFY_DEBUG-MODE`: Bool indicating debug mode defaults to `false` * `GOTIFY_GOOGLE-MAIL.KEYFILE`: the file described in [Google config file](#google-config-file) defaults to `gapps.json` @@ -98,20 +80,5 @@ Use gin for hot reloading. You can install additional dependencies without restarting the container by running `docker exec gotify_web_1 go get ...`, gotify_web_1 is the name of the container and ... is the dependency. ### As mock -1. Set the `pre-shared-key` config/environment variable. -2. Set the `mock-mode` config/environment variable to true -3. Enjoy - -Example docker-compose entry for mock service: -``` -services: - ... - gotify: - image: cthit/gotify:latest - environment: - GOTIFY_PRE-SHARED-KEY: "123abc" - GOTIFY_MOCK-MODE: "true" - -``` - -Other services would then be able to reach this service on `http://gotify:8080/...` with `123abc` as the preshared key \ No newline at end of file +1. Set the `mock-mode` config/environment variable to true +2. Enjoy diff --git a/api/proto/v1/mail.proto b/api/proto/v1/mail.proto index 7cbf570..140639e 100644 --- a/api/proto/v1/mail.proto +++ b/api/proto/v1/mail.proto @@ -5,23 +5,14 @@ package gotify; import "google/api/annotations.proto"; service Mailer { - rpc SendMail(SendMailRequest) returns (SendMailResponse) { + rpc SendMail(Mail) returns (Mail) { option(google.api.http) = { post: "/mail" + body: "*" }; } } -message SendMailRequest { - Mail mail = 1; -} - -message SendMailResponse { - Mail mail = 1; - bool success = 2; - string error = 3; -} - message Mail { string to = 1; string from = 2; diff --git a/docker-compose.yml b/docker-compose.yml index a7f00a0..2b51e61 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,6 @@ services: volumes: - .:/app environment: - GOTIFY_PRE-SHARED-KEY: "123abc" GOTIFY_DEBUG-MODE: "true" GOTIFY_MOCK-MODE: "true" GOTIFY_GOOGLE-MAIL.KEYFILE: "gapps.json" diff --git a/go.mod b/go.mod index ff4f94b..68740b4 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/cthit/gotify go 1.13 require ( + github.com/coreos/bbolt v1.3.2 github.com/golang/protobuf v1.3.2 github.com/grpc-ecosystem/grpc-gateway v1.9.0 github.com/spf13/viper v1.5.0 diff --git a/go.sum b/go.sum index faf19de..a6ee835 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= diff --git a/internal/app/app.go b/internal/app/app.go index d8c8562..967184d 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -47,6 +47,8 @@ func Start() error { c.WebPort(), c.Debug(), mailService, + c.MailDefaultFrom(), + c.MailDefaultReplyTo(), ) if err != nil { fmt.Println("Failed to create webserver.") diff --git a/internal/app/config/config.go b/internal/app/config/config.go index 5b11479..c278f1f 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -12,6 +12,8 @@ func LoadConfig() (*Config, error) { viper.SetDefault("rpc-port", "8090") viper.SetDefault("debug-mode", false) viper.SetDefault("google-mail.keyfile", "gapps.json") + viper.SetDefault("mail.default-from", "admin@chalmers.it") + viper.SetDefault("mail.default-reply-to", "no-reply@chalmers.it") viper.SetDefault("mock-mode", false) viper.SetEnvPrefix("gotify") @@ -51,3 +53,11 @@ func (*Config) Mock() bool { func (*Config) GmailKeyfile() string { return viper.GetString("google-mail.keyfile") } + +func (*Config) MailDefaultFrom() string { + return viper.GetString("mail.default-from") +} + +func (*Config) MailDefaultReplyTo() string { + return viper.GetString("mail.default-reply-to") +} diff --git a/internal/app/grpc/mail.go b/internal/app/grpc/mail.go new file mode 100644 index 0000000..ce3efff --- /dev/null +++ b/internal/app/grpc/mail.go @@ -0,0 +1,43 @@ +package grpc + +import ( + "context" + "strings" + + gotify "github.com/cthit/gotify/pkg/api/v1" + "github.com/cthit/gotify/pkg/mail" +) + +func (s *Server) SendMail(_ context.Context, in *gotify.Mail) (*gotify.Mail, error) { + //validate yo + m := mail.Mail{ + To: in.To, + Subject: in.Subject, + Body: in.Body, + } + + if strings.TrimSpace(in.From) == "" { + m.From = s.mailDefaultFromAddress + } else { + m.From = in.From + } + + if strings.TrimSpace(in.ReplyTo) == "" { + m.ReplyTo = s.mailDefaultReplyToAddress + } else { + m.ReplyTo = in.ReplyTo + } + + m, err := s.mailService.SendMail(m) + if err != nil { + return nil, err + } + //handle them errors + return &gotify.Mail{ + To: m.To, + From: m.From, + ReplyTo: m.ReplyTo, + Subject: m.Subject, + Body: m.Body, + }, nil +} diff --git a/internal/app/grpc/sever.go b/internal/app/grpc/sever.go index af714b9..392f01d 100644 --- a/internal/app/grpc/sever.go +++ b/internal/app/grpc/sever.go @@ -2,72 +2,54 @@ package grpc import ( "context" + "fmt" "github.com/cthit/gotify/pkg/api/v1" "github.com/cthit/gotify/pkg/mail" "github.com/grpc-ecosystem/grpc-gateway/runtime" "google.golang.org/grpc" - "log" "net" "net/http" "sync" ) type Server struct { - rpcPort, webPort string - debug bool - mailService mail.MailService - wg sync.WaitGroup + rpcPort, webPort string + debug bool + mailService mail.MailService + mailDefaultFromAddress string + mailDefaultReplyToAddress string + wg sync.WaitGroup } -func (s *Server) SendMail(_ context.Context, r *gotify.SendMailRequest) (*gotify.SendMailResponse, error) { - //validate yo - m, err := s.mailService.SendMail(mail.Mail{ - To: r.Mail.To, - From: r.Mail.From, - Subject: r.Mail.Subject, - Body: r.Mail.Body, - }) - if err != nil { - return nil, err - } - //handle them errors - return &gotify.SendMailResponse{ - Mail: &gotify.Mail{ - To: m.To, - From: m.From, - Subject: m.Subject, - Body: m.Body, - }, - Success: true, - Error: "", - }, nil -} - -func NewServer(rpcPort, webPort string, debug bool, mailService mail.MailService) (*Server, error) { +func NewServer(rpcPort, webPort string, debug bool, mailService mail.MailService, mailDefaultFromAddress, mailDefaultReplyToAddress string) (*Server, error) { return &Server{ - rpcPort: rpcPort, - webPort: webPort, - debug: debug, - mailService: mailService, + rpcPort: rpcPort, + webPort: webPort, + debug: debug, + mailService: mailService, + mailDefaultFromAddress: mailDefaultFromAddress, + mailDefaultReplyToAddress: mailDefaultReplyToAddress, }, nil } func (s *Server) Start() { s.wg.Add(1) go func() { - log.Fatal(s.startGRPC()) + err := s.startGRPC() + fmt.Println(err) s.wg.Done() }() s.wg.Add(1) go func() { - log.Fatal(s.startREST()) + err := s.startREST() + fmt.Println(err) s.wg.Done() }() s.wg.Wait() } func (s *Server) startGRPC() error { - lis, err := net.Listen("tcp", ":" + s.rpcPort) + lis, err := net.Listen("tcp", ":"+s.rpcPort) if err != nil { return err } @@ -84,9 +66,9 @@ func (s *Server) startREST() error { mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithInsecure()} - err := gotify.RegisterMailerHandlerFromEndpoint(ctx, mux, ":" + s.rpcPort, opts) + err := gotify.RegisterMailerHandlerFromEndpoint(ctx, mux, "localhost:" + s.rpcPort, opts) if err != nil { return err } - return http.ListenAndServe(":" + s.webPort, mux) + return http.ListenAndServe(":"+s.webPort, mux) } diff --git a/pkg/mail/gmail/service.go b/pkg/mail/gmail/service.go index 7892f55..e63e541 100644 --- a/pkg/mail/gmail/service.go +++ b/pkg/mail/gmail/service.go @@ -22,9 +22,8 @@ const googleInvalidEmailErrorMessage = `Response: { }` type googleService struct { - config jwt.Config - adminMail string - debug bool + config jwt.Config + debug bool } func (g *googleService) mailService(from string) (*gmail.Service, error) { @@ -47,8 +46,8 @@ func NewService(keyPath string, debug bool) (mail.MailService, error) { } gs := &googleService{ - config: *config, - debug: debug, + config: *config, + debug: debug, } return gs, err @@ -63,6 +62,7 @@ func (g *googleService) SendMail(m mail.Mail) (mail.Mail, error) { msgRaw := "From: " + m.From + "\r\n" + "To: " + m.To + "\r\n" + + "Reply-To: " + m.ReplyTo + "\r\n" + "Subject: " + mail.EncodeHeader(m.Subject) + "\r\n\r\n" + m.Body + "\r\n" diff --git a/pkg/mail/mail.go b/pkg/mail/mail.go index b393a60..e7f8c08 100644 --- a/pkg/mail/mail.go +++ b/pkg/mail/mail.go @@ -3,6 +3,7 @@ package mail type Mail struct { To string `json:"to"` From string `json:"from"` + ReplyTo string `json:"reply:to"` Subject string `json:"subject"` Body string `json:"body"` } diff --git a/scripts/proto-watcher.sh b/scripts/proto-watcher.sh index f52eb08..612652d 100644 --- a/scripts/proto-watcher.sh +++ b/scripts/proto-watcher.sh @@ -1,3 +1,3 @@ #!/bin/sh -./scripts/protoc-gen.sh || true -reflex -r '\.proto$' -- ./scripts/protoc-gen.sh \ No newline at end of file +(./scripts/protoc-gen.sh && echo "Generated!" ) || true +reflex -r '\.proto$' -- sh -c './scripts/protoc-gen.sh && echo "Generated!"' \ No newline at end of file From 3f6abe11095febef1a34ad12a8b8c4b3abad3629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Mon, 9 Mar 2020 10:41:06 +0100 Subject: [PATCH 14/23] Create DefaultsWrapper for mail service --- README.md | 6 +++--- go.mod | 2 +- go.sum | 2 ++ internal/app/app.go | 6 +++--- internal/app/grpc/mail.go | 16 ++-------------- internal/app/grpc/sever.go | 8 ++------ pkg/mail/defaults_wrapper.go | 32 ++++++++++++++++++++++++++++++++ pkg/mail/gmail/service.go | 2 +- pkg/mail/mock/service.go | 2 +- pkg/mail/service.go | 2 +- 10 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 pkg/mail/defaults_wrapper.go diff --git a/README.md b/README.md index 5d56f24..ed82183 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ How to use the running application POST `/mail` Json Request: -``` +```json5 { "to": "....", - "from": "....", (optional) - "reply_to": "....", (optional) + "from": "....", // (optional) + "reply_to": "....", // (optional) "subject": "....", "body": "...." } diff --git a/go.mod b/go.mod index 68740b4..39deb07 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( github.com/coreos/bbolt v1.3.2 - github.com/golang/protobuf v1.3.2 + github.com/golang/protobuf v1.3.4 github.com/grpc-ecosystem/grpc-gateway v1.9.0 github.com/spf13/viper v1.5.0 github.com/stretchr/testify v1.4.0 // indirect diff --git a/go.sum b/go.sum index a6ee835..2cc1460 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,8 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= diff --git a/internal/app/app.go b/internal/app/app.go index 967184d..abeb9f1 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -24,7 +24,7 @@ func Start() error { fmt.Printf("Setting up services...") - var mailService mail.MailService + var mailService mail.Service if !c.Mock() { mailService, err = gmail.NewService( @@ -38,6 +38,8 @@ func Start() error { mailService, _ = mock.NewService() } + mailService = mail.NewService(mailService, c.MailDefaultFrom(), c.MailDefaultReplyTo()) + fmt.Printf("Done! \n") fmt.Printf("Serving application on port %s \n", c.WebPort()) @@ -47,8 +49,6 @@ func Start() error { c.WebPort(), c.Debug(), mailService, - c.MailDefaultFrom(), - c.MailDefaultReplyTo(), ) if err != nil { fmt.Println("Failed to create webserver.") diff --git a/internal/app/grpc/mail.go b/internal/app/grpc/mail.go index ce3efff..02da7be 100644 --- a/internal/app/grpc/mail.go +++ b/internal/app/grpc/mail.go @@ -2,8 +2,6 @@ package grpc import ( "context" - "strings" - gotify "github.com/cthit/gotify/pkg/api/v1" "github.com/cthit/gotify/pkg/mail" ) @@ -12,22 +10,12 @@ func (s *Server) SendMail(_ context.Context, in *gotify.Mail) (*gotify.Mail, err //validate yo m := mail.Mail{ To: in.To, + From: in.From, + ReplyTo: in.ReplyTo, Subject: in.Subject, Body: in.Body, } - if strings.TrimSpace(in.From) == "" { - m.From = s.mailDefaultFromAddress - } else { - m.From = in.From - } - - if strings.TrimSpace(in.ReplyTo) == "" { - m.ReplyTo = s.mailDefaultReplyToAddress - } else { - m.ReplyTo = in.ReplyTo - } - m, err := s.mailService.SendMail(m) if err != nil { return nil, err diff --git a/internal/app/grpc/sever.go b/internal/app/grpc/sever.go index 392f01d..578bc1c 100644 --- a/internal/app/grpc/sever.go +++ b/internal/app/grpc/sever.go @@ -15,20 +15,16 @@ import ( type Server struct { rpcPort, webPort string debug bool - mailService mail.MailService - mailDefaultFromAddress string - mailDefaultReplyToAddress string + mailService mail.Service wg sync.WaitGroup } -func NewServer(rpcPort, webPort string, debug bool, mailService mail.MailService, mailDefaultFromAddress, mailDefaultReplyToAddress string) (*Server, error) { +func NewServer(rpcPort, webPort string, debug bool, mailService mail.Service) (*Server, error) { return &Server{ rpcPort: rpcPort, webPort: webPort, debug: debug, mailService: mailService, - mailDefaultFromAddress: mailDefaultFromAddress, - mailDefaultReplyToAddress: mailDefaultReplyToAddress, }, nil } diff --git a/pkg/mail/defaults_wrapper.go b/pkg/mail/defaults_wrapper.go new file mode 100644 index 0000000..aa4fbfe --- /dev/null +++ b/pkg/mail/defaults_wrapper.go @@ -0,0 +1,32 @@ +package mail + +import "strings" + +type DefaultsWrapper struct { + ms Service + mailDefaultFromAddress string + mailDefaultReplyToAddress string +} + +func NewService(ms Service, mailDefaultFromAddress string, mailDefaultReplyToAddress string) *DefaultsWrapper { + return &DefaultsWrapper{ + ms: ms, + mailDefaultFromAddress: mailDefaultFromAddress, + mailDefaultReplyToAddress: mailDefaultReplyToAddress, + } +} + +func (w DefaultsWrapper) SendMail(mail Mail) (Mail, error) { + if strings.TrimSpace(mail.From) == "" { + mail.From = w.mailDefaultFromAddress + } + + if strings.TrimSpace(mail.ReplyTo) == "" { + mail.ReplyTo = w.mailDefaultReplyToAddress + } + return w.ms.SendMail(mail) +} + +func (w DefaultsWrapper) Destroy() error { + return w.ms.Destroy() +} diff --git a/pkg/mail/gmail/service.go b/pkg/mail/gmail/service.go index e63e541..48dbe11 100644 --- a/pkg/mail/gmail/service.go +++ b/pkg/mail/gmail/service.go @@ -33,7 +33,7 @@ func (g *googleService) mailService(from string) (*gmail.Service, error) { return gmail.NewService(context.TODO(), option.WithScopes(gmail.GmailSendScope), option.WithTokenSource(c.TokenSource(context.TODO()))) } -func NewService(keyPath string, debug bool) (mail.MailService, error) { +func NewService(keyPath string, debug bool) (mail.Service, error) { jsonKey, err := ioutil.ReadFile(keyPath) if err != nil { return nil, err diff --git a/pkg/mail/mock/service.go b/pkg/mail/mock/service.go index bf710d8..75dbda5 100644 --- a/pkg/mail/mock/service.go +++ b/pkg/mail/mock/service.go @@ -8,7 +8,7 @@ import ( type mockService struct { } -func NewService() (mail.MailService, error) { +func NewService() (mail.Service, error) { return &mockService{}, nil } diff --git a/pkg/mail/service.go b/pkg/mail/service.go index 57e278e..d4c0b48 100644 --- a/pkg/mail/service.go +++ b/pkg/mail/service.go @@ -1,6 +1,6 @@ package mail -type MailService interface { +type Service interface { SendMail(mail Mail) (Mail, error) // Returns the actually sent email Destroy() error } From 775b1c118067c43e6330586e1ead32c272d0f3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Tue, 10 Mar 2020 15:01:44 +0100 Subject: [PATCH 15/23] Update design and readme --- DESIGN.md | 17 +++-------------- README.md | 39 ++++++++++++++++----------------------- 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 4f4e692..4c7bcac 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -1,22 +1,11 @@ # Design rationale for gotify ## Project structure -The root package/directory only contains the domain types and logic. This will probably be limited to notification service interfaces and notification types. This package may not depend on any other package in this repo. - -cmd contains the main package and takes care of meta sutch as configuration and binding all other packages together, This package may depend on any other package - -All other packages represent a functionality or dependency and may only depend on the root package as well as external packages. - -See [Project structure in go](https://medium.com/@benbjohnson/structuring-applications-in-go-3b04be4ff091) for further explanation. +See [Project structure in go](https://github.com/golang-standards/project-layout) for further explanation. ## API Structure One api endpoint for every notification type. See readme for existing api endpoints -A post request to an endpoint with the matching jason notification type should send a notification and on success return the sent notification in json. - -## Dependency injection - -The web package has some weird dependency injection. - -For now, take a look at it until you understand it. It looks like it does for a good reason. \ No newline at end of file +A post request to an endpoint with the matching json notification type should send a notification and on +success return the sent notification in json. \ No newline at end of file diff --git a/README.md b/README.md index ed82183..c48d0af 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,14 @@ this include configuration and key files at the moment The application is configured through environment variables. #### Environment Variables -* `GOTIFY_PORT`: Port for the web service, defaults to `8080` (string) +* `GOTIFY_WEB-PORT`: Port for the web service, defaults to `8080` (string) +* `GOTIFY_RPC-PORT`: Port for the rpc service, defaults to `8090` (string) * `GOTIFY_DEBUG-MODE`: Bool indicating debug mode defaults to `false` -* `GOTIFY_GOOGLE-MAIL.KEYFILE`: the file described in [Google config file](#google-config-file) defaults to `gapps.json` +* `GOTIFY_GOOGLE-MAIL.KEYFILE`: the file described in [Google config file](#google-config-file) defaults +to `gapps.json` +* `GOTIFY_MAIL.DEFAULT-FROM`: Default `from` address in the mail, defaults to `admin@chalmers.it` +* `GOTIFY_MAIL.DEFAULT-REPLY-TO`: Default `reply-to` address in the mail, defaults to `no-reply@chalmers.it` +* `GOTIFY_MOCK-MODE`: Enable mock mode, defaults to `false` ### Google config file This file (gapps.json by default config) should be placed in the working directory @@ -55,29 +60,17 @@ You must also allow mail api calls: * use api scope `https://www.googleapis.com/auth/gmail.send` ## Development -You can either set this project up manually or with a simple docker compose setup. The manual setup is recommended if you'll be doing extensive development. +To start a dockerized development environment with hot-reloading: +```bash +$ make dev +``` -Please referer to the software design document before starting development: `DESIGN.md` +To start a non-dockerized development environment: +```bash +$ make run +``` -See issues for suggested features. -### Manual -Make sure you have golang installed and you `$GOPATH` setup. -1. Follow the steps in [Setup](#setup) and enable debug mode. -2. Grab all dependencies by standing in the project root and run `go get -d ./...` -3. You find the main file in `cmd/main.go` -4. Go to http://localhost:8080 - -Use gin for hot reloading. -1. Grab it with `go get github.com/codegangsta/gin` -2. Run gotify with `gin -d cmd -a 8080 run main.go` -3. Go to http://localhost:3000 - -### Docker Compose -1. Get a [Google key file](#google-config-file). -2. Run `docker-compose up --build` -3. Go to http://localhost:8080 - -You can install additional dependencies without restarting the container by running `docker exec gotify_web_1 go get ...`, gotify_web_1 is the name of the container and ... is the dependency. +Please referer to the software design document before starting development: `DESIGN.md` ### As mock 1. Set the `mock-mode` config/environment variable to true From 1c904f1aa9197087ae18c172f7bcd65dad78897a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20=27NaN=27=20Wikstr=C3=B6m?= Date: Tue, 10 Mar 2020 15:12:15 +0100 Subject: [PATCH 16/23] Replace - and . characters in environment variables --- README.md | 14 +++++++------- internal/app/config/config.go | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c48d0af..4438b7b 100644 --- a/README.md +++ b/README.md @@ -30,14 +30,14 @@ this include configuration and key files at the moment The application is configured through environment variables. #### Environment Variables -* `GOTIFY_WEB-PORT`: Port for the web service, defaults to `8080` (string) -* `GOTIFY_RPC-PORT`: Port for the rpc service, defaults to `8090` (string) -* `GOTIFY_DEBUG-MODE`: Bool indicating debug mode defaults to `false` -* `GOTIFY_GOOGLE-MAIL.KEYFILE`: the file described in [Google config file](#google-config-file) defaults +* `GOTIFY_WEB_PORT`: Port for the web service, defaults to `8080` (string) +* `GOTIFY_RPC_PORT`: Port for the rpc service, defaults to `8090` (string) +* `GOTIFY_DEBUG_MODE`: Bool indicating debug mode defaults to `false` +* `GOTIFY_GOOGLE_MAIL_KEYFILE`: the file described in [Google config file](#google-config-file) defaults to `gapps.json` -* `GOTIFY_MAIL.DEFAULT-FROM`: Default `from` address in the mail, defaults to `admin@chalmers.it` -* `GOTIFY_MAIL.DEFAULT-REPLY-TO`: Default `reply-to` address in the mail, defaults to `no-reply@chalmers.it` -* `GOTIFY_MOCK-MODE`: Enable mock mode, defaults to `false` +* `GOTIFY_MAIL_DEFAULT_FROM`: Default `from` address in the mail, defaults to `admin@chalmers.it` +* `GOTIFY_MAIL_DEFAULT_REPLY_TO`: Default `reply-to` address in the mail, defaults to `no-reply@chalmers.it` +* `GOTIFY_MOCK_MODE`: Enable mock mode, defaults to `false` ### Google config file This file (gapps.json by default config) should be placed in the working directory diff --git a/internal/app/config/config.go b/internal/app/config/config.go index c278f1f..d7ee603 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -3,6 +3,7 @@ package config import ( "fmt" "github.com/spf13/viper" + "strings" ) type Config struct{} @@ -18,6 +19,7 @@ func LoadConfig() (*Config, error) { viper.SetEnvPrefix("gotify") viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_")) viper.SetConfigName("config") viper.AddConfigPath("/etc/gotify/") From 6f4800af6284480c4245fd81027057781c68f8a0 Mon Sep 17 00:00:00 2001 From: williamleven Date: Wed, 16 Sep 2020 19:42:41 +0200 Subject: [PATCH 17/23] add content type to mail --- README.md | 14 ++++++++------ api/proto/v1/mail.proto | 3 ++- docker-compose.yml | 8 ++++---- go.mod | 3 +-- internal/app/app.go | 2 +- internal/app/config/config.go | 5 +++++ internal/app/grpc/mail.go | 6 ++++-- internal/app/grpc/sever.go | 18 +++++++++--------- pkg/mail/defaults_wrapper.go | 9 ++++++++- pkg/mail/gmail/service.go | 9 +++++---- pkg/mail/mail.go | 11 ++++++----- 11 files changed, 53 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 4438b7b..577929a 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,12 @@ POST `/mail` Json Request: ```json5 { - "to": "....", - "from": "....", // (optional) - "reply_to": "....", // (optional) - "subject": "....", - "body": "...." + "to": "...", + "from": "...", // (optional) + "reply_to": "...", // (optional) + "content_type": "...", // (optional) + "subject": "...", + "body": "..." } ``` @@ -36,7 +37,8 @@ The application is configured through environment variables. * `GOTIFY_GOOGLE_MAIL_KEYFILE`: the file described in [Google config file](#google-config-file) defaults to `gapps.json` * `GOTIFY_MAIL_DEFAULT_FROM`: Default `from` address in the mail, defaults to `admin@chalmers.it` -* `GOTIFY_MAIL_DEFAULT_REPLY_TO`: Default `reply-to` address in the mail, defaults to `no-reply@chalmers.it` +* `GOTIFY_MAIL_DEFAULT_REPLY_TO`: Default `reply_to` address in the mail, defaults to `no-reply@chalmers.it` +* `GOTIFY_MAIL_DEFAULT_CONTENT_TYPE`: Default `content_type` in mail, default so `text/html; charset=ISO-8859-1` * `GOTIFY_MOCK_MODE`: Enable mock mode, defaults to `false` ### Google config file diff --git a/api/proto/v1/mail.proto b/api/proto/v1/mail.proto index 140639e..b3f698c 100644 --- a/api/proto/v1/mail.proto +++ b/api/proto/v1/mail.proto @@ -18,5 +18,6 @@ message Mail { string from = 2; string reply_to = 3; string subject = 4; - string body = 5; + string content_type = 5; + string body = 6; } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2b51e61..e28f6a7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,9 +11,9 @@ services: volumes: - .:/app environment: - GOTIFY_DEBUG-MODE: "true" - GOTIFY_MOCK-MODE: "true" - GOTIFY_GOOGLE-MAIL.KEYFILE: "gapps.json" + GOTIFY_DEBUG_MODE: "true" + GOTIFY_MOCK_MODE: "true" + GOTIFY_GOOGLE_MAIL_KEYFILE: "gapps.json" dev-gen: build: dockerfile: dev.Dockerfile @@ -22,7 +22,7 @@ services: volumes: - .:/app command: ./scripts/proto-watcher.sh - swagger: # Generated swagger file i quite bad at the time of writing + swagger: # Generated swagger file is quite bad at the time of writing image: swaggerapi/swagger-ui ports: - "8000:8000" diff --git a/go.mod b/go.mod index 39deb07..681582f 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,8 @@ module github.com/cthit/gotify -go 1.13 +go 1.14 require ( - github.com/coreos/bbolt v1.3.2 github.com/golang/protobuf v1.3.4 github.com/grpc-ecosystem/grpc-gateway v1.9.0 github.com/spf13/viper v1.5.0 diff --git a/internal/app/app.go b/internal/app/app.go index abeb9f1..fc4e2c4 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -38,7 +38,7 @@ func Start() error { mailService, _ = mock.NewService() } - mailService = mail.NewService(mailService, c.MailDefaultFrom(), c.MailDefaultReplyTo()) + mailService = mail.NewService(mailService, c.MailDefaultFrom(), c.MailDefaultReplyTo(), c.MailDefaultContentType()) fmt.Printf("Done! \n") diff --git a/internal/app/config/config.go b/internal/app/config/config.go index d7ee603..be5a972 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -15,6 +15,7 @@ func LoadConfig() (*Config, error) { viper.SetDefault("google-mail.keyfile", "gapps.json") viper.SetDefault("mail.default-from", "admin@chalmers.it") viper.SetDefault("mail.default-reply-to", "no-reply@chalmers.it") + viper.SetDefault("mail.default-content-type", "text/html; charset=ISO-8859-1") viper.SetDefault("mock-mode", false) viper.SetEnvPrefix("gotify") @@ -63,3 +64,7 @@ func (*Config) MailDefaultFrom() string { func (*Config) MailDefaultReplyTo() string { return viper.GetString("mail.default-reply-to") } + +func (*Config) MailDefaultContentType() string { + return viper.GetString("mail.default-content-type") +} diff --git a/internal/app/grpc/mail.go b/internal/app/grpc/mail.go index 02da7be..0177ad2 100644 --- a/internal/app/grpc/mail.go +++ b/internal/app/grpc/mail.go @@ -7,12 +7,13 @@ import ( ) func (s *Server) SendMail(_ context.Context, in *gotify.Mail) (*gotify.Mail, error) { - //validate yo + //TODO: validate m := mail.Mail{ To: in.To, From: in.From, ReplyTo: in.ReplyTo, Subject: in.Subject, + ContentType: in.ContentType, Body: in.Body, } @@ -20,12 +21,13 @@ func (s *Server) SendMail(_ context.Context, in *gotify.Mail) (*gotify.Mail, err if err != nil { return nil, err } - //handle them errors + //TODO: handle errors return &gotify.Mail{ To: m.To, From: m.From, ReplyTo: m.ReplyTo, Subject: m.Subject, + ContentType: m.ContentType, Body: m.Body, }, nil } diff --git a/internal/app/grpc/sever.go b/internal/app/grpc/sever.go index 578bc1c..1679088 100644 --- a/internal/app/grpc/sever.go +++ b/internal/app/grpc/sever.go @@ -13,18 +13,18 @@ import ( ) type Server struct { - rpcPort, webPort string - debug bool - mailService mail.Service - wg sync.WaitGroup + rpcPort, webPort string + debug bool + mailService mail.Service + wg sync.WaitGroup } func NewServer(rpcPort, webPort string, debug bool, mailService mail.Service) (*Server, error) { return &Server{ - rpcPort: rpcPort, - webPort: webPort, - debug: debug, - mailService: mailService, + rpcPort: rpcPort, + webPort: webPort, + debug: debug, + mailService: mailService, }, nil } @@ -62,7 +62,7 @@ func (s *Server) startREST() error { mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithInsecure()} - err := gotify.RegisterMailerHandlerFromEndpoint(ctx, mux, "localhost:" + s.rpcPort, opts) + err := gotify.RegisterMailerHandlerFromEndpoint(ctx, mux, "localhost:"+s.rpcPort, opts) if err != nil { return err } diff --git a/pkg/mail/defaults_wrapper.go b/pkg/mail/defaults_wrapper.go index aa4fbfe..d0fb435 100644 --- a/pkg/mail/defaults_wrapper.go +++ b/pkg/mail/defaults_wrapper.go @@ -6,13 +6,15 @@ type DefaultsWrapper struct { ms Service mailDefaultFromAddress string mailDefaultReplyToAddress string + mailDefaultContentType string } -func NewService(ms Service, mailDefaultFromAddress string, mailDefaultReplyToAddress string) *DefaultsWrapper { +func NewService(ms Service, mailDefaultFromAddress, mailDefaultReplyToAddress, mailDefaultContentType string) *DefaultsWrapper { return &DefaultsWrapper{ ms: ms, mailDefaultFromAddress: mailDefaultFromAddress, mailDefaultReplyToAddress: mailDefaultReplyToAddress, + mailDefaultContentType: mailDefaultContentType, } } @@ -24,6 +26,11 @@ func (w DefaultsWrapper) SendMail(mail Mail) (Mail, error) { if strings.TrimSpace(mail.ReplyTo) == "" { mail.ReplyTo = w.mailDefaultReplyToAddress } + + if strings.TrimSpace(mail.ContentType) == "" { + mail.ContentType = w.mailDefaultContentType + } + return w.ms.SendMail(mail) } diff --git a/pkg/mail/gmail/service.go b/pkg/mail/gmail/service.go index 48dbe11..cd59fac 100644 --- a/pkg/mail/gmail/service.go +++ b/pkg/mail/gmail/service.go @@ -22,8 +22,8 @@ const googleInvalidEmailErrorMessage = `Response: { }` type googleService struct { - config jwt.Config - debug bool + config jwt.Config + debug bool } func (g *googleService) mailService(from string) (*gmail.Service, error) { @@ -46,8 +46,8 @@ func NewService(keyPath string, debug bool) (mail.Service, error) { } gs := &googleService{ - config: *config, - debug: debug, + config: *config, + debug: debug, } return gs, err @@ -63,6 +63,7 @@ func (g *googleService) SendMail(m mail.Mail) (mail.Mail, error) { msgRaw := "From: " + m.From + "\r\n" + "To: " + m.To + "\r\n" + "Reply-To: " + m.ReplyTo + "\r\n" + + "Content-Type: " + m.ContentType + "\r\n" + "Subject: " + mail.EncodeHeader(m.Subject) + "\r\n\r\n" + m.Body + "\r\n" diff --git a/pkg/mail/mail.go b/pkg/mail/mail.go index e7f8c08..eadb6cd 100644 --- a/pkg/mail/mail.go +++ b/pkg/mail/mail.go @@ -1,9 +1,10 @@ package mail type Mail struct { - To string `json:"to"` - From string `json:"from"` - ReplyTo string `json:"reply:to"` - Subject string `json:"subject"` - Body string `json:"body"` + To string `json:"to"` + From string `json:"from"` + ReplyTo string `json:"reply_to"` + Subject string `json:"subject"` + ContentType string `json:"content_type"` + Body string `json:"body"` } From e9144f5fecdefd936a6295b0d201b5d661f84e93 Mon Sep 17 00:00:00 2001 From: williamleven Date: Fri, 25 Sep 2020 23:44:41 +0200 Subject: [PATCH 18/23] add environment setting and cors module --- README.md | 1 + internal/app/app.go | 1 + internal/app/config/config.go | 12 ++++++++++++ internal/app/config/environment.go | 7 +++++++ internal/app/grpc/cors.go | 22 ++++++++++++++++++++++ internal/app/grpc/mail.go | 20 ++++++++++---------- internal/app/grpc/sever.go | 12 +++++++++--- 7 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 internal/app/config/environment.go create mode 100644 internal/app/grpc/cors.go diff --git a/README.md b/README.md index 577929a..e366b66 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ to `gapps.json` * `GOTIFY_MAIL_DEFAULT_REPLY_TO`: Default `reply_to` address in the mail, defaults to `no-reply@chalmers.it` * `GOTIFY_MAIL_DEFAULT_CONTENT_TYPE`: Default `content_type` in mail, default so `text/html; charset=ISO-8859-1` * `GOTIFY_MOCK_MODE`: Enable mock mode, defaults to `false` +* `GOTIFY_ENVIRONMENT`: (`test` | `production` | `development`), defaults to `development` ### Google config file This file (gapps.json by default config) should be placed in the working directory diff --git a/internal/app/app.go b/internal/app/app.go index fc4e2c4..927c21f 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -47,6 +47,7 @@ func Start() error { server, err := grpc.NewServer( c.RPCPort(), c.WebPort(), + c.Environment(), c.Debug(), mailService, ) diff --git a/internal/app/config/config.go b/internal/app/config/config.go index be5a972..5c508e9 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -17,6 +17,7 @@ func LoadConfig() (*Config, error) { viper.SetDefault("mail.default-reply-to", "no-reply@chalmers.it") viper.SetDefault("mail.default-content-type", "text/html; charset=ISO-8859-1") viper.SetDefault("mock-mode", false) + viper.SetDefault("environment", EnvDevelopment) viper.SetEnvPrefix("gotify") viper.AutomaticEnv() @@ -53,6 +54,17 @@ func (*Config) Mock() bool { return viper.GetBool("mock-mode") } +func (*Config) Environment() string { + switch viper.GetString("environment") { + case EnvTest: + return EnvTest + case EnvProduction: + return EnvProduction + default: + return EnvDevelopment + } +} + func (*Config) GmailKeyfile() string { return viper.GetString("google-mail.keyfile") } diff --git a/internal/app/config/environment.go b/internal/app/config/environment.go new file mode 100644 index 0000000..6967771 --- /dev/null +++ b/internal/app/config/environment.go @@ -0,0 +1,7 @@ +package config + +const ( + EnvProduction = "production" + EnvDevelopment = "development" + EnvTest = "test" +) diff --git a/internal/app/grpc/cors.go b/internal/app/grpc/cors.go new file mode 100644 index 0000000..4f629c5 --- /dev/null +++ b/internal/app/grpc/cors.go @@ -0,0 +1,22 @@ +package grpc + +import ( + "net/http" +) + +// allowCORS allows Cross Origin Resoruce Sharing from any origin. +func allowCORS(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + origin := r.Header.Get("Origin") + if origin != "" { + w.Header().Set("Access-Control-Allow-Origin", origin) + + if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" { + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization") + w.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE") + return + } + } + h.ServeHTTP(w, r) + }) +} diff --git a/internal/app/grpc/mail.go b/internal/app/grpc/mail.go index 0177ad2..27a3c11 100644 --- a/internal/app/grpc/mail.go +++ b/internal/app/grpc/mail.go @@ -9,12 +9,12 @@ import ( func (s *Server) SendMail(_ context.Context, in *gotify.Mail) (*gotify.Mail, error) { //TODO: validate m := mail.Mail{ - To: in.To, - From: in.From, - ReplyTo: in.ReplyTo, - Subject: in.Subject, + To: in.To, + From: in.From, + ReplyTo: in.ReplyTo, + Subject: in.Subject, ContentType: in.ContentType, - Body: in.Body, + Body: in.Body, } m, err := s.mailService.SendMail(m) @@ -23,11 +23,11 @@ func (s *Server) SendMail(_ context.Context, in *gotify.Mail) (*gotify.Mail, err } //TODO: handle errors return &gotify.Mail{ - To: m.To, - From: m.From, - ReplyTo: m.ReplyTo, - Subject: m.Subject, + To: m.To, + From: m.From, + ReplyTo: m.ReplyTo, + Subject: m.Subject, ContentType: m.ContentType, - Body: m.Body, + Body: m.Body, }, nil } diff --git a/internal/app/grpc/sever.go b/internal/app/grpc/sever.go index 1679088..bf9e423 100644 --- a/internal/app/grpc/sever.go +++ b/internal/app/grpc/sever.go @@ -3,6 +3,7 @@ package grpc import ( "context" "fmt" + "github.com/cthit/gotify/internal/app/config" "github.com/cthit/gotify/pkg/api/v1" "github.com/cthit/gotify/pkg/mail" "github.com/grpc-ecosystem/grpc-gateway/runtime" @@ -15,15 +16,17 @@ import ( type Server struct { rpcPort, webPort string debug bool + env string mailService mail.Service wg sync.WaitGroup } -func NewServer(rpcPort, webPort string, debug bool, mailService mail.Service) (*Server, error) { +func NewServer(rpcPort, webPort string, env string, debug bool, mailService mail.Service) (*Server, error) { return &Server{ rpcPort: rpcPort, webPort: webPort, debug: debug, + env: env, mailService: mailService, }, nil } @@ -60,11 +63,14 @@ func (s *Server) startREST() error { ctx, cancel := context.WithCancel(ctx) defer cancel() - mux := runtime.NewServeMux() + var mux http.Handler = runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithInsecure()} - err := gotify.RegisterMailerHandlerFromEndpoint(ctx, mux, "localhost:"+s.rpcPort, opts) + err := gotify.RegisterMailerHandlerFromEndpoint(ctx, mux.(*runtime.ServeMux), "localhost:"+s.rpcPort, opts) if err != nil { return err } + if s.env == config.EnvDevelopment { + mux = allowCORS(mux) + } return http.ListenAndServe(":"+s.webPort, mux) } From 89d7344ed4768187f0af882c32cf77ac1ed91a96 Mon Sep 17 00:00:00 2001 From: williamleven Date: Sat, 26 Sep 2020 00:11:34 +0200 Subject: [PATCH 19/23] Make swagger usefull --- Makefile | 2 +- api/proto/v1/mail.proto | 6 ++++++ api/swagger/v1/apidocs.swagger.json | 1 + docker-compose.yml | 2 +- internal/app/app.go | 1 + scripts/protoc-gen.sh | 18 +++++++++++++++--- 6 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 api/swagger/v1/apidocs.swagger.json diff --git a/Makefile b/Makefile index fd72804..a91d8f5 100644 --- a/Makefile +++ b/Makefile @@ -25,4 +25,4 @@ dev: .PHONY: clean clean: rm -rf pkg/api - rm -rf api/swagger + git restore api/swagger diff --git a/api/proto/v1/mail.proto b/api/proto/v1/mail.proto index b3f698c..8f3acd6 100644 --- a/api/proto/v1/mail.proto +++ b/api/proto/v1/mail.proto @@ -3,6 +3,12 @@ syntax = "proto3"; package gotify; import "google/api/annotations.proto"; +import "protoc-gen-swagger/options/annotations.proto"; + +option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { + host: "localhost:8080" + schemes: HTTP; +}; service Mailer { rpc SendMail(Mail) returns (Mail) { diff --git a/api/swagger/v1/apidocs.swagger.json b/api/swagger/v1/apidocs.swagger.json new file mode 100644 index 0000000..81921ef --- /dev/null +++ b/api/swagger/v1/apidocs.swagger.json @@ -0,0 +1 @@ +PLACEHOLDER FILE \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e28f6a7..2b7f228 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,7 @@ services: volumes: - .:/app command: ./scripts/proto-watcher.sh - swagger: # Generated swagger file is quite bad at the time of writing + swagger: image: swaggerapi/swagger-ui ports: - "8000:8000" diff --git a/internal/app/app.go b/internal/app/app.go index 927c21f..6604582 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -21,6 +21,7 @@ func Start() error { fmt.Printf("Debug mode is set to: %t \n", c.Debug()) fmt.Printf("Mock mode is set to: %t \n", c.Mock()) + fmt.Printf("Environment is set to: %s \n", c.Environment()) fmt.Printf("Setting up services...") diff --git a/scripts/protoc-gen.sh b/scripts/protoc-gen.sh index 8c37efb..de67264 100755 --- a/scripts/protoc-gen.sh +++ b/scripts/protoc-gen.sh @@ -5,9 +5,21 @@ function generate_for_for_version { for f in $PROTOFILES; do mkdir -p api/swagger/$1 mkdir -p pkg/api/$1 - protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --go_out=plugins=grpc:pkg/api/$1 $(basename $f) - protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --grpc-gateway_out=logtostderr=true:pkg/api/$1 $(basename $f) - protoc --proto_path=api/proto/$1 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --swagger_out=allow_merge=true,logtostderr=true:api/swagger/$1 $(basename $f) + protoc --proto_path=api/proto/$1 \ + -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ + -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/ \ + --go_out=plugins=grpc:pkg/api/$1 \ + $(basename $f) + protoc --proto_path=api/proto/$1 \ + -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ + -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/ \ + --grpc-gateway_out=logtostderr=true:pkg/api/$1 \ + $(basename $f) + protoc --proto_path=api/proto/$1 \ + -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ + -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/ \ + --swagger_out=allow_merge=true,logtostderr=true:api/swagger/$1 \ + $(basename $f) done } From ddce4b96cc6f5aa65ccaf7a65675f3664144f3da Mon Sep 17 00:00:00 2001 From: williamleven Date: Sat, 26 Sep 2020 00:13:20 +0200 Subject: [PATCH 20/23] Dont use todo context --- pkg/mail/gmail/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/mail/gmail/service.go b/pkg/mail/gmail/service.go index cd59fac..7c4ffe4 100644 --- a/pkg/mail/gmail/service.go +++ b/pkg/mail/gmail/service.go @@ -30,7 +30,7 @@ func (g *googleService) mailService(from string) (*gmail.Service, error) { // make sure to not edit the original config c := g.config c.Subject = from - return gmail.NewService(context.TODO(), option.WithScopes(gmail.GmailSendScope), option.WithTokenSource(c.TokenSource(context.TODO()))) + return gmail.NewService(context.Background(), option.WithScopes(gmail.GmailSendScope), option.WithTokenSource(c.TokenSource(context.TODO()))) } func NewService(keyPath string, debug bool) (mail.Service, error) { From 85c05aac6c43721791eac474f51d68782a67eea1 Mon Sep 17 00:00:00 2001 From: williamleven Date: Sat, 26 Sep 2020 00:42:01 +0200 Subject: [PATCH 21/23] add linter --- .golangci.toml | 10 ++++++++ Makefile | 12 ++++++++++ cmd/gotify/main.go | 4 +++- internal/app/app.go | 2 ++ internal/app/config/config.go | 6 +++-- internal/app/grpc/mail.go | 1 + internal/app/grpc/{sever.go => server.go} | 29 ++++++++++++++++------- pkg/mail/gmail/service.go | 11 +++++---- pkg/mail/mock/service.go | 2 +- 9 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 .golangci.toml rename internal/app/grpc/{sever.go => server.go} (90%) diff --git a/.golangci.toml b/.golangci.toml new file mode 100644 index 0000000..fe11127 --- /dev/null +++ b/.golangci.toml @@ -0,0 +1,10 @@ +[linters] +enable = [ + "gofmt", + "goimports", + "whitespace", + "wsl" +] + +[linters-settings.goimports] +local-prefixes = "github.com/cthit/gotify" \ No newline at end of file diff --git a/Makefile b/Makefile index a91d8f5..54de9d8 100644 --- a/Makefile +++ b/Makefile @@ -26,3 +26,15 @@ dev: clean: rm -rf pkg/api git restore api/swagger + +.PHONY: lint +lint: + golangci-lint run + +.PHONY: lint-fix +lint-fix: + golangci-lint run --fix + +.PHONY: lint-docker +lint-docker: + docker run --rm -v `pwd`:/app -w /app golangci/golangci-lint:v1.31.0-alpine golangci-lint run diff --git a/cmd/gotify/main.go b/cmd/gotify/main.go index 5b4e63d..5d57462 100644 --- a/cmd/gotify/main.go +++ b/cmd/gotify/main.go @@ -2,8 +2,9 @@ package main import ( "fmt" - "github.com/cthit/gotify/internal/app" "os" + + "github.com/cthit/gotify/internal/app" ) func main() { @@ -12,5 +13,6 @@ func main() { fmt.Printf("Crash: %v\n", err) os.Exit(1) } + os.Exit(0) } diff --git a/internal/app/app.go b/internal/app/app.go index 6604582..3d925b7 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -45,6 +45,7 @@ func Start() error { fmt.Printf("Serving application on port %s \n", c.WebPort()) fmt.Printf("Serving rpc on port %s \n", c.RPCPort()) + server, err := grpc.NewServer( c.RPCPort(), c.WebPort(), @@ -58,5 +59,6 @@ func Start() error { } server.Start() + return nil } diff --git a/internal/app/config/config.go b/internal/app/config/config.go index 5c508e9..39b5340 100644 --- a/internal/app/config/config.go +++ b/internal/app/config/config.go @@ -2,8 +2,9 @@ package config import ( "fmt" - "github.com/spf13/viper" "strings" + + "github.com/spf13/viper" ) type Config struct{} @@ -29,12 +30,13 @@ func LoadConfig() (*Config, error) { err := viper.ReadInConfig() if err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { //nolint:gosimple fmt.Println("Failed to read config from file") } else { return &Config{}, err } } + return &Config{}, nil } diff --git a/internal/app/grpc/mail.go b/internal/app/grpc/mail.go index 27a3c11..ada98be 100644 --- a/internal/app/grpc/mail.go +++ b/internal/app/grpc/mail.go @@ -2,6 +2,7 @@ package grpc import ( "context" + gotify "github.com/cthit/gotify/pkg/api/v1" "github.com/cthit/gotify/pkg/mail" ) diff --git a/internal/app/grpc/sever.go b/internal/app/grpc/server.go similarity index 90% rename from internal/app/grpc/sever.go rename to internal/app/grpc/server.go index bf9e423..4181291 100644 --- a/internal/app/grpc/sever.go +++ b/internal/app/grpc/server.go @@ -3,14 +3,16 @@ package grpc import ( "context" "fmt" - "github.com/cthit/gotify/internal/app/config" - "github.com/cthit/gotify/pkg/api/v1" - "github.com/cthit/gotify/pkg/mail" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "google.golang.org/grpc" "net" "net/http" "sync" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + "github.com/cthit/gotify/internal/app/config" + gotify "github.com/cthit/gotify/pkg/api/v1" + "github.com/cthit/gotify/pkg/mail" ) type Server struct { @@ -33,17 +35,21 @@ func NewServer(rpcPort, webPort string, env string, debug bool, mailService mail func (s *Server) Start() { s.wg.Add(1) + go func() { err := s.startGRPC() fmt.Println(err) s.wg.Done() }() + s.wg.Add(1) + go func() { err := s.startREST() fmt.Println(err) s.wg.Done() }() + s.wg.Wait() } @@ -52,25 +58,30 @@ func (s *Server) startGRPC() error { if err != nil { return err } + grpcServer := grpc.NewServer() + gotify.RegisterMailerServer(grpcServer, s) - grpcServer.Serve(lis) - return nil + + return grpcServer.Serve(lis) } func (s *Server) startREST() error { - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) + ctx, cancel := context.WithCancel(context.Background()) defer cancel() var mux http.Handler = runtime.NewServeMux() + opts := []grpc.DialOption{grpc.WithInsecure()} + err := gotify.RegisterMailerHandlerFromEndpoint(ctx, mux.(*runtime.ServeMux), "localhost:"+s.rpcPort, opts) if err != nil { return err } + if s.env == config.EnvDevelopment { mux = allowCORS(mux) } + return http.ListenAndServe(":"+s.webPort, mux) } diff --git a/pkg/mail/gmail/service.go b/pkg/mail/gmail/service.go index 7c4ffe4..4055668 100644 --- a/pkg/mail/gmail/service.go +++ b/pkg/mail/gmail/service.go @@ -1,19 +1,18 @@ package gmail import ( + "encoding/base64" "fmt" "io/ioutil" "strings" - "github.com/cthit/gotify/pkg/mail" + "golang.org/x/net/context" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" "google.golang.org/api/gmail/v1" "google.golang.org/api/option" - "encoding/base64" - - "golang.org/x/net/context" + "github.com/cthit/gotify/pkg/mail" ) const googleInvalidEmailErrorMessage = `Response: { @@ -30,6 +29,7 @@ func (g *googleService) mailService(from string) (*gmail.Service, error) { // make sure to not edit the original config c := g.config c.Subject = from + return gmail.NewService(context.Background(), option.WithScopes(gmail.GmailSendScope), option.WithTokenSource(c.TokenSource(context.TODO()))) } @@ -54,7 +54,6 @@ func NewService(keyPath string, debug bool) (mail.Service, error) { } func (g *googleService) SendMail(m mail.Mail) (mail.Mail, error) { - mailService, err := g.mailService(m.From) if err != nil { return m, err @@ -70,11 +69,13 @@ func (g *googleService) SendMail(m mail.Mail) (mail.Mail, error) { msg := &gmail.Message{ Raw: base64.RawURLEncoding.EncodeToString([]byte(msgRaw)), } + _, err = mailService.Users.Messages.Send(m.From, msg).Context(context.Background()).Do() if err != nil { if strings.Contains(err.Error(), googleInvalidEmailErrorMessage) { return m, fmt.Errorf("Invalid from email, email must exists") } + return m, err } diff --git a/pkg/mail/mock/service.go b/pkg/mail/mock/service.go index 75dbda5..9dfe707 100644 --- a/pkg/mail/mock/service.go +++ b/pkg/mail/mock/service.go @@ -2,6 +2,7 @@ package mock import ( "fmt" + "github.com/cthit/gotify/pkg/mail" ) @@ -13,7 +14,6 @@ func NewService() (mail.Service, error) { } func (g *mockService) SendMail(mail mail.Mail) (mail.Mail, error) { - fmt.Printf("Sending mail:\n %#v \n", mail) return mail, nil From fcf42e7f306409a6210fffeb3e2ab1c798ff9ab5 Mon Sep 17 00:00:00 2001 From: williamleven Date: Sat, 26 Sep 2020 18:40:54 +0200 Subject: [PATCH 22/23] add validation --- go.mod | 1 + go.sum | 1 + internal/app/grpc/errors.go | 22 +++++++++++ internal/app/grpc/mail.go | 16 ++++++-- internal/validation/strings.go | 63 +++++++++++++++++++++++++++++++ internal/validation/validation.go | 49 ++++++++++++++++++++++++ pkg/mail/validation.go | 52 +++++++++++++++++++++++++ 7 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 internal/app/grpc/errors.go create mode 100644 internal/validation/strings.go create mode 100644 internal/validation/validation.go create mode 100644 pkg/mail/validation.go diff --git a/go.mod b/go.mod index 681582f..af0ab03 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( github.com/golang/protobuf v1.3.4 github.com/grpc-ecosystem/grpc-gateway v1.9.0 + github.com/pkg/errors v0.8.0 github.com/spf13/viper v1.5.0 github.com/stretchr/testify v1.4.0 // indirect golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 diff --git a/go.sum b/go.sum index 2cc1460..ab1327b 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/internal/app/grpc/errors.go b/internal/app/grpc/errors.go new file mode 100644 index 0000000..7c9c26b --- /dev/null +++ b/internal/app/grpc/errors.go @@ -0,0 +1,22 @@ +package grpc + +import ( + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type grpcError struct { + error + status codes.Code +} + +func (ge grpcError) GRPCStatus() *status.Status { + return status.New(ge.status, ge.Error()) +} + +func WithErrorStatus(err error, status codes.Code) grpcError { + return grpcError{ + error: err, + status: status, + } +} \ No newline at end of file diff --git a/internal/app/grpc/mail.go b/internal/app/grpc/mail.go index ada98be..b46fc23 100644 --- a/internal/app/grpc/mail.go +++ b/internal/app/grpc/mail.go @@ -3,12 +3,15 @@ package grpc import ( "context" + "google.golang.org/grpc/codes" + gotify "github.com/cthit/gotify/pkg/api/v1" "github.com/cthit/gotify/pkg/mail" + + "github.com/pkg/errors" ) func (s *Server) SendMail(_ context.Context, in *gotify.Mail) (*gotify.Mail, error) { - //TODO: validate m := mail.Mail{ To: in.To, From: in.From, @@ -18,11 +21,16 @@ func (s *Server) SendMail(_ context.Context, in *gotify.Mail) (*gotify.Mail, err Body: in.Body, } - m, err := s.mailService.SendMail(m) + err := mail.Validate(m) + if err != nil { + return nil, WithErrorStatus(err, codes.InvalidArgument) + } + + m, err = s.mailService.SendMail(m) if err != nil { - return nil, err + return nil, WithErrorStatus(errors.Wrap(err, "failed to send mail"), codes.Internal) } - //TODO: handle errors + return &gotify.Mail{ To: m.To, From: m.From, diff --git a/internal/validation/strings.go b/internal/validation/strings.go new file mode 100644 index 0000000..44fc0b5 --- /dev/null +++ b/internal/validation/strings.go @@ -0,0 +1,63 @@ +package validation + +import ( + "errors" + "regexp" +) + +var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") + +func OrString(errFuncs ...func(string) error) func(string) error { + return func(s string) error { + errs := make([]error, len(errFuncs)) + for i, errFunc := range errFuncs { + errs[i] = errFunc(s) + } + + return Or(errs...) + } +} + +func AndString(errFuncs ...func(string) error) func(string) error { + return func(s string) error { + errs := make([]error, len(errFuncs)) + for i, errFunc := range errFuncs { + errs[i] = errFunc(s) + } + + return And(errs...) + } +} + +func FieldString(name string, value string, errFuncs ...func(string) error) error { + errs := make([]error, len(errFuncs)) + for i, errFunc := range errFuncs { + errs[i] = errFunc(value) + } + + return Field(name, errs...) +} + +func IsEmpty(s string) error { + if s == "" { + return nil + } + + return errors.New("should be empty") +} + +func IsNotEmpty(s string) error { + if s != "" { + return nil + } + + return errors.New("should not be empty") +} + +func IsEmail(s string) error { + if emailRegexp.MatchString(s) { + return nil + } + + return errors.New("should be an email address") +} \ No newline at end of file diff --git a/internal/validation/validation.go b/internal/validation/validation.go new file mode 100644 index 0000000..e5bc079 --- /dev/null +++ b/internal/validation/validation.go @@ -0,0 +1,49 @@ +package validation + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" +) + +func Or(errs ...error) error { + var messages []string + + for _, err := range errs { + if err != nil { + messages = append(messages, err.Error()) + } else { + return nil + } + } + + return fmt.Errorf("should satisfy at least one of (%s)", strings.Join(messages, ", ")) +} + +func And(errs ...error) error { + var messages []string + + for _, err := range errs { + if err != nil { + messages = append(messages, err.Error()) + } + } + + if len(messages) == 0 { + return nil + } else if len(messages) == 1 { + return errors.New(messages[0]) + } + + return fmt.Errorf("should satisfy all of (%s)", strings.Join(messages, ", ")) +} + +func Field(name string, errs ...error) error { + err := And(errs...) + if err == nil { + return nil + } + + return errors.Wrapf(err, "field '%s' failed validation", name) +} \ No newline at end of file diff --git a/pkg/mail/validation.go b/pkg/mail/validation.go new file mode 100644 index 0000000..9d46e0e --- /dev/null +++ b/pkg/mail/validation.go @@ -0,0 +1,52 @@ +package mail + +import ( + "github.com/pkg/errors" + + "github.com/cthit/gotify/internal/validation" +) + +func Validate(mail Mail) error { + err := validation.And( + validation.FieldString( + "to", + mail.To, + validation.IsEmail, + ), + validation.FieldString( + "from", + mail.From, + validation.OrString( + validation.IsEmail, + validation.IsEmpty, + ), + ), + validation.FieldString( + "reply_to", + mail.ReplyTo, + validation.OrString( + validation.IsEmail, + validation.IsEmpty, + ), + ), + validation.FieldString( + "subject", + mail.Subject, + validation.IsNotEmpty, + ), + validation.FieldString( + "content_type", + mail.ContentType, + ), + validation.FieldString( + "body", + mail.Body, + validation.IsNotEmpty, + ), + ) + if err != nil { + return errors.Wrap(err, "validation failed") + } + + return nil +} From 19db80abcf294cc4226374acb75cec2915cdf2ef Mon Sep 17 00:00:00 2001 From: williamleven Date: Sat, 26 Sep 2020 20:49:24 +0200 Subject: [PATCH 23/23] add validation tests --- Makefile | 4 + internal/validation/strings.go | 5 +- internal/validation/strings_test.go | 326 +++++++++++++++++++++++++ internal/validation/validation_test.go | 198 +++++++++++++++ pkg/mail/validation_test.go | 98 ++++++++ 5 files changed, 629 insertions(+), 2 deletions(-) create mode 100644 internal/validation/strings_test.go create mode 100644 internal/validation/validation_test.go create mode 100644 pkg/mail/validation_test.go diff --git a/Makefile b/Makefile index 54de9d8..e7ff74a 100644 --- a/Makefile +++ b/Makefile @@ -38,3 +38,7 @@ lint-fix: .PHONY: lint-docker lint-docker: docker run --rm -v `pwd`:/app -w /app golangci/golangci-lint:v1.31.0-alpine golangci-lint run + +.PHONY: test +test: + go test ./... \ No newline at end of file diff --git a/internal/validation/strings.go b/internal/validation/strings.go index 44fc0b5..ab21936 100644 --- a/internal/validation/strings.go +++ b/internal/validation/strings.go @@ -3,6 +3,7 @@ package validation import ( "errors" "regexp" + "strings" ) var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") @@ -39,7 +40,7 @@ func FieldString(name string, value string, errFuncs ...func(string) error) erro } func IsEmpty(s string) error { - if s == "" { + if strings.TrimSpace(s) == "" { return nil } @@ -47,7 +48,7 @@ func IsEmpty(s string) error { } func IsNotEmpty(s string) error { - if s != "" { + if strings.TrimSpace(s) != "" { return nil } diff --git a/internal/validation/strings_test.go b/internal/validation/strings_test.go new file mode 100644 index 0000000..d77d0cf --- /dev/null +++ b/internal/validation/strings_test.go @@ -0,0 +1,326 @@ +package validation + +import ( + "testing" + + "github.com/pkg/errors" +) + +func willFail(s string) error { + return errors.New("always fails") +} +func willPass(s string) error { + return nil +} + +var stringAndTests = []struct { + name string + errFuncs []func(string) error + wantErr bool +}{ + { + name: "no ok", + errFuncs: []func(string) error{ + willFail, + willFail, + willFail, + }, + wantErr: true, + }, + { + name: "fail first", + errFuncs: []func(string) error{ + willFail, + willPass, + willPass, + }, + wantErr: true, + }, + { + name: "fail last", + errFuncs: []func(string) error{ + willPass, + willPass, + willFail, + }, + wantErr: true, + }, + { + name: "fail middle", + errFuncs: []func(string) error{ + willPass, + willFail, + willPass, + }, + wantErr: true, + }, + { + name: "two ok ", + errFuncs: []func(string) error{ + willFail, + willPass, + willPass, + }, + wantErr: true, + }, + { + name: "all ok ", + errFuncs: []func(string) error{ + willPass, + willPass, + willPass, + }, + wantErr: false, + }, + { + name: "one fail ", + errFuncs: []func(string) error{ + willFail, + }, + wantErr: true, + }, + { + name: "one ok ", + errFuncs: []func(string) error{ + willPass, + }, + wantErr: false, + }, + { + name: "none", + errFuncs: []func(string) error{}, + wantErr: false, + }, +} + +func TestAndString(t *testing.T) { + for _, tt := range stringAndTests { + t.Run(tt.name, func(t *testing.T) { + if err := AndString(tt.errFuncs...)(""); (err != nil) != tt.wantErr { + t.Errorf("AndString()() = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestOrString(t *testing.T) { + tests := []struct { + name string + errFuncs []func(string) error + wantErr bool + }{ + { + name: "no ok", + errFuncs: []func(string) error{ + willFail, + willFail, + willFail, + }, + wantErr: true, + }, + { + name: "ok first", + errFuncs: []func(string) error{ + willPass, + willFail, + willFail, + }, + wantErr: false, + }, + { + name: "ok last", + errFuncs: []func(string) error{ + willFail, + willFail, + willPass, + }, + wantErr: false, + }, + { + name: "ok middle", + errFuncs: []func(string) error{ + willFail, + willPass, + willFail, + }, + wantErr: false, + }, + { + name: "two ok ", + errFuncs: []func(string) error{ + willFail, + willPass, + willPass, + }, + wantErr: false, + }, + { + name: "all ok ", + errFuncs: []func(string) error{ + willPass, + willPass, + willPass, + }, + wantErr: false, + }, + { + name: "one fail ", + errFuncs: []func(string) error{ + willFail, + }, + wantErr: true, + }, + { + name: "one ok ", + errFuncs: []func(string) error{ + willPass, + }, + wantErr: false, + }, + { + name: "none", + errFuncs: []func(string) error{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := OrString(tt.errFuncs...)(""); (err != nil) != tt.wantErr { + t.Errorf("OrString()() = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestFieldString(t *testing.T) { + for _, tt := range stringAndTests { + t.Run(tt.name, func(t *testing.T) { + if err := FieldString("test", "value", tt.errFuncs...); (err != nil) != tt.wantErr { + t.Errorf("FieldString() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestIsEmail(t *testing.T) { + tests := []struct { + name string + s string + wantErr bool + }{ + { + name: "none", + s: "", + wantErr: true, + }, + { + name: "spaces", + s: " ", + wantErr: true, + }, + { + name: "no at", + s: "asdjhfb.com", + wantErr: true, + }, + { + name: "no dot", + s: "aaa@asdjhfb,com", + wantErr: true, + }, + { + name: "no beginning", + s: "@asdjhfb.com", + wantErr: true, + }, + { + name: "no end", + s: "aaa@asdjhfb.", + wantErr: true, + }, + { + name: "no middle", + s: "aaa@.asdjhfb", + wantErr: true, + }, + { + name: "valid", + s: "aaa@asdjhfb.com", + wantErr: false, + }, + { + name: "valid with dots", + s: "aa.aa.bb.cc@as.dj.h.fb.com", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := IsEmail(tt.s); (err != nil) != tt.wantErr { + t.Errorf("IsEmail() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestIsEmpty(t *testing.T) { + tests := []struct { + name string + s string + wantErr bool + }{ + { + name: "empty", + s: "", + wantErr: false, + }, + { + name: "not empty", + s: "a", + wantErr: true, + }, + { + name: "spaces", + s: " ", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := IsEmpty(tt.s); (err != nil) != tt.wantErr { + t.Errorf("IsEmpty() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestIsNotEmpty(t *testing.T) { + tests := []struct { + name string + s string + wantErr bool + }{ + { + name: "empty", + s: "", + wantErr: true, + }, + { + name: "not empty", + s: "a", + wantErr: false, + }, + { + name: "spaces", + s: " ", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := IsNotEmpty(tt.s); (err != nil) != tt.wantErr { + t.Errorf("IsNotEmpty() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} \ No newline at end of file diff --git a/internal/validation/validation_test.go b/internal/validation/validation_test.go new file mode 100644 index 0000000..48564a0 --- /dev/null +++ b/internal/validation/validation_test.go @@ -0,0 +1,198 @@ +package validation + +import ( + "testing" + + "github.com/pkg/errors" +) + +var e = errors.New("") + +var andTests = []struct { + name string + errs []error + wantErr bool +}{ + { + name: "no ok", + errs: []error{ + e, + e, + e, + }, + wantErr: true, + }, + { + name: "fail first", + errs: []error{ + e, + nil, + nil, + }, + wantErr: true, + }, + { + name: "fail last", + errs: []error{ + nil, + nil, + e, + }, + wantErr: true, + }, + { + name: "fail middle", + errs: []error{ + nil, + e, + nil, + }, + wantErr: true, + }, + { + name: "two ok ", + errs: []error{ + e, + nil, + nil, + }, + wantErr: true, + }, + { + name: "all ok ", + errs: []error{ + nil, + nil, + nil, + }, + wantErr: false, + }, + { + name: "one fail ", + errs: []error{ + e, + }, + wantErr: true, + }, + { + name: "one ok ", + errs: []error{ + nil, + }, + wantErr: false, + }, + { + name: "none", + errs: []error{}, + wantErr: false, + }, +} + +func TestAnd(t *testing.T) { + for _, tt := range andTests { + t.Run(tt.name, func(t *testing.T) { + if err := And(tt.errs...); (err != nil) != tt.wantErr { + t.Errorf("And() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestField(t *testing.T) { + for _, tt := range andTests { + t.Run(tt.name, func(t *testing.T) { + if err := Field("", tt.errs...); (err != nil) != tt.wantErr { + t.Errorf("Field() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestOr(t *testing.T) { + tests := []struct { + name string + errs []error + wantErr bool + }{ + { + name: "no ok", + errs: []error{ + e, + e, + e, + }, + wantErr: true, + }, + { + name: "ok first", + errs: []error{ + nil, + e, + e, + }, + wantErr: false, + }, + { + name: "ok last", + errs: []error{ + e, + e, + nil, + }, + wantErr: false, + }, + { + name: "ok middle", + errs: []error{ + e, + nil, + e, + }, + wantErr: false, + }, + { + name: "two ok ", + errs: []error{ + e, + nil, + nil, + }, + wantErr: false, + }, + { + name: "all ok ", + errs: []error{ + nil, + nil, + nil, + }, + wantErr: false, + }, + { + name: "one fail ", + errs: []error{ + e, + }, + wantErr: true, + }, + { + name: "one ok ", + errs: []error{ + nil, + }, + wantErr: false, + }, + { + name: "none", + errs: []error{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Or(tt.errs...); (err != nil) != tt.wantErr { + t.Errorf("Or() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} \ No newline at end of file diff --git a/pkg/mail/validation_test.go b/pkg/mail/validation_test.go new file mode 100644 index 0000000..d78ea50 --- /dev/null +++ b/pkg/mail/validation_test.go @@ -0,0 +1,98 @@ +package mail + +import "testing" + +func TestValidate(t *testing.T) { + tests := []struct { + name string + mail Mail + wantErr bool + }{ + { + name: "empty mail", + mail: Mail{}, + wantErr: true, + }, + { + name: "missing to", + mail: Mail{ + Subject: "not empty", + Body: "not empty", + }, + wantErr: true, + }, + { + name: "invalid to", + mail: Mail{ + To: "qwerty@abgc,com", + Subject: "not empty", + Body: "not empty", + }, + wantErr: true, + }, + { + name: "no optional fields", + mail: Mail{ + To: "qwerty@abgc.com", + Subject: "not empty", + Body: "not empty", + }, + wantErr: false, + }, + { + name: "no subject", + mail: Mail{ + To: "qwerty@abgc.com", + Body: "not empty", + }, + wantErr: true, + }, + { + name: "no body", + mail: Mail{ + To: "qwerty@abgc.com", + Subject: "not empty", + }, + wantErr: true, + }, + { + name: "invalid from", + mail: Mail{ + From: "qwerty@abgc,com", + To: "qwerty@abgc.com", + Subject: "not empty", + Body: "not empty", + }, + wantErr: true, + }, + { + name: "invalid reply-to", + mail: Mail{ + ReplyTo: "qwerty@abgc,com", + To: "qwerty@abgc.com", + Subject: "not empty", + Body: "not empty", + }, + wantErr: true, + }, + { + name: "all fields", + mail: Mail{ + To: "qwerty@abgc.com", + From: "qwerty@abgc.com", + ReplyTo: "qwerty@abgc.com", + Subject: "not empty", + ContentType: "some content type", + Body: "not empty", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := Validate(tt.mail); (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} \ No newline at end of file