├── .gitignore ├── build ├── Dockerfile ├── bin │ └── .gitkeep ├── build.sh ├── public │ ├── .gitkeep │ ├── assets │ │ └── .gitkeep │ └── index.html └── templates │ └── .gitkeep ├── docs └── .gitkeep ├── readme.md ├── src ├── backend │ ├── environment │ │ └── environment.go │ ├── logger │ │ └── logger.go │ ├── main.go │ ├── repository │ │ └── sample.go │ ├── samplehandlers │ │ ├── errorExample.go │ │ ├── helloWorld.go │ │ ├── helloWorld_test.go │ │ ├── sampleHandlers.go │ │ └── sampleHandlers_test.go │ └── vendor │ │ ├── .gitkeep │ │ └── vendor.json ├── docker-compose.yml └── frontend │ ├── .babelrc │ ├── .eslintignore │ ├── .eslintrc │ ├── app │ ├── app.vue │ ├── components │ │ ├── hello.styl │ │ └── hello.vue │ ├── main.js │ ├── routes.js │ ├── store │ │ ├── actions.js │ │ ├── index.js │ │ ├── mutations.js │ │ └── state.js │ └── styles │ │ ├── app.styl │ │ ├── colors.styl │ │ ├── common │ │ └── reset.styl │ │ └── mixins │ │ └── clearfix.styl │ ├── package.json │ ├── postcss.config.js │ ├── webpack.config.js │ └── yarn.lock └── test └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | **/node_modules 3 | src/backend/**/vendor/*/ -------------------------------------------------------------------------------- /build/Dockerfile: -------------------------------------------------------------------------------- 1 | # USE ALPINE LINUX AS BASE IMAGE (TO ALLOW BASH NAVIGATION) 2 | FROM alpine:3.7 3 | 4 | MAINTAINER DKF-Basel 5 | LABEL copyright="Departement Klinische Forschung, Basel, Switzerland. 2018" 6 | 7 | # ADD ROOT CERTIFICATE PROVIDERS FOR SSL CONNECTIONS 8 | RUN apk add --no-cache ca-certificates 9 | 10 | RUN mkdir /app 11 | 12 | # COPY THE REQUIRED FILES INTO THE CONTAINER 13 | ADD bin /app/bin 14 | ADD templates /app/templates 15 | ADD public /app/public 16 | 17 | # ADD VOLUMES FROM THE HOST 18 | # VOLUME [""] 19 | 20 | # ALLOW ACCESS ON PORT 80 21 | EXPOSE 80 22 | 23 | # SET THE CURRENT WORKING DIRECTORY 24 | WORKDIR /app 25 | 26 | # START THE APPLICATION WITH THE CONTAINER 27 | # TODO: add the name of the binary to start 28 | CMD ["/app/bin/[NAME OF THE BINARY]"] 29 | -------------------------------------------------------------------------------- /build/bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkfbasel/scratch/467bc4f14f9fca025be5dcc39f070b56d5354c80/build/bin/.gitkeep -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "-- build backend (y/n):" 4 | read buildBackend 5 | if [ "$buildBackend" = "y" ]; then 6 | cd ../src/backend 7 | GOOS=linux GOARCH=amd64 go build -o "../../build/bin/service" 8 | cd ../../build 9 | fi 10 | 11 | 12 | echo "-- build frontend (y/n):" 13 | read buildFrontend 14 | if [ "$buildFrontend" = "y" ]; then 15 | cd ../src/frontend 16 | docker run --rm -v "$(pwd):/app" -v "$(pwd)/../../build/public:/tmp/public" -e "COMMAND=npm run build" dkfbasel/hot-reload-webpack:6.2.0 17 | cd ../../build 18 | echo "-- build finished" 19 | fi 20 | 21 | echo "-- build docker container (y/n):" 22 | read buildContainer 23 | if [ "$buildContainer" = "y" ]; then 24 | echo "specify container tag (dev):" 25 | read tag 26 | if [ "$tag" = "" ]; then 27 | tag="dev" 28 | fi 29 | docker build -t dkfbasel/testing:$tag --no-cache . 30 | 31 | echo "-- push docker container (y/n)" 32 | read pushContainer 33 | if [ "$pushContainer" = "y" ]; then 34 | docker push dkfbasel/testing:$tag 35 | fi 36 | 37 | fi 38 | -------------------------------------------------------------------------------- /build/public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkfbasel/scratch/467bc4f14f9fca025be5dcc39f070b56d5354c80/build/public/.gitkeep -------------------------------------------------------------------------------- /build/public/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkfbasel/scratch/467bc4f14f9fca025be5dcc39f070b56d5354c80/build/public/assets/.gitkeep -------------------------------------------------------------------------------- /build/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [NAME OF THE APPLICATION] 5 | 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /build/templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkfbasel/scratch/467bc4f14f9fca025be5dcc39f070b56d5354c80/build/templates/.gitkeep -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkfbasel/scratch/467bc4f14f9fca025be5dcc39f070b56d5354c80/docs/.gitkeep -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | README 2 | ------ 3 | This is a template for projects using Go for the backend and Vue.js for the 4 | frontend. 5 | 6 | Directory Structure 7 | ------------------- 8 | ``` 9 | - build // contains all information required to run the project in production 10 | - bin // contains all binaries to run the service 11 | - public // contains all files that should be served by the service 12 | - templates // contains all templates required for the service 13 | 14 | - docs // documentation and asset source files for the project 15 | 16 | - src // contains all development information 17 | - backend // contains the golang code for the web server 18 | - frontend // contains the web application front-end source code 19 | - docker-compose.yml // configuration for development containers 20 | 21 | - test // directory for separate test data and integration tests 22 | 23 | - readme.md // readme file for every project 24 | ``` 25 | 26 | Golang Tools 27 | ------------ 28 | All golang packages should be vendored in the src/backend/vendor folder. 29 | Govendor (github.com/kardianos/govendor) should be used to vendor all go package 30 | dependencies until an official vendoring solution is available. 31 | 32 | Tests should be created in respective test files and should employ the package 33 | [github.com/smartystreets/goconvey](https://github.com/smartystreets/goconvey). 34 | However, goconvey should not be installed in the vendor folder but directly in 35 | your gopath (i.e. with go get). 36 | 37 | Configuration should be stored in a separate config.yaml file. It should also be 38 | possible to overwrite the configuration with environment variables. The package 39 | [github.com/jinzhu/configor](https://github.com/jinzhu/configor) can be used for 40 | this. 41 | 42 | For error handling the package [github.com/pkg/errors](https://github.com/pkg/errors) 43 | should be used. This will allow to add context to errors. 44 | 45 | Please use the package [github.com/uber-go/zap](https://github.com/uber-go/zap) 46 | to create structured logs. Logs should be sent to the standard out, so it is 47 | possible to later collect all logs from different docker services in a central 48 | place. 49 | 50 | The echo framework [github.com/labstack/echo](https://github.com/labstack/echo) 51 | can be used as web framework for services. However, try to use only the standard 52 | library in packages that can be used by external projects. This helps to avoid 53 | problems with dependencies on different versions of external packages. 54 | 55 | A special docker container dkfbasel/hot-reload-go is provided for development. 56 | This container will auto-compile and start the go binary every time a file 57 | in the project directory is changed. 58 | 59 | The package [github.com/eirwin/stubble](https://github.com/eirwin/stubble) can 60 | be used to setup a simple json mock api for testing and initial frontend 61 | development. 62 | 63 | 64 | Vue.js 65 | ------ 66 | The frontend should be developed using Vue.js employing various components that 67 | compose the application. 68 | 69 | Eslint should be used to ensure that the code-style conforms to the team standard. 70 | A respective .eslintrc file is provided in the frontend directory. 71 | 72 | Stylus should be used to write css specifications and should be kept in a 73 | separate .styl file next to the component specification. 74 | 75 | A special docker container dkfbasel/hot-reload-webpack is provided to support 76 | hot-reload development and simplify building the code with webpack. 77 | This container does provide all node_modules that are required to build the 78 | application. Therefore only project specific packages need to be included in 79 | the file package.json (i.e. vue, vuex, vue-router, axios). 80 | 81 | Yarn (https://yarnpkg.com) should be used instead of npm to load additional 82 | node_modules. 83 | 84 | 85 | Version-Control 86 | --------------- 87 | Git should be used for all directories as version control system. The branching 88 | should follow the git-flow model 89 | (http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/). 90 | 91 | All production releases should be integrated in the master branch and be given a 92 | respective tag. Tags should follow semantic versioning (i.e. major.minor.patch). 93 | In addition, a high level description of the changes should be added to every tag. 94 | 95 | 96 | Project Description 97 | ------------------- 98 | .. 99 | 100 | How To Build 101 | ------------ 102 | .. 103 | 104 | How To Run 105 | ---------- 106 | All projects should be packed into docker containers. A basic dockerfile is 107 | already provided in the build directory. Alpine-Linux should be used a base image 108 | to allow inspection of the running processes in the container without having 109 | to use huge images. 110 | 111 | A docker-compose configuration should be provided to run all required containers. 112 | -------------------------------------------------------------------------------- /src/backend/environment/environment.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "bitbucket.org/dkfbasel/scratch/src/backend/repository" 5 | "github.com/kelseyhightower/envconfig" 6 | ) 7 | 8 | // --- Globally available items --- 9 | 10 | // Spec is used for globally necessary items 11 | type Spec struct { 12 | // make the configuration available globally 13 | Config Configuration 14 | 15 | // define a global database 16 | SampleDB repository.SampleDBInterface 17 | } 18 | 19 | // --- Basic application configuration --- 20 | 21 | // Configuration is used to hold basic application configuration 22 | type Configuration struct { 23 | Host string `default:"0.0.0.0:80"` 24 | RequestLog bool `default:"false"` 25 | } 26 | 27 | // LoadConfiguration will load the basic application configuration from the 28 | // specified config file 29 | func LoadConfiguration(prefix string) (Configuration, error) { 30 | config := Configuration{} 31 | err := envconfig.Process(prefix, &config) 32 | return config, err 33 | } 34 | -------------------------------------------------------------------------------- /src/backend/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "flag" 5 | "os" 6 | 7 | "go.uber.org/zap" 8 | ) 9 | 10 | var logger *zap.Logger 11 | 12 | func init() { 13 | 14 | // get the debugging env config 15 | debugMode := os.Getenv("DEBUG") 16 | 17 | // get the logging env config 18 | logLevel := os.Getenv("LOGLEVEL") 19 | 20 | // use the test logging mode if started with go test and no logging mode specified 21 | if logLevel == "" && flag.Lookup("test.v") != nil { 22 | logLevel = "test" 23 | } 24 | 25 | // use the debug logging mode if started in debug mode and no logging mode specified 26 | if logLevel == "" && debugMode == "true" { 27 | logLevel = "debug" 28 | } 29 | 30 | // use different logging output depending on configuration 31 | switch logLevel { 32 | case "debug": 33 | logger, _ = zap.NewDevelopment() 34 | 35 | case "test": 36 | logger = zap.NewNop() 37 | 38 | default: 39 | logger, _ = zap.NewProduction() 40 | } 41 | 42 | } 43 | 44 | // Zap will return an initialized go.uber.org/zap logger 45 | func Zap() *zap.Logger { 46 | return logger 47 | } 48 | -------------------------------------------------------------------------------- /src/backend/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bitbucket.org/dkfbasel/scratch/src/backend/environment" 5 | "bitbucket.org/dkfbasel/scratch/src/backend/logger" 6 | "bitbucket.org/dkfbasel/scratch/src/backend/repository" 7 | "bitbucket.org/dkfbasel/scratch/src/backend/samplehandlers" 8 | 9 | "github.com/labstack/echo" 10 | "github.com/labstack/echo/middleware" 11 | "go.uber.org/zap" 12 | ) 13 | 14 | func main() { 15 | 16 | // initialize the environment 17 | env := environment.Spec{} 18 | 19 | // initialize a default error variable 20 | var err error 21 | 22 | // use a central logging package, this will allow us to use the logger 23 | // in various packages without having to pass a reference 24 | testLogger := logger.Zap().With(zap.String("user", "testuser")) 25 | 26 | testLogger.Info("this is a sample setup") 27 | testLogger.Debug("debug information is not shown in production setting") 28 | 29 | // load the configuration 30 | env.Config, err = environment.LoadConfiguration("scratch") 31 | if err != nil { 32 | logger.Zap().Fatal("configuration could not be loaded", zap.Error(err)) 33 | } 34 | 35 | // initialize a database connnection 36 | env.SampleDB, err = repository.NewSampleDB() 37 | if err != nil { 38 | logger.Zap().Fatal("cound not connect to the database", zap.Error(err)) 39 | } 40 | 41 | // initialize a new router 42 | router := echo.New() 43 | 44 | // add basic logging functionality 45 | if env.Config.RequestLog { 46 | router.Use(middleware.Logger()) 47 | } 48 | 49 | // add security middleware 50 | router.Use(middleware.Secure()) 51 | 52 | // default router 53 | router.GET("/", samplehandlers.HelloWorld) 54 | router.GET("/err", samplehandlers.ErrorExample(env)) 55 | 56 | router.GET("/set/:id/:value", samplehandlers.SetSample(env)) 57 | router.GET("/get/:id", samplehandlers.GetSample(env)) 58 | 59 | logger.Zap().Info("starting server", zap.String("host", env.Config.Host)) 60 | if env.Config.RequestLog { 61 | logger.Zap().Info("request logging activated") 62 | } 63 | 64 | // start the server 65 | err = router.Start(env.Config.Host) 66 | 67 | // log the error if server cannot be started or is terminated unexpectedly 68 | logger.Zap().Fatal("could not start server", zap.Error(err)) 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/backend/repository/sample.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import "sync" 4 | 5 | // SampleDBInterface defines the methods required to handle sample information 6 | type SampleDBInterface interface { 7 | Get(sampleID string) string 8 | Set(sampleID, value string) error 9 | } 10 | 11 | // SampleDB will implement the method to satisfy the sampleDBInterface 12 | type SampleDB struct { 13 | Content map[string]string 14 | sync.RWMutex 15 | } 16 | 17 | // NewSampleDB will return an initialized sample database 18 | func NewSampleDB() (*SampleDB, error) { 19 | // initialize the sample database 20 | sampleDB := SampleDB{} 21 | sampleDB.Content = make(map[string]string) 22 | return &sampleDB, nil 23 | } 24 | 25 | // Get will return the given sample id or an empty string if not present 26 | func (db *SampleDB) Get(sampleID string) string { 27 | db.RLock() 28 | defer db.RUnlock() 29 | return db.Content[sampleID] 30 | } 31 | 32 | // Set will set the given sample in the database 33 | func (db *SampleDB) Set(sampleID, value string) error { 34 | db.Lock() 35 | defer db.Unlock() 36 | db.Content[sampleID] = value 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /src/backend/samplehandlers/errorExample.go: -------------------------------------------------------------------------------- 1 | package samplehandlers 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "bitbucket.org/dkfbasel/scratch/src/backend/environment" 8 | "bitbucket.org/dkfbasel/scratch/src/backend/logger" 9 | "github.com/labstack/echo" 10 | "github.com/pkg/errors" 11 | "go.uber.org/zap" 12 | ) 13 | 14 | // ErrorExample will return an error 15 | func ErrorExample(env environment.Spec) echo.HandlerFunc { 16 | 17 | return func(ctx echo.Context) error { 18 | 19 | value, err := returnAnError("my sample value") 20 | if err != nil { 21 | logger.Zap().Error("errorExample request did not work", zap.Error(err)) 22 | return echo.NewHTTPError(http.StatusInternalServerError, "sorry. something did not work") 23 | } 24 | 25 | return ctx.String(http.StatusOK, value) 26 | 27 | } 28 | } 29 | 30 | // returnAnError is a function that will return an error to illustrate error 31 | // handling 32 | func returnAnError(value string) (string, error) { 33 | // error that would be produced i.e. by a database call that failed 34 | err := fmt.Errorf("This is a new error, i.e. from a database call") 35 | 36 | // wrapped error with additional information 37 | return "", errors.Wrap(err, "I always return an error") 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/backend/samplehandlers/helloWorld.go: -------------------------------------------------------------------------------- 1 | package samplehandlers 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/labstack/echo" 7 | ) 8 | 9 | // HelloWorld will print a hello world message 10 | func HelloWorld(ctx echo.Context) error { 11 | return ctx.String(http.StatusOK, "Greetings Earthlings!") 12 | } 13 | -------------------------------------------------------------------------------- /src/backend/samplehandlers/helloWorld_test.go: -------------------------------------------------------------------------------- 1 | package samplehandlers_test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | "bitbucket.org/dkfbasel/scratch/src/backend/samplehandlers" 9 | 10 | "github.com/labstack/echo" 11 | . "github.com/smartystreets/goconvey/convey" 12 | ) 13 | 14 | func TestHelloWord(t *testing.T) { 15 | 16 | Convey("Given a running server", t, func() { 17 | 18 | Convey("The hello world handler should return a string", func() { 19 | 20 | router := echo.New() 21 | request := new(http.Request) 22 | recorder := httptest.NewRecorder() 23 | ctx := router.NewContext(request, recorder) 24 | 25 | ctx.SetPath("/") 26 | err := samplehandlers.HelloWorld(ctx) 27 | 28 | So(err, ShouldBeNil) 29 | So(recorder.Body.String(), ShouldEqual, "Greetings Earthlings!") 30 | 31 | }) 32 | 33 | }) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/backend/samplehandlers/sampleHandlers.go: -------------------------------------------------------------------------------- 1 | package samplehandlers 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "bitbucket.org/dkfbasel/scratch/src/backend/environment" 8 | "bitbucket.org/dkfbasel/scratch/src/backend/logger" 9 | "github.com/labstack/echo" 10 | "go.uber.org/zap" 11 | ) 12 | 13 | // GetSample will return the sample value to the given id 14 | func GetSample(env environment.Spec) echo.HandlerFunc { 15 | 16 | return func(ctx echo.Context) error { 17 | sampleID := ctx.Param("id") 18 | 19 | if sampleID == "" { 20 | return echo.NewHTTPError(http.StatusBadRequest, "please provide all values") 21 | } 22 | 23 | sampleValue := env.SampleDB.Get(sampleID) 24 | return ctx.String(http.StatusOK, sampleValue) 25 | } 26 | } 27 | 28 | // SetSample will set the value for the given key 29 | func SetSample(env environment.Spec) echo.HandlerFunc { 30 | 31 | return func(ctx echo.Context) error { 32 | 33 | sampleID := ctx.Param("id") 34 | sampleValue := ctx.Param("value") 35 | 36 | if sampleID == "" || sampleValue == "" { 37 | return echo.NewHTTPError(http.StatusBadRequest, "please provide all values") 38 | } 39 | 40 | err := env.SampleDB.Set(sampleID, sampleValue) 41 | if err != nil { 42 | logger.Zap().Error("setSampleRequest failed", zap.Error(err)) 43 | return echo.NewHTTPError(http.StatusBadRequest, "could not set the value") 44 | } 45 | 46 | return ctx.String(http.StatusOK, fmt.Sprintf("%s: %s", sampleID, sampleValue)) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/backend/samplehandlers/sampleHandlers_test.go: -------------------------------------------------------------------------------- 1 | package samplehandlers_test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | "bitbucket.org/dkfbasel/scratch/src/backend/environment" 9 | "bitbucket.org/dkfbasel/scratch/src/backend/repository" 10 | "bitbucket.org/dkfbasel/scratch/src/backend/samplehandlers" 11 | 12 | "github.com/labstack/echo" 13 | . "github.com/smartystreets/goconvey/convey" 14 | ) 15 | 16 | type mockDB struct{} 17 | 18 | // Get .. 19 | func (*mockDB) Get(sampleID string) string { 20 | return "my-sample-value" 21 | } 22 | 23 | // Set .. 24 | func (*mockDB) Set(sampleID, value string) error { 25 | return nil 26 | } 27 | 28 | func TestSampleHandlers(t *testing.T) { 29 | 30 | env := environment.Spec{} 31 | 32 | // env.SampleDB = &mockDB{} 33 | env.SampleDB, _ = repository.NewSampleDB() 34 | env.SampleDB.Set("sampleid", "my-sample-value") // nolint: errcheck 35 | 36 | Convey("Given a running server", t, func() { 37 | 38 | Convey("Get sample should return the specified sample", func() { 39 | 40 | router := echo.New() 41 | request := new(http.Request) 42 | recorder := httptest.NewRecorder() 43 | ctx := router.NewContext(request, recorder) 44 | 45 | ctx.SetParamNames("id") 46 | ctx.SetParamValues("sampleid") 47 | handler := samplehandlers.GetSample(env) 48 | err := handler(ctx) 49 | 50 | So(err, ShouldBeNil) 51 | So(recorder.Body.String(), ShouldEqual, "my-sample-value") 52 | 53 | }) 54 | 55 | Convey("Set sample should set a sample value", func() { 56 | 57 | router := echo.New() 58 | request := new(http.Request) 59 | recorder := httptest.NewRecorder() 60 | 61 | ctx := router.NewContext(request, recorder) 62 | ctx.SetParamNames("id") 63 | ctx.SetParamValues("no-exist") 64 | 65 | getHandler := samplehandlers.GetSample(env) 66 | err := getHandler(ctx) 67 | So(err, ShouldBeNil) 68 | So(recorder.Body.String(), ShouldBeEmpty) 69 | 70 | ctx = router.NewContext(request, recorder) 71 | ctx.SetParamNames("id", "value") 72 | ctx.SetParamValues("no-exist", "now I exist") 73 | setHandler := samplehandlers.SetSample(env) 74 | err = setHandler(ctx) 75 | So(err, ShouldBeNil) 76 | 77 | recorder2 := httptest.NewRecorder() 78 | ctx = router.NewContext(request, recorder2) 79 | ctx.SetParamNames("id") 80 | ctx.SetParamValues("no-exist") 81 | err = getHandler(ctx) 82 | So(err, ShouldBeNil) 83 | So(recorder2.Body.String(), ShouldEqual, "now I exist") 84 | 85 | }) 86 | 87 | }) 88 | } 89 | -------------------------------------------------------------------------------- /src/backend/vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkfbasel/scratch/467bc4f14f9fca025be5dcc39f070b56d5354c80/src/backend/vendor/.gitkeep -------------------------------------------------------------------------------- /src/backend/vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "2Fy1Y6Z3lRRX1891WF/+HT4XS2I=", 7 | "path": "github.com/dgrijalva/jwt-go", 8 | "revision": "2268707a8f0843315e2004ee4f1d021dc08baedf", 9 | "revisionTime": "2017-02-01T22:58:49Z" 10 | }, 11 | { 12 | "checksumSHA1": "gWlw0l2wMBLhmejJCTr/Zywqyk8=", 13 | "path": "github.com/kelseyhightower/envconfig", 14 | "revision": "b6fde1625d631a48340817849a547164f395d9eb", 15 | "revisionTime": "2017-04-24T16:37:37Z" 16 | }, 17 | { 18 | "checksumSHA1": "7rUfphht9/t/HCY41POCMGdPgL8=", 19 | "path": "github.com/labstack/echo", 20 | "revision": "b2430fc4a8c023dd6a07fd3f7be19f3747861dca", 21 | "revisionTime": "2017-03-15T19:11:47Z" 22 | }, 23 | { 24 | "checksumSHA1": "jDe2yacV+Q02mQdBl1TD/NVWJYE=", 25 | "path": "github.com/labstack/echo/middleware", 26 | "revision": "b2430fc4a8c023dd6a07fd3f7be19f3747861dca", 27 | "revisionTime": "2017-03-15T19:11:47Z" 28 | }, 29 | { 30 | "checksumSHA1": "NvfQq6Y6kh9cMw+y1m00ivEd5b4=", 31 | "path": "github.com/labstack/gommon/bytes", 32 | "revision": "9cedb429ffbe71a32a3ae7c65fd109cb7ae07804", 33 | "revisionTime": "2017-03-12T01:28:59Z" 34 | }, 35 | { 36 | "checksumSHA1": "R6DzcBLEP0BONPpsyr+11N7xh5w=", 37 | "path": "github.com/labstack/gommon/color", 38 | "revision": "9cedb429ffbe71a32a3ae7c65fd109cb7ae07804", 39 | "revisionTime": "2017-03-12T01:28:59Z" 40 | }, 41 | { 42 | "checksumSHA1": "uHA6zbtxzJce4r+6uk8zu11qkuk=", 43 | "path": "github.com/labstack/gommon/log", 44 | "revision": "9cedb429ffbe71a32a3ae7c65fd109cb7ae07804", 45 | "revisionTime": "2017-03-12T01:28:59Z" 46 | }, 47 | { 48 | "checksumSHA1": "lW02E/TGpV59u9q49TsK0Cmva0c=", 49 | "path": "github.com/labstack/gommon/random", 50 | "revision": "9cedb429ffbe71a32a3ae7c65fd109cb7ae07804", 51 | "revisionTime": "2017-03-12T01:28:59Z" 52 | }, 53 | { 54 | "checksumSHA1": "BOFetxMdGQVex14gI977F2aI9sM=", 55 | "path": "github.com/mattn/go-colorable", 56 | "revision": "a392f450ea64cee2b268dfaacdc2502b50a22b18", 57 | "revisionTime": "2017-03-12T23:57:56Z" 58 | }, 59 | { 60 | "checksumSHA1": "U6lX43KDDlNOn+Z0Yyww+ZzHfFo=", 61 | "path": "github.com/mattn/go-isatty", 62 | "revision": "57fdcb988a5c543893cc61bce354a6e24ab70022", 63 | "revisionTime": "2017-03-07T16:30:44Z" 64 | }, 65 | { 66 | "checksumSHA1": "ynJSWoF6v+3zMnh9R0QmmG6iGV8=", 67 | "path": "github.com/pkg/errors", 68 | "revision": "ff09b135c25aae272398c51a07235b90a75aa4f0", 69 | "revisionTime": "2017-03-16T20:15:38Z" 70 | }, 71 | { 72 | "checksumSHA1": "LTOa3BADhwvT0wFCknPueQALm8I=", 73 | "path": "github.com/valyala/bytebufferpool", 74 | "revision": "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7", 75 | "revisionTime": "2016-08-17T18:16:52Z" 76 | }, 77 | { 78 | "checksumSHA1": "gtG8jeB0fcTmKZf+6Vbcoa6nfec=", 79 | "path": "github.com/valyala/fasttemplate", 80 | "revision": "dcecefd839c4193db0d35b88ec65b4c12d360ab0", 81 | "revisionTime": "2017-02-24T21:24:29Z" 82 | }, 83 | { 84 | "checksumSHA1": "6NS7FWJl1FobB+Xfe4SzBGD+75g=", 85 | "path": "go.uber.org/atomic", 86 | "revision": "3b8db5e93c4c02efbc313e17b2e796b0914a01fb", 87 | "revisionTime": "2016-12-15T19:56:52Z" 88 | }, 89 | { 90 | "checksumSHA1": "qHEUkiCrUOyvze9NcGOpOqiFtwM=", 91 | "path": "go.uber.org/zap", 92 | "revision": "4257c7cf05477d92ec25c31cfd3d60e89575f18a", 93 | "revisionTime": "2017-03-15T00:02:06Z" 94 | }, 95 | { 96 | "checksumSHA1": "HYo/9nwrY08NQA+2ItPOAH8IFW8=", 97 | "path": "go.uber.org/zap/buffer", 98 | "revision": "4257c7cf05477d92ec25c31cfd3d60e89575f18a", 99 | "revisionTime": "2017-03-15T00:02:06Z" 100 | }, 101 | { 102 | "checksumSHA1": "MuxOAtZEsJitlWBzhmpm2vGiHok=", 103 | "path": "go.uber.org/zap/internal/bufferpool", 104 | "revision": "4257c7cf05477d92ec25c31cfd3d60e89575f18a", 105 | "revisionTime": "2017-03-15T00:02:06Z" 106 | }, 107 | { 108 | "checksumSHA1": "uC0L9eCSAYcCWNC8udJk/t1vvIU=", 109 | "path": "go.uber.org/zap/internal/color", 110 | "revision": "4257c7cf05477d92ec25c31cfd3d60e89575f18a", 111 | "revisionTime": "2017-03-15T00:02:06Z" 112 | }, 113 | { 114 | "checksumSHA1": "b80CJExrVpXu3SA1iCQ6uLqTn2c=", 115 | "path": "go.uber.org/zap/internal/exit", 116 | "revision": "4257c7cf05477d92ec25c31cfd3d60e89575f18a", 117 | "revisionTime": "2017-03-15T00:02:06Z" 118 | }, 119 | { 120 | "checksumSHA1": "mpgG6h5iItTyTOjG1CsPGYb65bM=", 121 | "path": "go.uber.org/zap/internal/multierror", 122 | "revision": "4257c7cf05477d92ec25c31cfd3d60e89575f18a", 123 | "revisionTime": "2017-03-15T00:02:06Z" 124 | }, 125 | { 126 | "checksumSHA1": "eIsrYtjvQgZNI8iqhL0K8AOR7nI=", 127 | "path": "go.uber.org/zap/zapcore", 128 | "revision": "4257c7cf05477d92ec25c31cfd3d60e89575f18a", 129 | "revisionTime": "2017-03-15T00:02:06Z" 130 | }, 131 | { 132 | "checksumSHA1": "D1l731k467E5E4Uau+cQKEYkaVY=", 133 | "path": "golang.org/x/crypto/acme", 134 | "revision": "728b753d0135da6801d45a38e6f43ff55779c5c2", 135 | "revisionTime": "2017-01-24T01:46:54Z" 136 | }, 137 | { 138 | "checksumSHA1": "MG1frAM4p4lg1ioOhUa7rjN3xm0=", 139 | "path": "golang.org/x/crypto/acme/autocert", 140 | "revision": "728b753d0135da6801d45a38e6f43ff55779c5c2", 141 | "revisionTime": "2017-01-24T01:46:54Z" 142 | }, 143 | { 144 | "checksumSHA1": "Y+HGqEkYM15ir+J93MEaHdyFy0c=", 145 | "path": "golang.org/x/net/context", 146 | "revision": "a6577fac2d73be281a500b310739095313165611", 147 | "revisionTime": "2017-03-08T20:54:49Z" 148 | }, 149 | { 150 | "checksumSHA1": "WHc3uByvGaMcnSoI21fhzYgbOgg=", 151 | "path": "golang.org/x/net/context/ctxhttp", 152 | "revision": "a6577fac2d73be281a500b310739095313165611", 153 | "revisionTime": "2017-03-08T20:54:49Z" 154 | }, 155 | { 156 | "checksumSHA1": "/oZpHfYc+ZgOwYAhlvcMhmETYpw=", 157 | "path": "golang.org/x/sys/unix", 158 | "revision": "99f16d856c9836c42d24e7ab64ea72916925fa97", 159 | "revisionTime": "2017-03-08T15:04:45Z" 160 | } 161 | ], 162 | "rootPath": "bitbucket.org/dkfbasel/scratch/src/backend" 163 | } 164 | -------------------------------------------------------------------------------- /src/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | networks: 4 | dkfbasel: 5 | external: true 6 | 7 | services: 8 | 9 | api: 10 | image: dkfbasel/hot-reload-go:1.10.2 11 | networks: [dkfbasel] 12 | ports: 13 | - 3002:80 14 | volumes: 15 | - ./backend:/app 16 | - ../build/templates:/tmp/templates 17 | 18 | environment: 19 | - PROJECT=bitbucket.org/dkfbasel/scratch/src/backend 20 | - CMD=build 21 | - DEBUG=true 22 | - SCRATCH_HOST=0.0.0.0:80 23 | - SCRATCH_REQUESTLOG=true 24 | 25 | test: 26 | image: dkfbasel/hot-reload-go:1.10.2 27 | networks: [dkfbasel] 28 | ports: 29 | - 3004:8080 30 | volumes: 31 | # note that the package must be linked into the gopath directory 32 | # directly for the goconvey watcher to work 33 | - ./backend:/go/src/bitbucket.org/dkfbasel/scratch/src/backend 34 | - ../build/templates:/tmp/templates 35 | 36 | environment: 37 | - PROJECT=bitbucket.org/dkfbasel/scratch/src/backend 38 | - CMD=goconvey 39 | 40 | frontend: 41 | image: dkfbasel/hot-reload-webpack:6.2.0 42 | networks: [dkfbasel] 43 | ports: 44 | - 3000:3000 45 | volumes: 46 | - ./frontend:/app 47 | - ../build/public:/tmp/public 48 | environment: 49 | - COMMAND=npm run dev 50 | -------------------------------------------------------------------------------- /src/frontend/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"], 3 | "plugins": ["transform-runtime"], 4 | "env": { 5 | "production": { 6 | "presets": ["minify"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | build/* 3 | -------------------------------------------------------------------------------- /src/frontend/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaFeatures": { 3 | "arrowFunctions": true, 4 | "modules": true, 5 | "destructuring": true, 6 | "spread": true, 7 | "restParams": true, 8 | "blockBindings": true 9 | }, 10 | "env": { 11 | "browser": true, 12 | "es6": true 13 | }, 14 | "parser": "babel-eslint", 15 | "rules": { 16 | "semi": [2, "always"], 17 | "quotes": [1, "single"], 18 | "indent": [2, "tab", {"SwitchCase": 0}], 19 | "comma-dangle": [2, "never"], 20 | "no-mixed-spaces-and-tabs": 2, 21 | "babel/new-cap": 0, 22 | "new-cap": 0, 23 | "no-shadow": 1, 24 | "eol-last": 1, 25 | "space-before-function-paren": [1, "never"], 26 | "space-before-blocks": 1, 27 | "no-underscore-dangle": 0, 28 | "no-debugger": 0, 29 | "no-console": 0, 30 | "no-use-before-define": 0, 31 | "no-alert": 0, 32 | "no-unused-vars": 0, 33 | "no-trailing-spaces": [2, { "skipBlankLines": true }] 34 | }, 35 | "plugins": ["babel", "html"] 36 | } 37 | -------------------------------------------------------------------------------- /src/frontend/app/app.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /src/frontend/app/components/hello.styl: -------------------------------------------------------------------------------- 1 | p { 2 | border: 1px solid $unibas-red; 3 | background: tint($unibas-red, 80%); 4 | padding: 20px; 5 | color: $unibas-red; 6 | } 7 | -------------------------------------------------------------------------------- /src/frontend/app/components/hello.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 | 31 | -------------------------------------------------------------------------------- /src/frontend/app/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | // enable dev tools if not in production 4 | if (process.env.NODE_ENV === 'production') { 5 | Vue.config.devtools = true; 6 | } 7 | 8 | // --- ROUTING --- 9 | 10 | // use vue-router for navigation 11 | import Router from 'vue-router'; 12 | import routes from './routes.js'; 13 | 14 | // make the router components and methods available to all vue components 15 | Vue.use(Router); 16 | 17 | // initialize a new router 18 | var router = new Router({ 19 | mode: 'history', 20 | routes: routes 21 | }); 22 | 23 | // --- STATE MANAGEMENT --- 24 | 25 | // import a vuex store to handle all state in one location 26 | import store from './store/index'; 27 | 28 | // synchronize the router with vuex 29 | import {sync} from 'vuex-router-sync'; 30 | sync(store, router); 31 | 32 | // import the main app component 33 | import App from './app.vue'; 34 | 35 | // initialize the application 36 | new Vue({ 37 | router, 38 | store, 39 | el: '#app', 40 | render: h => h(App) 41 | }); 42 | -------------------------------------------------------------------------------- /src/frontend/app/routes.js: -------------------------------------------------------------------------------- 1 | // import components for specific routes 2 | import HelloComponent from './components/hello.vue'; 3 | 4 | // define the routing paths 5 | const routes = [ 6 | { 7 | path: '/hello', 8 | name: 'hello', 9 | component: HelloComponent, 10 | alias: ['/'] 11 | } 12 | ]; 13 | 14 | export default routes; 15 | -------------------------------------------------------------------------------- /src/frontend/app/store/actions.js: -------------------------------------------------------------------------------- 1 | // define the actions that can be called from components 2 | const actions = { 3 | 4 | // actions should be camelcased 5 | addUser({commit}, name) { 6 | 7 | // always use object style commits for consistency 8 | commit({ 9 | type: 'ADD_USER', 10 | name: name 11 | }); 12 | 13 | } 14 | 15 | }; 16 | 17 | export default actions; 18 | -------------------------------------------------------------------------------- /src/frontend/app/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | // make vuex methods available to all vue components 5 | Vue.use(Vuex); 6 | 7 | // import the initial state definition 8 | import initialState from './state'; 9 | 10 | // import all mutations that are allowed to change the store 11 | import mutations from './mutations'; 12 | 13 | // import all actions available to the components 14 | import actions from './actions'; 15 | 16 | // initialize a new store 17 | // note: use vuex-modules for specific functionality 18 | const store = new Vuex.Store({ 19 | state: initialState, 20 | mutations: mutations, 21 | actions: actions, 22 | strict: process.env.NODE_ENV !== 'production' 23 | }); 24 | 25 | // add hot-load capabilities for actions, mutations and modules 26 | if (module.hot) { 27 | // accept actions and mutations as hot modules 28 | module.hot.accept(['./mutations', './actions'], () => { 29 | 30 | // require the updated modules 31 | // have to add .default here due to babel 6 module output 32 | const newMutations = require('./mutations').default; 33 | const newActions = require('./actions').default; 34 | 35 | // swap in the new actions and mutations 36 | store.hotUpdate({ 37 | mutations: newMutations, 38 | actions: newActions 39 | }); 40 | }); 41 | } 42 | 43 | export default store; 44 | -------------------------------------------------------------------------------- /src/frontend/app/store/mutations.js: -------------------------------------------------------------------------------- 1 | // define the root store mutations 2 | const mutations = { 3 | 4 | // note: mutation names should be in uppercase letters to be clearly 5 | // distinguished from actions 6 | ADD_USER(state, payload) { 7 | state.user = payload.name; 8 | } 9 | 10 | }; 11 | 12 | export default mutations; 13 | -------------------------------------------------------------------------------- /src/frontend/app/store/state.js: -------------------------------------------------------------------------------- 1 | // define the initial state of the application 2 | const state = { 3 | user: null 4 | }; 5 | 6 | export default state; 7 | -------------------------------------------------------------------------------- /src/frontend/app/styles/app.styl: -------------------------------------------------------------------------------- 1 | // app.css will be included on the top of the page and apply to all views 2 | @require "./common/reset.styl"; 3 | 4 | body { 5 | background: #efefef; 6 | } 7 | -------------------------------------------------------------------------------- /src/frontend/app/styles/colors.styl: -------------------------------------------------------------------------------- 1 | // color definitions for the website 2 | $unibas-mint = rgb(165, 215, 210); 3 | $unibas-anthracite = rgb(45, 55, 60); 4 | $unibas-red = rgb(210, 5, 55); 5 | $unibas-grey = rgb(234, 234, 234); 6 | 7 | $unibas-black = rgb(0, 0, 0); 8 | $unibas-white = rgb(255, 255, 255); 9 | 10 | $unibas-text = rgb(34, 34, 34); 11 | $unibas-placeholder = #9b9b9b; 12 | -------------------------------------------------------------------------------- /src/frontend/app/styles/common/reset.styl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkfbasel/scratch/467bc4f14f9fca025be5dcc39f070b56d5354c80/src/frontend/app/styles/common/reset.styl -------------------------------------------------------------------------------- /src/frontend/app/styles/mixins/clearfix.styl: -------------------------------------------------------------------------------- 1 | // clearfix for float clearing 2 | clearfix() { 3 | &::after { 4 | content: ''; 5 | display: table; 6 | clear: both; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dkfbasel-scratch", 3 | "version": "1.1.0", 4 | "description": "Scratch project template by the Department of Clinical Research Basel", 5 | "author": "Department of Clinical Research, University Hospital Basel", 6 | "scripts": { 7 | "dev": "cross-env NODE_ENV=development webpack-dev-server --inline --hot", 8 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.18.0", 12 | "vue": "^2.5.16", 13 | "vue-router": "^3.0.1", 14 | "vuex": "^3.0.1", 15 | "vuex-router-sync": "^5.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | // configuration for postcss to enable autoprefixer 2 | module.exports = { 3 | plugins: [ 4 | require('autoprefixer')({ 5 | browsers: ['Chrome >= 60', 6 | 'Firefox >= 55', 7 | 'Edge >= 12', 8 | 'Explorer >= 10', 9 | 'Safari >= 10'], 10 | cascade: true, 11 | add: true, 12 | remove: true 13 | }) 14 | ] 15 | }; 16 | -------------------------------------------------------------------------------- /src/frontend/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var poststylus = require('poststylus'); 4 | var autoprefixer = require('autoprefixer'); 5 | 6 | var VueLoaderPlugin = require('vue-loader/lib/plugin'); 7 | var MiniCssExtractPlugin = require('mini-css-extract-plugin'); 8 | 9 | // note: we prefer using includes over excludes, as this will give us finer 10 | // control over what is actually transpiled 11 | var appDirectory = path.resolve(__dirname, 'app'); 12 | var nodeModules = path.resolve(__dirname, 'node_modules'); 13 | var includes = [appDirectory]; 14 | 15 | // note: we prefer using includes over excludes, as this will give us finer 16 | // control over what is actually transpiled 17 | var appDirectory = path.resolve(__dirname, 'app'); 18 | var nodeModules = path.resolve(__dirname, 'node_modules'); 19 | var includes = [appDirectory]; 20 | 21 | // define specific proxy routes 22 | var proxyConfig = { 23 | '/api': { 24 | target: 'http://profiles.graphql', 25 | secure: false 26 | } 27 | }; 28 | 29 | 30 | // basic configuration shared for development and production build 31 | var config = { 32 | entry: { 33 | app: [path.resolve(__dirname, 'app/main.js')] 34 | }, 35 | output: { 36 | path: '/tmp/public/assets', 37 | filename: '[name].bundle.js', 38 | publicPath: '/assets/' 39 | }, 40 | module: { 41 | rules: [ 42 | { 43 | // parse vue components 44 | test: /\.vue$/, 45 | loader: 'vue-loader', 46 | include: includes 47 | }, { 48 | // parse javascript files (use babel to transpile) 49 | // note that presets and plugins must be defined as plugin 50 | // settings (at least for now) 51 | test: /\.js$/, 52 | loader: 'babel-loader', 53 | include: includes 54 | }, { 55 | // parse stylus styles 56 | test: /\.styl(us)?$/, 57 | use: [ 58 | process.env.NODE_ENV !== 'production' ? 59 | 'vue-style-loader': MiniCssExtractPlugin.loader, 60 | 'css-loader', 'stylus-loader'], 61 | include: [appDirectory, nodeModules + '/nib'] 62 | }, { 63 | // parse css styles 64 | test: /\.css$/, 65 | use: [ 66 | process.env.NODE_ENV !== 'production' ? 67 | 'vue-style-loader': MiniCssExtractPlugin.loader, 68 | 'css-loader','postcss-loader'], 69 | include: [appDirectory, nodeModules + '/vue-multiselect/dist'] 70 | }, { 71 | // include all svg-files as vue components 72 | test: /\.svg$/, 73 | loader: 'vue-svg-loader', 74 | options: { 75 | svgo: { 76 | plugins: [ 77 | {removeDoctype: true}, 78 | {removeComments: true} 79 | ] 80 | } 81 | } 82 | } 83 | ] 84 | }, 85 | resolve: { 86 | modules: [ 87 | path.resolve('./app'), 88 | path.resolve('./node_modules') 89 | ] 90 | }, 91 | plugins: [ 92 | new VueLoaderPlugin() 93 | ] 94 | }; 95 | 96 | 97 | // override some build config to extract the text 98 | 99 | // use specific configuration depending on build mode 100 | if (process.env.NODE_ENV !== 'production') { 101 | 102 | console.log('-- using development config'); 103 | 104 | config.mode = 'development'; 105 | 106 | config.devtool = '#cheap-module-eval-source-map'; 107 | 108 | // setup devserver config 109 | config.devServer = { 110 | contentBase: '/tmp/public', 111 | historyApiFallback: true, 112 | noInfo: true, 113 | 114 | host: '0.0.0.0', 115 | port: 3000, 116 | 117 | // proxy api calls to a container named api 118 | proxy: proxyConfig, 119 | 120 | before(app) { 121 | app.get('/assets/*.css', function(req, res) { 122 | res.setHeader('Content-Type', 'text/css'); 123 | res.send(''); 124 | }); 125 | }, 126 | stats: { 127 | assets: true, 128 | children: false, 129 | chunks: false, 130 | hash: false, 131 | modules: false, 132 | publicPath: true, 133 | timings: true, 134 | version: false, 135 | warnings: true 136 | }, 137 | overlay: true 138 | }; 139 | 140 | // resolve vue to non minified bundle for development 141 | config.resolve.alias = { 142 | vue: 'vue/dist/vue.common.js' 143 | }; 144 | 145 | 146 | } else { 147 | 148 | console.log('-- using production config'); 149 | 150 | config.mode = 'production'; 151 | 152 | // add babel-polyfill to the build 153 | config.entry.app.unshift('babel-polyfill'); 154 | 155 | // create source maps for the minified code 156 | config.devtool = '#source-map', 157 | 158 | // add some more plugins to the plugin array 159 | config.plugins.unshift(new webpack.DefinePlugin({ 160 | 'process.env': { 161 | NODE_ENV: '"production"' 162 | } 163 | })); 164 | 165 | // add a plugin to extract all css into separate files 166 | config.plugins.push(new MiniCssExtractPlugin({ 167 | filename: 'app.css', 168 | allChunks: true 169 | })); 170 | 171 | } 172 | 173 | module.exports = config; 174 | -------------------------------------------------------------------------------- /src/frontend/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | axios@^0.18.0: 6 | version "0.18.0" 7 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102" 8 | dependencies: 9 | follow-redirects "^1.3.0" 10 | is-buffer "^1.1.5" 11 | 12 | debug@^3.1.0: 13 | version "3.1.0" 14 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 15 | dependencies: 16 | ms "2.0.0" 17 | 18 | follow-redirects@^1.3.0: 19 | version "1.4.1" 20 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.4.1.tgz#d8120f4518190f55aac65bb6fc7b85fcd666d6aa" 21 | dependencies: 22 | debug "^3.1.0" 23 | 24 | is-buffer@^1.1.5: 25 | version "1.1.5" 26 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" 27 | 28 | ms@2.0.0: 29 | version "2.0.0" 30 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 31 | 32 | vue-router@^3.0.1: 33 | version "3.0.1" 34 | resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9" 35 | 36 | vue@^2.5.16: 37 | version "2.5.16" 38 | resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.16.tgz#07edb75e8412aaeed871ebafa99f4672584a0085" 39 | 40 | vuex-router-sync@^5.0.0: 41 | version "5.0.0" 42 | resolved "https://registry.yarnpkg.com/vuex-router-sync/-/vuex-router-sync-5.0.0.tgz#1a225c17a1dd9e2f74af0a1b2c62072e9492b305" 43 | 44 | vuex@^3.0.1: 45 | version "3.0.1" 46 | resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2" 47 | -------------------------------------------------------------------------------- /test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dkfbasel/scratch/467bc4f14f9fca025be5dcc39f070b56d5354c80/test/.gitkeep --------------------------------------------------------------------------------