Initial commit

This commit is contained in:
Bas Kloosterman 2022-05-02 16:26:06 +02:00
commit 77f1d417c9
53 changed files with 3914 additions and 0 deletions

13
.dockerignore Normal file
View File

@ -0,0 +1,13 @@
.git
data
run
bin
nginx
dist
.DS_Store
._.DS_Store
node_modules
package-lock.json

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
.DS_Store
._.DS_Store
node_modules
package-lock.json
dist/*
bin/*
data/db/data.db
nginx/flip_cohen.conf
data/config/config.yaml
dockercompose/*
!dockercompose/docker-compose.yaml
!.keep

48
Dockerfile Normal file
View File

@ -0,0 +1,48 @@
# syntax=docker/dockerfile:1
##
## Build
##
FROM golang:alpine AS build
WORKDIR /app
RUN apk update
RUN apk add --update gcc g++
RUN apk add --update python3
RUN apk add --update npm
COPY package.json ./package.json
RUN npm i
COPY ./src ./src
COPY ./gulpfile.js ./gulpfile.js
COPY ./webpack.config.js ./webpack.config.js
RUN npx gulp build
COPY go.mod .
COPY go.sum .
RUN go mod download
RUN go install github.com/mattn/go-sqlite3
COPY . ./
RUN ls -l
RUN mkdir bin
RUN go build -o bin/thomasshop
##
## Deploy
##
FROM alpine:latest
WORKDIR /
COPY --from=build /app/bin/thomasshop /bin
COPY --from=build /app/dist /dist
COPY --from=build /app/templates /templates
RUN echo "#!/bin/ash" > /run.sh
RUN echo "cp -r /dist/* /outdist" >> /run.sh
RUN echo "exec /bin/thomasshop" >> /run.sh
RUN chmod +x /run.sh
ENTRYPOINT ["/run.sh"]

21
Makefile Normal file
View File

@ -0,0 +1,21 @@
.PHONY: all clean config
all: bin/site_flip dist/js/shared.js dist/css/index.css config
bin/site_flip: main.go events.go albums.go
go build main.go events.go albums.go && mv main bin/site_flip
dist/js/shared.js: $(wildcard src/js/*.js src/js/*/*.js)
NODE_ENV=production gulp build
dist/css/index.css: $(wildcard src/sass/*.scss src/sass/*/*.scss)
NODE_ENV=production gulp build
config: nginx/flip_cohen.conf
nginx/flip_cohen.conf: nginx/template.conf
./init.sh `pwd`
clean:
rm -f nginx/flip_cohen.conf
rm -f bin/site_flip

152
albums.go Normal file
View File

@ -0,0 +1,152 @@
package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"strings"
)
type Song struct {
Title string
Lyrics []string
Path string
NR string
}
type Album struct {
Name string
Release string
Path string
Songs []Song
Links map[string]string
}
const ALBUM_DIR = "./data/albums/"
var albums []*Album
func parseAlbumName(fName string) (string, string) {
s := strings.Split(fName, ".")
s = strings.Split(s[0], "_")
return s[0], strings.Join(s[1:], " ")
}
func ParseSong(s os.FileInfo, basePath string) Song {
nr, title := parseAlbumName(s.Name())
f, err := os.Open(path.Join(basePath, s.Name()))
defer f.Close()
if err != nil {
log.Fatal(err)
}
content, err := ioutil.ReadAll(f)
if err != nil {
log.Fatal(err)
}
lyrics := strings.Split(string(content), "\n\n")
song := Song{
Title: title,
Lyrics: lyrics,
Path: strings.ToLower(strings.Replace(title, " ", "-", -1)),
NR: nr,
}
return song
}
func getSong(album *Album, title string) (Song, error) {
for _, song := range album.Songs {
if song.Path == strings.ToLower(title) {
return song, nil
}
}
return Song{}, io.EOF
}
func ParseAlbum(f os.FileInfo) *Album {
year, name := parseAlbumName(f.Name())
filen := path.Join(ROOT_DIR, ALBUM_DIR, f.Name())
files, err := ioutil.ReadDir(filen)
if err != nil {
fmt.Printf("Error read songs: %s", err)
log.Fatal(err)
}
album := Album{
Name: name,
Path: strings.ToLower(strings.Replace(name, " ", "-", -1)),
Release: year,
}
for _, f := range files {
if !strings.HasSuffix(f.Name(), ".lyric") {
continue
}
album.Songs = append(album.Songs, ParseSong(f, filen))
}
fd, err := os.Open(path.Join(filen, "info.json"))
defer fd.Close()
if err != nil {
fmt.Printf("Error info.json: %s", err)
log.Fatal(err)
}
content, _ := ioutil.ReadAll(fd)
json.Unmarshal(content, &album)
return &album
}
func getAlbum(albums []*Album, title string) (*Album, error) {
for _, album := range albums {
if album.Path == strings.ToLower(title) {
return album, nil
}
}
return nil, io.EOF
}
func getAlbums() []*Album {
if albums != nil {
return albums
}
files, err := ioutil.ReadDir(path.Join(ROOT_DIR, ALBUM_DIR))
if err != nil {
log.Fatal(err)
}
for _, f := range files {
if strings.HasPrefix(f.Name(), ".") {
continue
}
albums = append(albums, ParseAlbum(f))
}
for i := len(albums)/2 - 1; i >= 0; i-- {
opp := len(albums) - 1 - i
albums[i], albums[opp] = albums[opp], albums[i]
}
return albums
}

0
data/config/.keep Normal file
View File

0
data/db/.keep Normal file
View File

136
db.go Normal file
View File

@ -0,0 +1,136 @@
package main
import (
"fmt"
"log"
"os"
"path"
"time"
uuid "github.com/satori/go.uuid"
sqlite "gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
const (
PRE_ORDER_STATE_CREATED = "CREATED"
)
func initDB() (*gorm.DB, error) {
log.Printf("Connect to DB on path: %v", path.Join(ROOT_DIR, "data/db/data.db"))
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Info, // Log level
Colorful: false, // Disable color
},
)
db, err := gorm.Open(sqlite.Open(path.Join(ROOT_DIR, "data/db/data.db")), &gorm.Config{
Logger: newLogger,
})
return db, err
}
type Product struct {
gorm.Model
Name string
Image string
Description string
Price int
}
type Item struct {
gorm.Model
ProductID uint
Product Product
PreOrderID uuid.UUID
PreOrder PreOrder
}
type MolliePayment struct {
gorm.Model
MolliePaymentID string
Amount int
Status string
PreOrderID uuid.UUID
PreOrder PreOrder
}
type PreOrder struct {
ID uuid.UUID `sql:"type:string;primary_key;"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time `sql:"index"`
Name string
Email string
Items []Item
Address string
Postcode string
City string
Country string
Status string
MolliePayments []MolliePayment
}
var nullUUID = uuid.UUID{}
func (base *PreOrder) BeforeCreate(scope *gorm.DB) error {
fmt.Println(base.ID.String(), nullUUID.String())
if base.ID.String() == nullUUID.String() {
id := uuid.NewV4()
base.ID = id
}
return nil
}
func (po *PreOrder) CalcTotal() int {
total := 0
for _, i := range po.Items {
total += i.Product.Price
}
return total
}
func insertProducts(db *gorm.DB) {
db.Create(&Product{
Name: "Methods to Madness | LP",
Image: "/static/img/mtm.jpg",
Description: "Methods to Madness LP",
Price: 2999,
})
db.Create(&Product{
Name: "Methods to Madness | CD",
Image: "/static/img/mtm.jpg",
Description: "Methods to Madness CD",
Price: 1999,
})
}
func GetDB() *gorm.DB {
db, err := initDB()
if err != nil {
panic("failed to connect database")
}
// Migrate the schema
db.AutoMigrate(
&Product{},
&Item{},
&PreOrder{},
&MolliePayment{},
)
products := []Product{}
db.Find(&products)
return db
}

0
dist/.keep vendored Normal file
View File

View File

@ -0,0 +1,18 @@
version: '3.0'
services:
alex:
image: dregistry.baskloosterman.nl:5000/thomasshop
container_name: thomasshop
restart: always
volumes:
- "./thomas/data:/data"
- "./thomas/dist:/outdist"
nginx:
image: nginx:alpine
container_name: nginx_thomas
restart: always
volumes:
- "./thomas/nginx:/etc/nginx/conf.d"
- "./thomas/dist:/var/www/thomas/dist"
ports:
- "3999:80"

185
events.go Normal file
View File

@ -0,0 +1,185 @@
package main
import (
"bufio"
"encoding/csv"
"encoding/json"
"fmt"
"io"
"log"
"os"
"path"
"sort"
"strings"
"time"
)
type MyTime time.Time
func (mt *MyTime) UnmarshalJSON(b []byte) error {
s := strings.Trim(string(b), "\"")
t, err := time.Parse("2006-01-02", s)
if err != nil {
return err
}
*mt = MyTime(t)
return nil
}
func ParseDate(b string) MyTime {
s := strings.Trim(b, "\"")
t, err := time.Parse("2006-01-02", s)
if err != nil {
return MyTime{}
}
return MyTime(t)
}
func (mt *MyTime) ParseDate(b []byte) error {
s := strings.Trim(string(b), "\"")
t, err := time.Parse("2006-01-02", s)
if err != nil {
return err
}
*mt = MyTime(t)
return nil
}
func (mt MyTime) MarshalJSON() ([]byte, error) {
return json.Marshal(mt)
}
var months = map[string]string{
"1": "Jan",
"2": "Feb",
"3": "Mrt",
"4": "Apr",
"5": "Mei",
"6": "Jun",
"7": "Jul",
"8": "Aug",
"9": "Sept",
"10": "Okt",
"11": "Nov",
"12": "Dec",
}
var monthsLong = map[string]string{
"1": "Januari",
"2": "Februari",
"3": "Maart",
"4": "April",
"5": "Mei",
"6": "Juni",
"7": "July",
"8": "Augustus",
"9": "September",
"10": "Oktober",
"11": "November",
"12": "December",
}
func (mt MyTime) Format() string {
t := time.Time(mt)
return fmt.Sprintf("%v %v %v", t.Format("Jan"), t.Format("02"), t.Format("2006"))
}
func (mt MyTime) Title() string {
t := time.Time(mt)
return fmt.Sprintf("%v %v", t.Format("Januari"), t.Format("2006"))
}
type EventFlags struct {
Soldout bool
Tryout bool
Premiere bool
Preview bool
XL bool
Solo bool
}
type Event struct {
Date MyTime `json:"date"`
Venue string `json:"venue"`
City string `json:"city"`
Link string `json:"link"`
Time string `json:"time"`
Title string `json:"title"`
Flags EventFlags `json:"flags"`
}
func (e *Event) SetFlags(flags string) {
for _, flag := range strings.Split(flags, ",") {
switch strings.Trim(strings.ToLower(flag), " \t") {
case "uitverkocht":
e.Flags.Soldout = true
case "tryout":
e.Flags.Tryout = true
case "preview":
e.Flags.Preview = true
case "premiere":
e.Flags.Premiere = true
case "solo":
e.Flags.Solo = true
case "xl":
e.Flags.XL = true
}
}
}
func getEvents() *[]Event {
var events []Event
f, err := os.Open(path.Join(ROOT_DIR, "data/events.csv"))
if err != nil {
log.Fatal(err)
}
defer f.Close()
reader := csv.NewReader(bufio.NewReader(f))
reader.Comma = ';'
now := time.Now()
for {
line, error := reader.Read()
if error == io.EOF {
break
} else if error != nil {
log.Fatal(error)
}
if line[0] == "date" {
continue
}
if now.After(
(time.Time(ParseDate(line[0]))).Local().Add(time.Hour * time.Duration(24)),
) {
continue
}
event := Event{
Date: ParseDate(line[0]),
Time: line[1],
City: line[2],
Title: line[3],
Venue: line[4],
Link: line[5],
}
event.SetFlags(line[6])
events = append(events, event)
}
sort.SliceStable(events, func(i, j int) bool {
a := events[i]
b := events[j]
return time.Time(a.Date).Before(time.Time(b.Date))
})
return &events
}

17
go.mod Normal file
View File

@ -0,0 +1,17 @@
module gitea.basklooosterman.nl/thomasshop
go 1.13
require (
github.com/VictorAvelar/mollie-api-go v1.6.6 // indirect
github.com/VictorAvelar/mollie-api-go/v3 v3.1.2 // indirect
github.com/gin-contrib/multitemplate v0.0.0-20220323084503-710510e67c20
github.com/gin-gonic/gin v1.7.7
github.com/satori/go.uuid v1.2.0
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/yaml.v2 v2.4.0
gorm.io/driver/sqlite v1.3.1
gorm.io/gorm v1.23.4
)

469
go.sum Normal file
View File

@ -0,0 +1,469 @@
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/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/VictorAvelar/mollie-api-go v1.6.6 h1:VWP+k0PRt6esWO8qbCMO/ZvIRDrhBonkt0kxYEnGugk=
github.com/VictorAvelar/mollie-api-go v1.6.6/go.mod h1:ndE8MbwgUZoq827yh0ppyYncii5jZLa0acL6j5iqZyk=
github.com/VictorAvelar/mollie-api-go/v3 v3.1.2 h1:uDtk4yMcehQflUkugqds9HQ6bBjV7a6Wjy9DMhWV9R0=
github.com/VictorAvelar/mollie-api-go/v3 v3.1.2/go.mod h1:/twx+AOJSjclpc9yaUHFrCxET9ad6y0nlH0HOMOT38Q=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/gin-contrib/multitemplate v0.0.0-20211002122701-e9e3201b87a0 h1:5Ot5t4hgu/lhNzwbJhxEq3gsoHUf/C7yX4P1jZcJWO0=
github.com/gin-contrib/multitemplate v0.0.0-20211002122701-e9e3201b87a0/go.mod h1:V2h3mKlTX44jWIvsJ6KDOq808yu3dws0qosCjP9Q7nY=
github.com/gin-contrib/multitemplate v0.0.0-20220323084503-710510e67c20 h1:R8oLt08so7CFl7DHJDfNgWSgpf6iFCbjmUdJiepbbNU=
github.com/gin-contrib/multitemplate v0.0.0-20220323084503-710510e67c20/go.mod h1:V2h3mKlTX44jWIvsJ6KDOq808yu3dws0qosCjP9Q7nY=
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.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/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/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
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.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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/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/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
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/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
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/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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
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/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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-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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c h1:dk0ukUIHmGHqASjP0iue2261isepFCC6XRCSd1nHgDw=
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c/go.mod h1:iQL9McJNjoIa5mjH6nYTCTZXUN6RP+XW3eib7Ya3XcI=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225143145-3bcbab3f74ef/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
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/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
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/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
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/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
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-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
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.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk=
gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg=
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.4 h1:8aPcyEJhY0MAt8aY6Dc524Pn+pO29K+ydu+e/cXSpQM=
gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.4 h1:1BKWM67O6CflSLcwGQR7ccfmC4ebOxQrTfOQGRE9wjg=
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
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=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

68
gulpfile.js Normal file
View File

@ -0,0 +1,68 @@
const gulp = require('gulp');
// const sass = require('gulp-sass');
const sass = require('gulp-sass')(require('sass'));
const webpack = require('webpack');
const webpackStream = require('webpack-stream');
const config = require('./webpack.config');
const cleanCSS = require('gulp-clean-css');
const autoprefixer = require('gulp-autoprefixer');
const newer = require('gulp-newer');
const fontsSrc = './src/fonts'
const fontsDest = './dist/fonts'
const imgSrc = './src/img'
const imgDest = './dist/img'
const downloadSrc = './src/download'
const downloadDest = './dist/download'
const sassTask = () => {
return gulp.src('./src/sass/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(autoprefixer({
browsers: ['last 2 versions', 'ie > 8'],
cascade: false
}))
.pipe(cleanCSS({compatibility: 'ie8'}))
.pipe(gulp.dest('./dist/css'));
}
const jsTask =() => {
return gulp.src('./src/js/shared.js')
.pipe(webpackStream(config, webpack))
.pipe(gulp.dest('./dist/js/'));
}
const fontsTask = () => {
return gulp.src(`./${fontsSrc}/**`)
.pipe(newer(fontsDest))
.pipe(gulp.dest(fontsDest));
}
const imgTask = () => {
return gulp.src(`./${imgSrc}/**`)
// .pipe(newer(imgDest))
.pipe(gulp.dest(imgDest));
}
const downloadTask = () => {
return gulp.src(`./${downloadSrc}/**`)
// .pipe(newer(downloadDest))
.pipe(gulp.dest(downloadDest));
}
const watch = () => {
gulp.watch('./src/sass/**/*.scss', sassTask);
gulp.watch('./src/fonts/**', fontsTask);
gulp.watch('./src/img/**', imgTask);
}
const build = gulp.parallel(jsTask, gulp.series(sassTask, fontsTask, imgTask, downloadTask));
module.exports = {
default: gulp.parallel(jsTask, gulp.series(sassTask, fontsTask, imgTask, watch)),
build
}

7
init.sh Executable file
View File

@ -0,0 +1,7 @@
#! /bin/sh
echo "Enter fqdn:"
read host_name
sed "s#{{root_dir}}#$1#g;s#{{host_name}}#$2#g" nginx/template.conf > nginx/flip_cohen.conf

213
main.go Normal file
View File

@ -0,0 +1,213 @@
package main
import (
"fmt"
"html/template"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"os/signal"
"path"
"time"
"github.com/VictorAvelar/mollie-api-go/v3/mollie"
"github.com/gin-contrib/multitemplate"
"github.com/gin-gonic/gin"
yaml "gopkg.in/yaml.v2"
"gorm.io/gorm"
)
// var SOCKNAME = "thomas.sock"
var addr = "0.0.0.0:3000"
type Config struct {
MollieToken string `yaml:"mollie_token"`
MollieTesting bool `yaml:"mollie_testing"`
MollieWebhook string `yaml:"mollie_webhook"`
MollieRedirectURL string `yaml:"mollie_redirect_url"`
MailServer string `yaml:"mail_server"`
MailPort int `yaml:"mail_port"`
MailUser string `yaml:"mail_user"`
MailPass string `yaml:"mail_pass"`
FromAddr string `yaml:"from_addr"`
BCC string `yaml:"bcc"`
}
var config Config
var mollieClient *mollie.Client
var ROOT_DIR string
func notFound(w http.ResponseWriter, r *http.Request) error {
w.WriteHeader(http.StatusNotFound)
t, err := template.ParseFiles(path.Join(ROOT_DIR, "templates/404.html"))
if err != nil {
return err
}
return t.Execute(w, nil)
}
func index(c *gin.Context) {
db := getDB(c)
products := []Product{}
db.Order("id desc").Find(&products)
c.HTML(http.StatusOK, "index", map[string]interface{}{
"products": products,
})
}
func handle404(c *gin.Context) {
c.HTML(http.StatusOK, "404.html", map[string]interface{}{})
}
func createSocket(s string) net.Listener {
os.Remove(s)
log.Println("Creating UNIX socket:", s)
sock, err := net.Listen("unix", s)
if err != nil {
panic(err)
}
err = os.Chmod(s, 0777)
if err != nil {
panic(err)
}
return sock
}
func getDB(c *gin.Context) *gorm.DB {
item, _ := c.Get("db")
db := item.(*gorm.DB)
return db
}
func getProd(id uint, products []Product) *Product {
for _, prod := range products {
if prod.ID == id {
return &prod
}
}
return nil
}
func createMyRender(base string) multitemplate.Renderer {
r := multitemplate.NewRenderer()
f := template.FuncMap{
"formatMoney": func(cents int) string {
return fmt.Sprintf("€%.2f", float64(float64(cents)/100))
},
"mult": func(x, y int) int {
return x * y
},
"plus": func(a, b int) int {
return a + b
},
"total": func(a Product, ac int, b Product, bc int, c Product, cc int) int {
return a.Price*ac + b.Price*bc + c.Price*cc
},
"nowYear": func() string {
return time.Now().Format("2006")
},
"getProd": getProd,
"countries": func() map[string]string {
return countries
},
"menu": func() []MenuItem {
return getMenuItems()
},
}
r.AddFromFilesFuncs("404.html", f, path.Join(base, "base.html"), path.Join(base, "404.html"))
r.AddFromFilesFuncs("index", f, path.Join(base, "base.html"), path.Join(base, "index.html"))
r.AddFromFilesFuncs("orderInfo", f, path.Join(base, "base.html"), path.Join(base, "order_info.html"))
r.AddFromFilesFuncs("orderPending", f, path.Join(base, "base.html"), path.Join(base, "order_pending.html"))
r.AddFromFilesFuncs("orderThankyou", f, path.Join(base, "base.html"), path.Join(base, "order_thankyou.html"))
r.AddFromFilesFuncs("checkout", f, path.Join(base, "base.html"), path.Join(base, "order_checkout.html"))
return r
}
func main() {
if len(os.Args) > 1 {
ROOT_DIR = os.Args[1]
}
go func() {
getMenuItems()
}()
log.Printf("Running from with app root dir: %s\n", ROOT_DIR)
configPath := path.Join(ROOT_DIR, "data/config/config.yaml")
configBytes, err := ioutil.ReadFile(configPath)
if err != nil {
panic(fmt.Sprintf("Can't open config file at %s; %v", configPath, err))
}
if err := yaml.Unmarshal([]byte(configBytes), &config); err != nil {
panic(fmt.Sprintf("Can't parse config file at %s; %v", configPath, err))
}
if mollieClient, err = initMollie(); err != nil {
panic(err)
}
db := GetDB()
r := gin.Default()
r.HTMLRender = createMyRender(path.Join(ROOT_DIR, "templates"))
r.Use(func(c *gin.Context) {
c.Set("db", db)
c.Next()
})
r.GET("/", index)
r.POST("/order", postGetOrderInfo)
r.GET("/order", index)
r.POST("/order/checkout", postCheckout)
r.POST("/order/create-order", createOrder)
r.POST("/order/webhook", postMollieWebhook)
r.GET("/order/thankyou", getPreOrderThankYou)
r.POST("/order/retry/:orderid", postOrderRetry)
r.GET("/order/pending/:orderid", getPendingOrder)
r.NoRoute(handle404)
// sockPath := path.Join(ROOT_DIR, "run", SOCKNAME)
// sock := createSocket(sockPath)
server := http.Server{
Handler: r,
Addr: addr,
}
fmt.Println("Running...")
go func() {
log.Fatal(server.ListenAndServe())
}()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
// Block until a signal is received.
<-c
fmt.Println("Stopping...")
// os.Remove(path.Join(ROOT_DIR, "run", SOCKNAME))
}

50
menu.go Normal file
View File

@ -0,0 +1,50 @@
package main
import (
"encoding/json"
"log"
"net/http"
)
var menuURL = "http://www.thomaspol.com/wp-json/wp/v2/menu"
type MenuItem struct {
Title string `json:"title"`
URL string `json:"url"`
}
func _getMenuItems() func() []MenuItem {
var items []MenuItem
return func() []MenuItem {
if items == nil {
res, err := http.DefaultClient.Get(menuURL)
if err != nil {
log.Printf("[Err] Error getting menu: %v", err)
return items
}
dec := json.NewDecoder(res.Body)
tmpItems := []MenuItem{}
if err := dec.Decode(&tmpItems); err != nil {
return items
}
items_ := []MenuItem{}
for _, item := range tmpItems {
if item.URL == "#" {
continue
}
items_ = append(items_, item)
}
items = items_
}
return items
}
}
var getMenuItems = _getMenuItems()

39
mollie.go Normal file
View File

@ -0,0 +1,39 @@
package main
import (
"context"
"fmt"
"os"
"github.com/VictorAvelar/mollie-api-go/v3/mollie"
)
func createMolliePayment(preOrder PreOrder, shippingCost int) (*mollie.Response, *mollie.Payment, error) {
amount := preOrder.CalcTotal() + shippingCost
return mollieClient.Payments.Create(context.Background(), mollie.Payment{
Amount: &mollie.Amount{
Currency: "EUR",
Value: fmt.Sprintf("%.2f", float64(amount)/float64(100)),
},
Description: fmt.Sprintf("Thomas Pol Shop"),
Metadata: map[string]interface{}{
"preorderID": preOrder.ID,
},
RedirectURL: fmt.Sprintf("%s/%v", config.MollieRedirectURL, preOrder.ID),
WebhookURL: config.MollieWebhook,
}, nil)
}
func initMollie() (*mollie.Client, error) {
os.Setenv("MOLLIE_API_TOKEN", config.MollieToken)
apiTokenClient, err := mollie.NewClient(nil, mollie.NewConfig(
false,
mollie.APITokenEnv,
))
if err != nil {
return nil, err
}
return apiTokenClient, nil
}

37
nginx/alex-go.conf Normal file
View File

@ -0,0 +1,37 @@
server {
listen 80;
listen [::]:80;
server_name ;
client_max_body_size 20M;
#charset koi8-r;
# access_log /usr/local/var/log/nginx/.access.log;
# error_log /usr/local/var/log/nginx/.error.log;
location /static {
alias /var/www/alex-go/dist;
gzip_static on;
expires max;
add_header Cache-Control public;
}
location /favicon {
return 404 "Not Found";
}
location /robots.txt {
return 404 "Not Found";
}
location / {
proxy_pass http://unix://var/www/alex-go/run/flip.sock;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 5m;
}
}

37
nginx/template.conf Normal file
View File

@ -0,0 +1,37 @@
server {
listen 80;
listen [::]:80;
server_name {{host_name}};
client_max_body_size 20M;
#charset koi8-r;
# access_log /usr/local/var/log/nginx/{{host_name}}.access.log;
# error_log /usr/local/var/log/nginx/{{host_name}}.error.log;
location /static {
alias {{root_dir}}/dist;
gzip_static on;
expires max;
add_header Cache-Control public;
}
location /favicon {
return 404 "Not Found";
}
location /robots.txt {
return 404 "Not Found";
}
location / {
proxy_pass http://unix:/{{root_dir}}/run/flip.sock;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 5m;
}
}

667
order.go Normal file
View File

@ -0,0 +1,667 @@
package main
import (
"bytes"
"context"
"fmt"
"html/template"
"log"
"net/http"
"path"
"regexp"
"strconv"
"github.com/gin-gonic/gin"
"gopkg.in/gomail.v2"
)
const SHIPPING = 675
const SHIPPING_INT = 930
var countries = map[string]string{
"AF": "Afghanistan",
"AX": "Åland Islands",
"AL": "Albania",
"DZ": "Algeria",
"AS": "American Samoa",
"AD": "AndorrA",
"AO": "Angola",
"AI": "Anguilla",
"AQ": "Antarctica",
"AG": "Antigua and Barbuda",
"AR": "Argentina",
"AM": "Armenia",
"AW": "Aruba",
"AU": "Australia",
"AT": "Austria",
"AZ": "Azerbaijan",
"BS": "Bahamas",
"BH": "Bahrain",
"BD": "Bangladesh",
"BB": "Barbados",
"BY": "Belarus",
"BE": "Belgium",
"BZ": "Belize",
"BJ": "Benin",
"BM": "Bermuda",
"BT": "Bhutan",
"BO": "Bolivia",
"BA": "Bosnia and Herzegovina",
"BW": "Botswana",
"BV": "Bouvet Island",
"BR": "Brazil",
"IO": "British Indian Ocean Territory",
"BN": "Brunei Darussalam",
"BG": "Bulgaria",
"BF": "Burkina Faso",
"BI": "Burundi",
"KH": "Cambodia",
"CM": "Cameroon",
"CA": "Canada",
"CV": "Cape Verde",
"KY": "Cayman Islands",
"CF": "Central African Republic",
"TD": "Chad",
"CL": "Chile",
"CN": "China",
"CX": "Christmas Island",
"CC": "Cocos (Keeling) Islands",
"CO": "Colombia",
"KM": "Comoros",
"CG": "Congo",
"CD": "Congo, The Democratic Republic of the",
"CK": "Cook Islands",
"CR": "Costa Rica",
"CI": "Cote D'Ivoire",
"HR": "Croatia",
"CU": "Cuba",
"CY": "Cyprus",
"CZ": "Czech Republic",
"DK": "Denmark",
"DJ": "Djibouti",
"DM": "Dominica",
"DO": "Dominican Republic",
"EC": "Ecuador",
"EG": "Egypt",
"SV": "El Salvador",
"GQ": "Equatorial Guinea",
"ER": "Eritrea",
"EE": "Estonia",
"ET": "Ethiopia",
"FK": "Falkland Islands (Malvinas",
"FO": "Faroe Islands",
"FJ": "Fiji",
"FI": "Finland",
"FR": "France",
"GF": "French Guiana",
"PF": "French Polynesia",
"TF": "French Southern Territories",
"GA": "Gabon",
"GM": "Gambia",
"GE": "Georgia",
"DE": "Germany",
"GH": "Ghana",
"GI": "Gibraltar",
"GR": "Greece",
"GL": "Greenland",
"GD": "Grenada",
"GP": "Guadeloupe",
"GU": "Guam",
"GT": "Guatemala",
"GG": "Guernsey",
"GN": "Guinea",
"GW": "Guinea-Bissau",
"GY": "Guyana",
"HT": "Haiti",
"HM": "Heard Island and Mcdonald Islands",
"VA": "Holy See (Vatican City State",
"HN": "Honduras",
"HK": "Hong Kong",
"HU": "Hungary",
"IS": "Iceland",
"IN": "India",
"ID": "Indonesia",
"IR": "Iran, Islamic Republic Of",
"IQ": "Iraq",
"IE": "Ireland",
"IM": "Isle of Man",
"IL": "Israel",
"IT": "Italy",
"JM": "Jamaica",
"JP": "Japan",
"JE": "Jersey",
"JO": "Jordan",
"KZ": "Kazakhstan",
"KE": "Kenya",
"KI": "Kiribati",
"KP": "Korea, Democratic People'S Republic of",
"KR": "Korea, Republic of",
"KW": "Kuwait",
"KG": "Kyrgyzstan",
"LA": "Lao People'S Democratic Republic",
"LV": "Latvia",
"LB": "Lebanon",
"LS": "Lesotho",
"LR": "Liberia",
"LY": "Libyan Arab Jamahiriya",
"LI": "Liechtenstein",
"LT": "Lithuania",
"LU": "Luxembourg",
"MO": "Macao",
"MK": "Macedonia, The Former Yugoslav Republic of",
"MG": "Madagascar",
"MW": "Malawi",
"MY": "Malaysia",
"MV": "Maldives",
"ML": "Mali",
"MT": "Malta",
"MH": "Marshall Islands",
"MQ": "Martinique",
"MR": "Mauritania",
"MU": "Mauritius",
"YT": "Mayotte",
"MX": "Mexico",
"FM": "Micronesia, Federated States of",
"MD": "Moldova, Republic of",
"MC": "Monaco",
"MN": "Mongolia",
"MS": "Montserrat",
"MA": "Morocco",
"MZ": "Mozambique",
"MM": "Myanmar",
"NA": "Namibia",
"NR": "Nauru",
"NP": "Nepal",
"NL": "Netherlands",
"AN": "Netherlands Antilles",
"NC": "New Caledonia",
"NZ": "New Zealand",
"NI": "Nicaragua",
"NE": "Niger",
"NG": "Nigeria",
"NU": "Niue",
"NF": "Norfolk Island",
"MP": "Northern Mariana Islands",
"NO": "Norway",
"OM": "Oman",
"PK": "Pakistan",
"PW": "Palau",
"PS": "Palestinian Territory, Occupied",
"PA": "Panama",
"PG": "Papua New Guinea",
"PY": "Paraguay",
"PE": "Peru",
"PH": "Philippines",
"PN": "Pitcairn",
"PL": "Poland",
"PT": "Portugal",
"PR": "Puerto Rico",
"QA": "Qatar",
"RE": "Reunion",
"RO": "Romania",
"RU": "Russian Federation",
"RW": "RWANDA",
"SH": "Saint Helena",
"KN": "Saint Kitts and Nevis",
"LC": "Saint Lucia",
"PM": "Saint Pierre and Miquelon",
"VC": "Saint Vincent and the Grenadines",
"WS": "Samoa",
"SM": "San Marino",
"ST": "Sao Tome and Principe",
"SA": "Saudi Arabia",
"SN": "Senegal",
"CS": "Serbia and Montenegro",
"SC": "Seychelles",
"SL": "Sierra Leone",
"SG": "Singapore",
"SK": "Slovakia",
"SI": "Slovenia",
"SB": "Solomon Islands",
"SO": "Somalia",
"ZA": "South Africa",
"GS": "South Georgia and the South Sandwich Islands",
"ES": "Spain",
"LK": "Sri Lanka",
"SD": "Sudan",
"SR": "Suriname",
"SJ": "Svalbard and Jan Mayen",
"SZ": "Swaziland",
"SE": "Sweden",
"CH": "Switzerland",
"SY": "Syrian Arab Republic",
"TW": "Taiwan, Province of China",
"TJ": "Tajikistan",
"TZ": "Tanzania, United Republic of",
"TH": "Thailand",
"TL": "Timor-Leste",
"TG": "Togo",
"TK": "Tokelau",
"TO": "Tonga",
"TT": "Trinidad and Tobago",
"TN": "Tunisia",
"TR": "Turkey",
"TM": "Turkmenistan",
"TC": "Turks and Caicos Islands",
"TV": "Tuvalu",
"UG": "Uganda",
"UA": "Ukraine",
"AE": "United Arab Emirates",
"GB": "United Kingdom",
"US": "United States",
"UM": "United States Minor Outlying Islands",
"UY": "Uruguay",
"UZ": "Uzbekistan",
"VU": "Vanuatu",
"VE": "Venezuela",
"VN": "Viet Nam",
"VG": "Virgin Islands, British",
"VI": "Virgin Islands, U.S",
"WF": "Wallis and Futuna",
"EH": "Western Sahara",
"YE": "Yemen",
"ZM": "Zambia",
"ZW": "Zimbabwe",
}
type orderPayload struct {
Products []int `form:"product"`
Name string `form:"name"`
Streetname string `form:"streetname"`
Postcode string `form:"postcode"`
EmailAddress string `form:"email_address"`
City string `form:"city"`
}
func sendMail(preOrder PreOrder, config Config) {
tpl, err := template.ParseFiles(path.Join(ROOT_DIR, "templates/preorder.email"))
if err != nil {
panic(err)
}
m := gomail.NewMessage()
var w bytes.Buffer
data := map[string]interface{}{
"preOrder": preOrder,
}
tpl.Execute(&w, data)
m.SetHeader("From", config.FromAddr)
m.SetHeader("To", preOrder.Email)
m.SetAddressHeader("Bcc", config.BCC, "admin")
m.SetHeader("Subject", "Your Thomas Pol Shop order")
m.SetBody("text/html", w.String())
d := gomail.NewDialer(config.MailServer, config.MailPort, config.MailUser, config.MailPass)
// Send the email to Bob, Cora and Dan.
if err := d.DialAndSend(m); err != nil {
panic(err)
}
sendAdminMail(preOrder, config)
}
func sendAdminMail(preOrder PreOrder, config Config) {
tpl, err := template.New("tpl").Funcs(template.FuncMap{
"countries": func() map[string]string {
return countries
},
}).ParseFiles(path.Join(ROOT_DIR, "templates/preorder_admin.email"))
if err != nil {
panic(err)
}
m := gomail.NewMessage()
var w bytes.Buffer
data := map[string]interface{}{
"preOrder": preOrder,
}
tpl.Execute(&w, data)
m.SetHeader("From", config.FromAddr)
m.SetHeader("To", config.BCC)
m.SetAddressHeader("Bcc", config.BCC, "admin")
m.SetHeader("Subject", "Thomas Pol order")
m.SetBody("text/html", w.String())
d := gomail.NewDialer(config.MailServer, config.MailPort, config.MailUser, config.MailPass)
// Send the email to Bob, Cora and Dan.
if err := d.DialAndSend(m); err != nil {
panic(err)
}
}
var prodRegexp = regexp.MustCompile(`products\[(\d+)\]`)
func parseProducts(c *gin.Context) map[uint]int {
prods := map[uint]int{}
c.Request.ParseForm()
for k, v := range c.Request.Form {
if m := prodRegexp.FindAllStringSubmatch(k, 1); len(m) > 0 {
id, _ := strconv.ParseInt(m[0][1], 10, 64)
n, _ := strconv.ParseInt(v[0], 10, 64)
prods[uint(id)] = int(n)
}
}
return prods
}
type Shipping struct {
Name string
Email string
Address string
Postcode string
City string
Country string
}
func (s Shipping) Valid() bool {
if s.Name == "" {
return false
}
if s.Email == "" {
return false
}
if s.Address == "" {
return false
}
if s.Postcode == "" {
return false
}
if s.City == "" {
return false
}
if s.Country == "" {
return false
}
return true
}
func parseShipping(c *gin.Context) Shipping {
shipping := Shipping{}
c.Request.ParseForm()
for k, v := range c.Request.Form {
switch k {
case "name":
shipping.Name = v[0]
case "email":
shipping.Email = v[0]
case "address":
shipping.Address = v[0]
case "postcode":
shipping.Postcode = v[0]
case "city":
shipping.City = v[0]
case "country":
shipping.Country = v[0]
}
}
if shipping.Country == "" {
shipping.Country = "NL"
}
return shipping
}
func postGetOrderInfo(c *gin.Context) {
db := getDB(c)
products := []Product{}
db.Find(&products)
c.Request.ParseForm()
prods := parseProducts(c)
shipping := parseShipping(c)
total := 0
for p, q := range prods {
cur := getProd(p, products)
if cur == nil {
continue
}
total += q * cur.Price
}
c.HTML(http.StatusOK, "checkout", map[string]interface{}{
"error": "",
"total": total,
"products": products,
"shipping": shipping,
"order": prods,
})
}
func postCheckout(c *gin.Context) {
db := getDB(c)
products := []Product{}
db.Find(&products)
c.Request.ParseForm()
prods := parseProducts(c)
total := 0
for p, q := range prods {
cur := getProd(p, products)
if cur == nil {
continue
}
total += q * cur.Price
}
shipping := parseShipping(c)
if !shipping.Valid() {
c.HTML(http.StatusOK, "checkout", map[string]interface{}{
"error": "All fields are required",
"total": total,
"products": products,
"shipping": shipping,
"order": prods,
})
return
}
shippingCost := SHIPPING
shippingLabel := "Shipping NL"
if shipping.Country != "NL" {
shippingCost = SHIPPING_INT
shippingLabel = "Shipping international"
}
total += shippingCost
c.HTML(http.StatusOK, "orderInfo", map[string]interface{}{
"total": total,
"products": products,
"shipping": shipping,
"shippingCost": shippingCost,
"shippingLabel": shippingLabel,
"order": prods,
})
}
func createOrder(c *gin.Context) {
db := getDB(c)
products := []Product{}
db.Find(&products)
c.Request.ParseForm()
prods := parseProducts(c)
shipping := parseShipping(c)
preOrder := PreOrder{
Name: shipping.Name,
Email: shipping.Email,
Address: shipping.Address,
Postcode: shipping.Postcode,
City: shipping.City,
Country: shipping.Country,
Status: PRE_ORDER_STATE_CREATED,
}
db.Create(&preOrder)
for p, q := range prods {
for i := 0; i < q; i++ {
db.Create(&Item{
ProductID: p,
PreOrderID: preOrder.ID,
})
}
}
shippingCost := SHIPPING
if shipping.Country != "NL" {
shippingCost = SHIPPING_INT
}
db.Preload("Items").Preload("Items.Product").Where("id = ?", preOrder.ID).Find(&preOrder)
_, payment, err := createMolliePayment(preOrder, shippingCost)
if err != nil {
log.Printf("Something went wrong creating payment: %v", err)
c.AbortWithError(500, fmt.Errorf("I'am sorry, something went wrong...:("))
return
}
molliePayment := MolliePayment{
MolliePaymentID: payment.ID,
Amount: preOrder.CalcTotal() + shippingCost,
Status: payment.Status,
PreOrderID: preOrder.ID,
}
db.Create(&molliePayment)
c.Redirect(http.StatusFound, payment.Links.Checkout.Href)
}
func postMollieWebhook(c *gin.Context) {
db := getDB(c)
paymentID := c.Request.FormValue("id")
_, payment, err := mollieClient.Payments.Get(context.Background(), paymentID, nil)
if err != nil {
log.Printf("Error get payment from mollie; %v; %s", err, paymentID)
c.AbortWithError(500, fmt.Errorf("Deze bestelling is onbekend"))
return
}
molliePayment := &MolliePayment{}
if err := db.Preload("PreOrder").Where("mollie_payment_id = ?", paymentID).Find(&molliePayment).Error; err != nil {
log.Printf("Error get payment from db; %v; %s", err, paymentID)
c.AbortWithError(500, fmt.Errorf("Unknown order"))
return
}
if payment.Status != molliePayment.Status {
molliePayment.Status = payment.Status
db.Save(&molliePayment)
preOrder := PreOrder{}
db.Preload("Items").Preload("Items.Product").Where("id = ?", molliePayment.PreOrderID).Find(&preOrder)
if molliePayment.Status == "paid" {
sendMail(preOrder, config)
}
}
c.Status(200)
}
func postOrderRetry(c *gin.Context) {
orderID := c.Param("orderid")
db := getDB(c)
preOrder := &PreOrder{}
if err := db.Preload("Items").Preload("Items.Product").Where("id = ?", orderID).Find(&preOrder).Error; err != nil {
c.AbortWithError(500, fmt.Errorf("This order is unknown"))
return
}
if preOrder.ID.String() == nullUUID.String() {
c.AbortWithError(500, fmt.Errorf("Unknown Order"))
return
}
db.Find(&preOrder)
shippingCost := SHIPPING
if preOrder.Country != "NL" {
shippingCost = SHIPPING_INT
}
_, payment, err := createMolliePayment(*preOrder, shippingCost)
if err != nil {
c.AbortWithError(500, fmt.Errorf("Sorry, something went wrong...:("))
return
}
molliePayment := MolliePayment{
MolliePaymentID: payment.ID,
Amount: preOrder.CalcTotal() + SHIPPING,
Status: payment.Status,
PreOrderID: preOrder.ID,
}
db.Create(&molliePayment)
c.Redirect(http.StatusFound, payment.Links.Checkout.Href)
}
func getPendingOrder(c *gin.Context) {
orderID := c.Param("orderid")
db := getDB(c)
payments := []*MolliePayment{}
if err := db.Where("pre_order_id = ?", orderID).Order("created_at desc").Find(&payments).Error; err != nil {
c.AbortWithError(500, fmt.Errorf("Unknown Order"))
return
}
if len(payments) == 0 {
c.AbortWithError(500, fmt.Errorf("Unknown Order"))
return
}
payment := payments[0]
if payment.Status == "paid" {
c.Redirect(http.StatusFound, "/order/thankyou")
return
}
c.HTML(http.StatusOK, "orderPending", map[string]interface{}{"payment": payment, "orderID": orderID})
}
func getPreOrderThankYou(c *gin.Context) {
c.HTML(http.StatusOK, "orderThankyou", nil)
}

28
package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "cohen_site",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-es2015": "^6.24.1",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^6.1.0",
"gulp-clean-css": "^4.2.0",
"gulp-newer": "^1.4.0",
"gulp-sass": "^5.1.0",
"webpack": "^4.1.1",
"webpack-stream": "^4.0.2"
},
"dependencies": {
"sass": "^1.50.1",
"uglifyjs-webpack-plugin": "^2.2.0"
}
}

30
preorder_test.go Normal file
View File

@ -0,0 +1,30 @@
package main
import (
"fmt"
"io/ioutil"
"path"
"testing"
yaml "gopkg.in/yaml.v2"
)
func TestSendMail(t *testing.T) {
db := GetDB()
config := Config{}
configPath := path.Join(ROOT_DIR, "data/config/config.yaml")
configBytes, err := ioutil.ReadFile(configPath)
if err != nil {
panic(fmt.Sprintf("Can't open config file at %s; %v", configPath, err))
}
if err := yaml.Unmarshal([]byte(configBytes), &config); err != nil {
panic(fmt.Sprintf("Can't parse config file at %s; %v", configPath, err))
}
preOrder := PreOrder{}
db.Preload("Items").Preload("Items.Product").Where("id = ?", "66297049-6019-45a3-bb98-959d4c2476d9").Find(&preOrder)
sendMail(preOrder, config)
}

0
run/.keep Normal file
View File

3
seed.sql Normal file
View File

@ -0,0 +1,3 @@
INSERT INTO "products" VALUES(1,'2021-12-11 09:53:59.584728+01:00','2021-12-11 09:53:59.584728+01:00',NULL,'In America (CD)','/static/img/tpia_front.png','In America (CD)', 1000);
INSERT INTO "products" VALUES(2,'2021-12-11 09:53:59.584728+01:00','2021-12-11 09:53:59.584728+01:00',NULL,'In America (CD) + Blue Soil (LP)','/static/img/combi.png','In America (CD) + Blue Soil (LP)', 3000);
INSERT INTO "products" VALUES(3,'2021-12-11 09:53:59.584728+01:00','2021-12-11 09:53:59.584728+01:00',NULL,'Blue Soil (LP)','/static/img/blue_soil.jpg','Blue Soil (LP)', 2500);

BIN
src/fonts/socicon.woff Normal file

Binary file not shown.

BIN
src/img/blue_soil.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
src/img/caret.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
src/img/combi.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
src/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

BIN
src/img/favicon.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
src/img/tpia_front.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

21
src/js/shared.js Normal file
View File

@ -0,0 +1,21 @@
!function () {
var t = document.querySelector('.js-mobile-trigger')
var menu = document.querySelector('.js-menu')
var close = document.querySelector('.js-close')
function foo() {
menu.classList.remove('active')
window.removeEventListener('click', foo)
}
t.addEventListener('click', function (e) {
e.preventDefault()
e.stopPropagation()
menu.classList.add('active')
window.addEventListener('click', foo)
})
close.addEventListener('click', function (e) {
e.preventDefault()
e.stopPropagation()
foo()
})
}()

3
src/sass/_color.scss Normal file
View File

@ -0,0 +1,3 @@
$black: #111;
$primary:#00f596;
$grey: #BFB7B0;

7
src/sass/_hamburger.scss Normal file

File diff suppressed because one or more lines are too long

663
src/sass/_menu.scss Normal file
View File

@ -0,0 +1,663 @@
@font-face {
font-family: socicon;
src: url(../fonts/socicon.woff) format("woff");
font-weight: 400;
font-style: normal
}
[data-icon]:before {
content: attr(data-icon)
}
.socicon-modelmayhem:before {
content: "\e000"
}
.socicon-mixcloud:before {
content: "\e001"
}
.socicon-drupal:before {
content: "\e002"
}
.socicon-swarm:before {
content: "\e003"
}
.socicon-istock:before {
content: "\e004"
}
.socicon-yammer:before {
content: "\e005"
}
.socicon-ello:before {
content: "\e006"
}
.socicon-stackoverflow:before {
content: "\e007"
}
.socicon-persona:before {
content: "\e008"
}
.socicon-triplej:before {
content: "\e009"
}
.socicon-houzz:before {
content: "\e00a"
}
.socicon-rss:before {
content: "\e00b"
}
.socicon-paypal:before {
content: "\e00c"
}
.socicon-odnoklassniki:before {
content: "\e00d"
}
.socicon-airbnb:before {
content: "\e00e"
}
.socicon-periscope:before {
content: "\e00f"
}
.socicon-outlook:before {
content: "\e010"
}
.socicon-coderwall:before {
content: "\e011"
}
.socicon-tripadvisor:before {
content: "\e012"
}
.socicon-appnet:before {
content: "\e013"
}
.socicon-goodreads:before {
content: "\e014"
}
.socicon-tripit:before {
content: "\e015"
}
.socicon-lanyrd:before {
content: "\e016"
}
.socicon-slideshare:before {
content: "\e017"
}
.socicon-buffer:before {
content: "\e018"
}
.socicon-disqus:before {
content: "\e019"
}
.socicon-vkontakte:before {
content: "\e01a"
}
.socicon-whatsapp:before {
content: "\e01b"
}
.socicon-patreon:before {
content: "\e01c"
}
.socicon-storehouse:before {
content: "\e01d"
}
.socicon-pocket:before {
content: "\e01e"
}
.socicon-mail:before {
content: "\e01f"
}
.socicon-blogger:before {
content: "\e020"
}
.socicon-technorati:before {
content: "\e021"
}
.socicon-reddit:before {
content: "\e022"
}
.socicon-dribbble:before {
content: "\e023"
}
.socicon-stumbleupon:before {
content: "\e024"
}
.socicon-digg:before {
content: "\e025"
}
.socicon-envato:before {
content: "\e026"
}
.socicon-behance:before {
content: "\e027"
}
.socicon-delicious:before {
content: "\e028"
}
.socicon-deviantart:before {
content: "\e029"
}
.socicon-forrst:before {
content: "\e02a"
}
.socicon-play:before {
content: "\e02b"
}
.socicon-zerply:before {
content: "\e02c"
}
.socicon-wikipedia:before {
content: "\e02d"
}
.socicon-apple:before {
content: "\e02e"
}
.socicon-flattr:before {
content: "\e02f"
}
.socicon-github:before {
content: "\e030"
}
.socicon-renren:before {
content: "\e031"
}
.socicon-friendfeed:before {
content: "\e032"
}
.socicon-newsvine:before {
content: "\e033"
}
.socicon-identica:before {
content: "\e034"
}
.socicon-bebo:before {
content: "\e035"
}
.socicon-zynga:before {
content: "\e036"
}
.socicon-steam:before {
content: "\e037"
}
.socicon-xbox:before {
content: "\e038"
}
.socicon-windows:before {
content: "\e039"
}
.socicon-qq:before {
content: "\e03a"
}
.socicon-douban:before {
content: "\e03b"
}
.socicon-meetup:before {
content: "\e03c"
}
.socicon-playstation:before {
content: "\e03d"
}
.socicon-android:before {
content: "\e03e"
}
.socicon-snapchat:before {
content: "\e03f"
}
.socicon-twitter:before {
content: "\e040"
}
.socicon-facebook:before {
content: "\e041"
}
.socicon-googleplus:before {
content: "\e042"
}
.socicon-pinterest:before {
content: "\e043"
}
.socicon-foursquare:before {
content: "\e044"
}
.socicon-yahoo:before {
content: "\e045"
}
.socicon-skype:before {
content: "\e046"
}
.socicon-yelp:before {
content: "\e047"
}
.socicon-feedburner:before {
content: "\e048"
}
.socicon-linkedin:before {
content: "\e049"
}
.socicon-viadeo:before {
content: "\e04a"
}
.socicon-xing:before {
content: "\e04b"
}
.socicon-myspace:before {
content: "\e04c"
}
.socicon-soundcloud:before {
content: "\e04d"
}
.socicon-spotify:before {
content: "\e04e"
}
.socicon-grooveshark:before {
content: "\e04f"
}
.socicon-lastfm:before {
content: "\e050"
}
.socicon-youtube:before {
content: "\e051"
}
.socicon-vimeo:before {
content: "\e052"
}
.socicon-dailymotion:before {
content: "\e053"
}
.socicon-vine:before {
content: "\e054"
}
.socicon-flickr:before {
content: "\e055"
}
.socicon-500px:before {
content: "\e056"
}
.socicon-instagram:before {
content: "\e057"
}
.socicon-wordpress:before {
content: "\e058"
}
.socicon-tumblr:before {
content: "\e059"
}
.socicon-twitch:before {
content: "\e05a"
}
.socicon-8tracks:before {
content: "\e05b"
}
.socicon-amazon:before {
content: "\e05c"
}
.socicon-icq:before {
content: "\e05d"
}
.socicon-smugmug:before {
content: "\e05e"
}
.socicon-ravelry:before {
content: "\e05f"
}
.socicon-weibo:before {
content: "\e060"
}
.socicon-baidu:before {
content: "\e061"
}
.socicon-angellist:before {
content: "\e062"
}
.socicon-ebay:before {
content: "\e063"
}
.socicon-imdb:before {
content: "\e064"
}
.socicon-stayfriends:before {
content: "\e065"
}
.socicon-residentadvisor:before {
content: "\e066"
}
.socicon-google:before {
content: "\e067"
}
.socicon-yandex:before {
content: "\e068"
}
.socicon-sharethis:before {
content: "\e069"
}
.socicon-bandcamp:before {
content: "\e06a"
}
.socicon-itunes:before {
content: "\e06b"
}
.socicon-deezer:before {
content: "\e06c"
}
[class*=" socicon-"]:before,
[class^=socicon-]:before,
[data-icon]:before {
font-family: socicon !important;
font-style: normal !important;
font-weight: 400 !important;
font-variant: normal !important;
text-transform: none !important;
speak: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale
}
.c-main-nav {
position: fixed;
top: 0;
left: 0;
background-color: rgba(1, 1, 1, .95);
color: #fdfdfd;
width: 100%;
z-index: 9999;
-moz-box-shadow: 2px 2px 9px transparent;
-webkit-box-shadow: 2px 2px 9px transparent;
box-shadow: 2px 2px 9px transparent;
-moz-transform: translate(0, -100%);
-ms-transform: translate(0, -100%);
-webkit-transform: translate(0, -100%);
transform: translate(0, -100%);
-moz-transition: -moz-transform 1s;
-o-transition: -o-transform 1s;
-webkit-transition: -webkit-transform 1s;
transition: all 1s ease-out;
opacity: 0;
}
.c-main-nav.active {
-moz-transform: translate(0, 0);
-ms-transform: translate(0, 0);
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
-moz-box-shadow: 2px 2px 9px rgba(0, 0, 0, .4);
-webkit-box-shadow: 2px 2px 9px rgba(0, 0, 0, .4);
box-shadow: 2px 2px 9px rgba(0, 0, 0, .4);
opacity: 1;
}
.c-main-nav__list {
list-style-type: none;
font-size: 1.5rem;
padding: 0;
margin: 0
}
.c-main-nav__item {
text-align: center;
font-weight: 300;
padding: 0
}
.c-main-nav__link {
color: #fdfdfd;
display: block;
padding: 12px 0;
margin: 0;
width: 100%;
height: 100%;
text-decoration: none;
}
.c-main-nav__link:hover {
background-color: #00f596;
color: #111
}
.c-main-nav__item:last-of-type .c-main-nav__link {
color: #00f596
}
.c-main-nav__item:last-of-type .c-main-nav__link:hover {
background-color: transparent;
color: #00f596
}
@media (max-height:575px) {
.c-main-nav__list {
font-size: 1.5rem
}
.c-main-nav__link {
padding: 9px 0
}
}
.c-mobile-trigger {
position: fixed;
top: 15px;
right: 15px;
padding: 5px 10px;
z-index: 1000;
box-shadow: 2px 2px 10px rgba(0,0,0,0.2);
background-color: rgba(0,0,0,1);
display: block;
&__link {
color: white!important;
font-weight: 400;
text-decoration: none;
font-weight: 400;
}
}
.page-header {
position: fixed;
background-color: #111;
padding: 10px 30px;
width: 100%;
color: #fdfdfd;
z-index: 9998
}
.page-header .subtitle {
font-size: 1.2rem;
letter-spacing: 1px;
margin-left: 7px;
color: #00f596
}
@media (max-width:650px) {
.page-header .subtitle {
display: none
}
}
.page-header a,
.page-header a:active,
.page-header a:visited {
color: #fdfdfd;
text-decoration: none;
}
.page-header a.home-link,
.page-header a:active.home-link,
.page-header a:visited.home-link {
font-family: mohave;
font-size: 2rem;
letter-spacing: 3px;
text-transform: uppercase;
font-weight: 300;
}
.page-header .open-menu-wrapper {
float: right;
display: inline-block;
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%)
}
.page-header .open-menu {
font-size: 1.3rem;
line-height: 0;
height: 1.5rem;
padding: 2px 3px
}
.page-header ul.social-container {
float: right;
display: inline-block;
list-style: none;
font-size: 1.2rem;
padding: 0;
margin: 0;
position: absolute;
right: 115px;
top: 51%;
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
-webkit-transform: translateY(-50%);
transform: translateY(-50%)
}
.page-header ul.social-container li {
display: inline-block
}
.page-header ul.social-container a {
display: inline-block;
width: 25px;
position: relative
}
.page-header ul.social-container a span {
position: relative
}
.bg-hr,
.bg-stretch {
position: absolute;
width: 100%
}
.page-header ul.social-container a .socicon-facebook {
font-size: .95rem;
top: -2px;
left: 6px;
}
@media (max-width:740px) {
.page-header ul.social-container {
display: none
}
}

4
src/sass/_mixins.scss Normal file
View File

@ -0,0 +1,4 @@
@mixin font-size($sizeValue) {
font-size: $sizeValue + px;
font-size: ($sizeValue / 16) + rem;
}

289
src/sass/_order.scss Normal file
View File

@ -0,0 +1,289 @@
.c-products {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-column-gap: 25px;
grid-row-gap: 25px;
@media (max-width: 900px) {
grid-template-columns: repeat(1, 1fr);
}
}
.c-product {
&__name {
font-weight: 500;
margin-bottom: 10px;
}
&__order {
display: flex;
justify-content: start;
}
&__input {
border: 0;
border-bottom: 1px solid #111;
font-size: 20px;
font-weight: 500;
font-family: 'Roboto';
width: 50px;
margin-left: 5px;
&:focus {
border-color: $primary;
}
}
&__img {
max-width: 100%;
margin-bottom: 5px;
}
&__price {
margin-right: 5px;
}
}
form {
width: 100%;
}
.c-order-info {
margin: 25px 0;
}
.c-form-row {
display: block;
width: 100%;
margin-bottom: 25px;
}
.c-error {
@extend .t-p1;
color: white;
padding: 10px;
background-color: red;
font-weight: 400;
display: block;
}
.c-label {
font-weight: 400;
display: block;
margin-bottom: 10px;
}
.c-input {
$this: &;
border: 0;
outline: 0;
padding-bottom: 5px;
border-bottom: 1px solid rgba(0,0,0,0.2);
font-family: 'Roboto', sans-serif;
font-size: 20px;
width: 100%;
font-weight: 200;
&:focus {
border-bottom: 1px solid rgba(0,0,0,0.8);
}
transition: border-color 200ms ease-out;
&:read-only {
margin-left: -2px;
border-bottom: 0;
&:focus {
border-bottom: 0;
}
}
}
.c-select {
position: relative;
&__select {
$this: &;
border: 0;
outline: 0;
padding-bottom: 5px;
border: 1px solid rgba(0,0,0,0.2);
font-family: 'Roboto', sans-serif;
font-size: 20px;
width: 100%;
font-weight: 200;
padding: 10px;
// border-bottom: 1px solid rgba(0,0,0,0.8);
appearance: none;
transition: border-color 200ms ease-out;
position: relative;
}
&:after {
content: "";
display: block;
position: absolute;
width: 14px;
height: 14px;
top: 15px;
right: 12px;
background-image: url(/static/img/caret.png);
background-size: cover;
}
// &:read-only {
// margin-left: -2px;
// border-bottom: 0;
// &:focus {
// border-bottom: 0;
// }
// }
}
.o-input-select-margin input {
margin-top: 14px;
}
.c-grid {
display: grid;
grid-template-columns: 50% 50%;
grid-column-gap: 50px;
@media (max-width: 900px) {
div:last-of-type {
margin-top: 25px;
}
grid-template-columns: 100%;
}
}
.c-col {
// width: 50%;
}
.c-product-container {
display: flex;
@media (max-width: 700px) {
display: block;
}
}
.c-button-container {
margin: 40px 0;
}
.c-button {
$this: &;
padding: 15px 45px;
background-color: black;
color: white!important;
text-decoration: none;
border: 0;
outline: 0;
font-weight: 400;
cursor: pointer;
font-family: 'Roboto', sans-serif;
font-size: 16px;
text-transform: uppercase;
transition: all 300ms ease-out;
&:hover {
background-color: $primary;
}
@media (max-width: 700px) {
width: 100%;
display: block;
text-align: center;
}
}
// .c-product {
// $this: &;
// padding: 25px 0;
// margin-right: 50px;
// &:last-of-type {
// margin-right: 0;
// }
// &__img {
// display: block;
// width: 250px;
// margin: 15px 0;
// }
// &__x {
// height: 31px;
// display: inline-block;
// }
// &__title {
// display: inline-block;
// text-transform: uppercase;
// font-weight: 400;
// }
// &__price {
// display: block;
// }
// &__input {
// padding: 5px;
// border: 1px solid rgba(0,0,0,0.2)!important;
// width: 43px;
// &:focus {
// border: 1px solid rgba(0,0,0,0.8)!important;
// }
// transition: border-color 200ms ease-out;
// }
// &__order-info {
// display: flex;
// align-items: center;
// span {
// margin-right: 10px;
// };
// }
// @media (max-width: 700px) {
// width: 100%;
// margin-right: 0;
// #{$this}__img {
// width: 100%;
// }
// }
// }
.c-price-order {
}
.c-price-table {
width: 100%;
td {
padding: 0px 0;
}
&__right {
text-align: right;
}
&__top {
td {
padding-top: 15px;
}
}
&__underline {
border-bottom: 1px solid black;
td {
padding-bottom: 15px;
}
}
}

52
src/sass/_reset.scss Normal file
View File

@ -0,0 +1,52 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
html * {
box-sizing: border-box;
}

35
src/sass/_typography.scss Normal file
View File

@ -0,0 +1,35 @@
.t-h1 {
font-family: mohave;
display: block;
font-size: 5rem;
margin: 0 0 15px 0;
font-weight: 400;
text-transform: uppercase;
}
p, .t-p1 {
display: block;
margin-block-start: 1rem;
margin-block-end: 1rem;
margin-inline-start: 0px;
margin-inline-end: 0px;
}
.t-h2 {
font-family: mohave;
display: block;
font-size: 1.6rem;
margin: 0 0 15px 0;
font-weight: 400;
text-transform: uppercase;
}
.t-h3 {
display: block;
font-size: 1.1rem;
margin-block-start: 0.67rem;
margin-block-end: 0.67rem;
margin-inline-start: 0px;
margin-inline-end: 0px;
font-weight: bold;
}

99
src/sass/index.scss Normal file
View File

@ -0,0 +1,99 @@
@import "reset";
@import "mixins";
@import "color";
@import "typography";
@import "order";
@import "menu";
body {
/* text-transform: uppercase; */
font-family: 'Roboto', sans-serif;
font-size: 20px;
padding: 0;
margin: 0;
line-height: 1.4;
font-weight: 100;
color: $black;
}
main {
min-height: calc(100vh - 58px);
padding-top: 75px;
}
// table {
// height: 100vh;
// width: 100%;
// }
.c-link, .c-link:hover, .c-link:visited {
color: black;
font-weight: 300;
}
.main {
background-image: url('/static/img/alex.jpg');
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
min-height: 100vh;
}
.c-section {
padding: 50px;
}
@media (max-width: 1024px) {
.c-section {
padding: 25px 15px;
}
}
.c-section__content {
max-width: 960px;
margin: auto;
}
ul {list-style: none; padding: 0;}
li {
line-height: 1.4;
}
.c-auto-ratio__inner {
padding-top: 56.25%;
position: relative;
}
.c-auto-ratio__inner iframe {
position: absolute;
top:0;
left: 0;
width: 100%;
height: 100%;
}
.c-footer {
background-color: black;
padding: 15px;
}
.c-footer ul {
margin: auto;
text-align: center;
}
.c-footer__item {
color: white;
font-size: 12px;
display: inline-block;
margin-right: 15px;
}
.c-footer__item a {
color: white!important;
text-decoration: none;
}

11
templates/404.html Normal file
View File

@ -0,0 +1,11 @@
{{define "body"}}
<main>
<div class="c-page-section">
<div class="c-page-section__content" style="text-align: center;">
<h1 class="t-h1" style="font-size: 200px; font-weight: bold; margin-top: 100px;">404</h1>
<p class="t-p1">The page you trying to find is no longer there.</p>
<p class="t-p1"><a href="/" class="t-p1 t-p1--invert">Back to shop</a></p>
</div>
</div>
</main>
{{end}}

73
templates/base.html Normal file

File diff suppressed because one or more lines are too long

32
templates/index.html Normal file
View File

@ -0,0 +1,32 @@
{{define "body"}}
<main>
<div class="c-section">
<div class="c-section__content">
<h1 class="t-h1">
Shop
</h1>
<p class="t-p1">
Welcome to the T.Pol shop! My solo album 'Blue Soil' is fresh out on vinyl and it sounds great. My debut album 'In America' is on sale and discounted when you order the Vinyl! Enjoy my music and thank you very much.
</p>
<form method="POST" action="/order">
<div class="c-products">
{{range .products}}
<div class="c-product">
<h1 class="c-product__name">{{.Name}}</h1>
<img class="c-product__img" src="{{.Image}}"/>
<div class="c-product__order">
<span class="c-product__price">{{formatMoney .Price}}</span>
<span>&times;</span>
<input name="products[{{.ID}}]"class="c-product__input" type="number" value="0">
</div>
</div>
{{end}}
</div>
<div class="c-button-container">
<button class="c-button">Place order</button>
</div>
</form>
</div>
</div>
</main>
{{end}}

View File

@ -0,0 +1,76 @@
{{define "body"}}
<main>
<div class="c-section" style="margin-top: 25px;">
<div class="c-section__content">
<form action="/order/checkout" method="POST">
{{range $k, $v := .order}}
<input name="products[{{$k}}]" type="hidden" value="{{$v}}">
{{end}}
<h2 class="t-h2">Order summary</h2>
<div class="c-order-info">
<div class="c-form-row">
<table class="c-price-table">
{{range $k, $v := .order}}
{{if gt $v 0}}
{{$prod := getProd $k $.products}}
<tr>
<td style="font-weight: 400;">{{$prod.Name}} &times; {{$v}}</td>
<td class="c-price-table__right">{{formatMoney (mult $prod.Price $v)}}</td>
</tr>
{{end}}
{{end}}
</table>
</div>
</div>
{{if ne .error ""}}
<div class="c-error">
{{.error}}
</div>
{{end}}
<div style="margin-top: 50px">
<h2 class="t-h2">Shipping info</h2>
<div class="c-grid c-form-row">
<div>
<label for="name" class="c-label">Name</label>
<input name="name" id="name" class="c-input" type="text" value="{{.shipping.Name}}"/>
</div>
<div>
<label for="email" class="c-label">Email address</label>
<input name="email" id="email" class="c-input" type="text" value="{{.shipping.Email}}"/>
</div>
</div>
<div class="c-grid c-form-row">
<div>
<label for="address" class="c-label">Streetname + number</label>
<input name="address" id="address" class="c-input" type="text" value="{{.shipping.Address}}"/>
</div>
<div>
<label for="postcode" class="c-label">Postcode</label>
<input name="postcode" id="postcode" class="c-input" type="text" value="{{.shipping.Postcode}}"/>
</div>
</div>
<div class="c-grid c-form-row">
<div class="o-input-select-margin">
<label for="city" class="c-label">City</label>
<input name="city" id="city" class="c-input" value="{{.shipping.City}}"/>
</div>
<div>
<label for="country" class="c-label">Country</label>
<div class="c-select">
<select class="c-select__select" name="country" value="{{.shipping.Country}}" id="country">
{{range $key, $description := countries}}
<option value="{{$key}}"{{if eq $.shipping.Country $key}}selected{{end}}>{{$description}}</option>
{{end}}
</select>
</div>
</div>
</div>
</div>
<button class="c-button c-button">Next</button>
</form>
</div>
</div>
</main>
{{end}}

View File

@ -0,0 +1,52 @@
{{define "body"}}
<main>
<div class="c-section" style="margin-top: 25px;">
<div class="c-section__content">
<h1>Store</h1>
{{if ne .error ""}}
<p class="c-error">{{.error}}</p>
{{end}}
<form action="/order/checkout" method="POST">
<p>At the moment we only ship within the Netherlands. If you live outside the Netherlands and would like to receive the album please send an email to <a class="c-link" href="mailto:info@alexandervanpopta.com">info@alexandervanpopta.nl</a>.</p>
<ul class="c-product-container">
{{range .products}}
<li class="c-product">
<span class="c-product__title">{{.Name}}</span>
<img class="c-product__img" src="{{.Image}}" alt="">
<div class="c-product__order-info">
<span class="c-product__price">{{formatMoney .Price}}</span> <span class="c-product__x">&times;</span>
<input class="c-input c-product__input" name="product[{{.ID}}]" type="number" value="{{if eq .ID 1}}{{$.order.MTMLP}}{{else if eq .ID 2}}{{$.order.MTMCD}}{{else}}{{$.order.AllTheMemCD}}{{end}}">
</div>
</li>
{{end}}
</ul>
<h2 class="t-h2">Order info</h2>
<div class="c-order-info">
<div class="c-form-row">
<label for="name" class="c-label">Name</label>
<input name="name" id="name" class="c-input" type="text" value="{{.order.Name}}"/>
</div>
<div class="c-form-row">
<label for="email_address" class="c-label">Email address</label>
<input name="email_address" id="email_address" class="c-input" type="text" value="{{.order.EmailAddress}}"/>
</div>
<div class="c-form-row">
<label for="streetname" class="c-label">Streetname + number</label>
<input name="streetname" id="streetname" class="c-input" type="text" value="{{.order.Streetname}}"/>
</div>
<div class="c-form-row">
<label for="postcode" class="c-label">Postcode</label>
<input name="postcode" id="postcode" class="c-input" type="text" value="{{.order.Postcode}}"/>
</div>
<div class="c-form-row">
<label for="city" class="c-label">City</label>
<input name="city" id="city" class="c-input" value="{{.order.City}}"/>
</div>
</div>
<button class="c-button">Order</button>
</form>
</div>
</div>
</main>
{{end}}

58
templates/order_info.html Normal file
View File

@ -0,0 +1,58 @@
{{define "body"}}
<main>
<div class="c-section" style="margin-top: 25px;">
<div class="c-section__content">
<form action="/order/create-order" method="POST">
{{range $k, $v := .order}}
<input name="products[{{$k}}]" type="hidden" value="{{$v}}">
{{end}}
<h2 class="t-h2">Order summary</h2>
<div class="c-order-info">
<div class="c-form-row">
<table class="c-price-table">
{{range $k, $v := .order}}
{{if gt $v 0}}
{{$prod := getProd $k $.products}}
<tr>
<td style="font-weight: 400;">{{$prod.Name}} &times; {{$v}}</td>
<td class="c-price-table__right">{{formatMoney (mult $prod.Price $v)}}</td>
</tr>
{{end}}
{{end}}
<tr class="c-price-table__underline">
<td style="font-weight: 400;">{{.shippingLabel}}</td>
<td class="c-price-table__right">{{formatMoney .shippingCost}}</td>
</tr>
<tr class="c-price-table__top">
<td style="font-weight: 400;">Total</td>
<td class="c-price-table__right">{{formatMoney .total}}</td>
</tr>
</table>
</div>
</div>
<div style="margin-top: 50px">
<h2 class="t-h2">Shipping info</h2>
<label for="" class="c-label">Name/Email</label>
<div class="c-form-row">
<input type="hidden" name="name" id="name" readonly class="c-input" value="{{.shipping.Name}}"/>
<input type="hidden" name="email" readonly class="c-input" value="{{.shipping.Email}}"/>
<input readonly class="c-input" value="{{.shipping.Name}}"/>
<input readonly class="c-input" value="{{.shipping.Email}}"/>
</div>
<label for="" class="c-label">Address</label>
<div class="c-form-row">
<input type="hidden" name="address" readonly class="c-input" value="{{.shipping.Address}}"/>
<input type="hidden" name="postcode" readonly class="c-input" value="{{.shipping.Postcode}}"/>
<input type="hidden" name="city" readonly class="c-input" value="{{.shipping.City}}"/>
<input type="hidden" name="country" readonly class="c-input" value="{{.shipping.Country}}"/>
<input readonly class="c-input" value="{{.shipping.Address}}"/>
<input readonly class="c-input" value="{{.shipping.Postcode}} {{.shipping.City}}, {{index countries .shipping.Country}}"/>
</div>
<button class="c-button c-button">Pay</button>
</form>
</div>
</div>
</main>
{{end}}

View File

@ -0,0 +1,36 @@
{{define "body"}}
<main>
<div class="c-section" style="margin-top: 25px;">
<div class="c-section__content">
{{if eq .payment.Status "open"}}
<p class="t-p1">Processing payment <span class="js-loading" style="display: inline-block; width: 15px; text-align: left"></span></p>
{{else}}
<form action="/order/retry/{{.orderID}}" method="POST">
<p class="t-p1">Your payment failed</p>
<button style="margin-top: 50px"class="c-button">try again</button>
</form>
{{end}}
</div>
</div>
</main>
<script>
{{if eq .payment.Status "open"}}
var loading = document.querySelector('.js-loading')
setInterval((function () {
var times = 0
return function () {
times++
var dots = ''
for (i = 0; i < times % 4; i++) {
dots += '.'
}
loading.textContent = dots
}
})(), 200)
setTimeout(function () {window.location.reload()}, 2000)
{{end}}
</script>
{{end}}

View File

@ -0,0 +1,16 @@
{{define "body"}}
<main>
<div class="c-section" style="margin-top: 25px;">
<div class="c-section__content">
<p class="t-p1">
Thank you for ordering my music and I hope you enjoy it!
</p>
<p class="t-p1">
Yours truly,<br>
Thomas Pol
</p>
<p style="margin-top: 50px"><a href="/" style="margin-top: 10px" class="c-button">HOME</a></p>
</div>
</div>
</main>
{{end}}

8
templates/preorder.email Normal file
View File

@ -0,0 +1,8 @@
<p>Dear {{.preOrder.Name}},</p>
<p>&nbsp;</p>
<p>Thank you for your order! I will ship your order as soon as possible.</p>
<p>&nbsp;</p>
<p>For questions regarding your order you can contact me at <a href="mailto:info@thomaspol.com">info@thomaspol.com</a></p>
<p>&nbsp;</p>
<p>Yours truly,</p>
<p>Thomas Pol</p>

View File

@ -0,0 +1,31 @@
{{define "tpl"}}
<table>
<tr>
<th style="text-align: left; padding: 5px;">Naam</th>
<th style="text-align: left; padding: 5px;">Email</th>
<th style="text-align: left; padding: 5px;">Straatnaam + nr</th>
<th style="text-align: left; padding: 5px;">Postcode</th>
<th style="text-align: left; padding: 5px;">Stad</th>
<th style="text-align: left; padding: 5px;">Land</th>
</tr>
<tr>
<td style="padding: 5px;">{{.preOrder.Name}}</td>
<td style="padding: 5px;">{{.preOrder.Email}}</td>
<td style="padding: 5px;">{{.preOrder.Address}}</td>
<td style="padding: 5px;">{{.preOrder.Postcode}}</td>
<td style="padding: 5px;">{{.preOrder.City}}</td>
<td style="padding: 5px;">{{index countries .preOrder.Country}}</td>
</tr>
</table>
<table>
<tr>
<th style="text-align: left; padding: 5px;">Producten</th>
</tr>
{{range .preOrder.Items}}
<tr>
<td style="padding: 5px;">{{.Product.Name}}</td>
</tr>
{{end}}
</table>
{{end}}

48
templates/song.html Normal file
View File

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="/static/css/index.css?v4">
<link rel="icon" type="image/png" href="/static/img/favicon.png" />
</head>
<body>
<nav class="nav">
<div class="nav__section nav__left">
<a href="/albums#{{.album.Path}}">Terug</a>
</div>
<div class="nav__section nav__right">
<a href="/">Zingt Leonard Cohen</a>
<a href="/love-it">Love It!</a>
</div>
</nav>
<div class="c-page-section">
<div class="c-page-section__content">
<a class="t-h2" style="text-decoration: none; font-size: 26px; margin-bottom: 25px;" href="/albums#{{.album.Path}}">terug</a>
<div></div>
<h1 class="t-h1" style="margin-bottom: 15px;">
{{.album.Name}} ({{.album.Release}})
</h1>
<div></div>
<h1 class="t-h2">
{{.song.Title}}
</h1>
<div style="margin-bottom: 35px;"></div>
{{range .song.Lyrics}}
<pre class="t-p1" style="margin-bottom: 25px; font-size: 24px;">
{{- . -}}
</pre>
{{end}}
</div>
</div>
<footer class="c-footer">
<span>© {{.nowYear}} Flip Noorman </span>
<span>info@flipnoorman.nl</span>
</footer>
<script src="/static/js/shared.js"></script>
</body>
</html>

50
webpack.config.js Normal file
View File

@ -0,0 +1,50 @@
const webpack = require('webpack');
const path = require('path');
const srcPath = path.join(__dirname, 'src');
const srcJsPath = path.join(srcPath, 'js');
const destPath = path.join(__dirname, 'dist');
const destJsPath = path.join(destPath, 'js');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const env = process.env.NODE_ENV || 'production';
const config = {
watch: env !== 'production',
entry: {
"shared": srcJsPath + '/shared',
},
output: {
path: destJsPath,
filename: '[name].js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
]
},
stats: {
colors: true
},
plugins: []
}
if (env === 'production') {
config.plugins.push(
new UglifyJsPlugin()
)
}
module.exports = config;