summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrailFinder <wohilas@gmail.com>2024-03-17 13:13:01 +0300
committerGrailFinder <wohilas@gmail.com>2024-03-17 13:13:01 +0300
commit33db3abdadd6687eb16305014c70654e03168fe5 (patch)
tree62b8f8766b896227fdc9ce15c0cac8530ced3099
init
-rw-r--r--.gitignore3
-rw-r--r--Makefile36
-rw-r--r--README.md1
-rw-r--r--cmd/root.go46
-rw-r--r--cmd/start.go37
-rw-r--r--config/config.go43
-rw-r--r--go.mod31
-rw-r--r--go.sum75
-rw-r--r--internal/handlers/main.go36
-rw-r--r--internal/server/main.go60
-rw-r--r--internal/server/router.go23
-rw-r--r--internal/service/main.go0
-rw-r--r--main.go14
13 files changed, 405 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9339e73
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+secrethitler
+config.yml
+apjournal
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e8d3207
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
+.PHONY: all init deps install test lint run stop
+
+run:
+ # templ generate
+ go build
+ ./secrethitler start
+
+init:
+ go mod init
+
+# install all dependencies used by the application
+deps:
+ go clean -modcache
+ go mod download
+
+# install the application in the Go bin/ folder
+install:
+ go install ./...
+
+test:
+ go test ./...
+
+lint:
+ golangci-lint run --config .golangci.yml
+
+gen:
+ go generate ./...
+
+build-container:
+ docker build -t secretH:master .
+
+stop-container:
+ docker rm -f secretH 2>/dev/null && echo "old container removed"
+
+run-container: stop-container
+ docker run --name=secretH -v $(CURDIR)/store.json:/root/store.json -p 0.0.0.0:8087:8087 -d secretH:master
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..135609a
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# Goserver implementation for tabletop game named Secret Hitler
diff --git a/cmd/root.go b/cmd/root.go
new file mode 100644
index 0000000..9e87b5b
--- /dev/null
+++ b/cmd/root.go
@@ -0,0 +1,46 @@
+package cmd
+
+import (
+ "log"
+
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+)
+
+// LogLevel Flag
+var LogLevel = "info"
+var LogFormat = "json"
+var cfgFile string
+var rootCmd = &cobra.Command{
+ Use: "apjournal",
+ Short: "apjournal",
+}
+
+func init() {
+ cobra.OnInitialize(initConfig)
+ rootCmd.PersistentFlags().StringVar(&cfgFile,
+ "config", "", "config file (default is ./config.yml)")
+ viper.SetConfigName("config")
+ viper.AddConfigPath(".") // First try to load the config from the current directory
+ viper.AddConfigPath("$HOME") // Then try to load it from the HOME directory
+ viper.AddConfigPath("/etc/apjournal/") // As a last resort try to load it from the /etc/
+}
+
+func initConfig() {
+ if cfgFile != "" {
+ // Use config file from the flag.
+ viper.SetConfigFile(cfgFile)
+ }
+ viper.SetEnvPrefix("CFG")
+ viper.AutomaticEnv()
+ if err := viper.ReadInConfig(); err != nil {
+ log.Fatal("Can't read configuration file", "error", err)
+ }
+}
+
+// Execute the commands
+func Execute() {
+ if err := rootCmd.Execute(); err != nil {
+ log.Fatal("failed to execute", "error", err)
+ }
+}
diff --git a/cmd/start.go b/cmd/start.go
new file mode 100644
index 0000000..00872aa
--- /dev/null
+++ b/cmd/start.go
@@ -0,0 +1,37 @@
+package cmd
+
+import (
+ "os"
+ "apjournal/config"
+ "apjournal/internal/server"
+
+ "log/slog"
+
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+)
+
+func init() {
+ rootCmd.AddCommand(startCmd)
+}
+
+var startCmd = &cobra.Command{
+ Use: "start",
+ Short: "Start data server",
+ Long: `Start data server`,
+ Run: func(cmd *cobra.Command, args []string) {
+ log := slog.New(slog.NewJSONHandler(os.Stdout, nil))
+ // load server configuration from server
+ log.Debug("Loading server configuration")
+ if viper.ConfigFileUsed() != "" {
+ log.Debug("Configuration file loaded", "section", "init",
+ "path", viper.ConfigFileUsed())
+ }
+ cfg := config.LoadConfig(viper.GetViper())
+
+ srv := server.NewServer(cfg, log)
+ // listen for new messages
+ log.Info("Listening for incoming events")
+ srv.Listen()
+ },
+}
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000..135df95
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,43 @@
+package config
+
+import (
+ "log/slog"
+ "os"
+
+ "github.com/spf13/viper"
+)
+
+type Config struct {
+ ServerConfig ServerConfig `mapstructure:"SERVICE"`
+ BaseURL string `mapstructure:"BASE_URL"`
+ SessionLifetime int `mapstructure:"SESSION_LIFETIME_SECONDS"`
+}
+
+type ServerConfig struct {
+ Host string `mapstructure:"HOST"`
+ Port string `mapstructure:"PORT"`
+}
+
+// LoadConfig Load server configuration from the yaml file
+func LoadConfig(viperConf *viper.Viper) Config {
+ logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
+ var config Config
+
+ err := viperConf.Unmarshal(&config)
+ if err != nil {
+ logger.Error("Unable to decode configuration file", "error", err)
+ }
+ return config
+}
+
+// OpenConfig open config file
+func OpenConfig() {
+ logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
+ viper.SetConfigType("yml")
+ viper.SetConfigName("config")
+ viper.SetEnvPrefix("CFG")
+ err := viper.ReadInConfig() // Find and read the config file
+ if err != nil { // Handle errors reading the config file
+ logger.Error("Unable to read configuration file", "error", err)
+ }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..71dd35f
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,31 @@
+module apjournal
+
+go 1.22.1
+
+require (
+ github.com/spf13/cobra v1.8.0
+ github.com/spf13/viper v1.18.2
+)
+
+require (
+ github.com/fsnotify/fsnotify v1.7.0 // indirect
+ github.com/hashicorp/hcl v1.0.0 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/magiconair/properties v1.8.7 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/pelletier/go-toml/v2 v2.1.0 // indirect
+ github.com/sagikazarmark/locafero v0.4.0 // indirect
+ github.com/sagikazarmark/slog-shim v0.1.0 // indirect
+ github.com/sourcegraph/conc v0.3.0 // indirect
+ github.com/spf13/afero v1.11.0 // indirect
+ github.com/spf13/cast v1.6.0 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/subosito/gotenv v1.6.0 // indirect
+ go.uber.org/atomic v1.9.0 // indirect
+ go.uber.org/multierr v1.9.0 // indirect
+ golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
+ golang.org/x/sys v0.15.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
+ gopkg.in/ini.v1 v1.67.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..b6a7dcc
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,75 @@
+github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
+github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
+github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
+github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
+github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
+github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
+github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
+github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
+github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
+go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
+go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
+golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/handlers/main.go b/internal/handlers/main.go
new file mode 100644
index 0000000..3b551f1
--- /dev/null
+++ b/internal/handlers/main.go
@@ -0,0 +1,36 @@
+package handlers
+
+import (
+ "log/slog"
+ "net/http"
+ "os"
+ "apjournal/config"
+)
+
+// Handlers structure
+type Handlers struct {
+ cfg config.Config
+ // s *service.Service
+ log *slog.Logger
+}
+
+// NewHandlers constructor
+func NewHandlers(
+ // cfg config.Config, s *service.Service, l *slog.Logger,
+ cfg config.Config, l *slog.Logger,
+) *Handlers {
+ if l == nil {
+ l = slog.New(slog.NewJSONHandler(os.Stdout, nil))
+ }
+ h := &Handlers{
+ cfg: cfg,
+ // s: s,
+ log: l,
+ }
+ return h
+}
+
+func (h *Handlers) Ping(w http.ResponseWriter, r *http.Request) {
+ h.log.Info("got ping request")
+ w.Write([]byte("pong"))
+}
diff --git a/internal/server/main.go b/internal/server/main.go
new file mode 100644
index 0000000..45f3d41
--- /dev/null
+++ b/internal/server/main.go
@@ -0,0 +1,60 @@
+package server
+
+import (
+ "apjournal/config"
+ "apjournal/internal/handlers"
+
+ "context"
+ "log/slog"
+ "os"
+ "os/signal"
+ "syscall"
+)
+
+// Server interface
+type Server interface {
+ Listen()
+}
+
+type server struct {
+ config config.Config
+ actions *handlers.Handlers
+ ctx context.Context
+ close context.CancelFunc
+}
+
+func (srv *server) stopOnSignal(close context.CancelFunc) {
+ // listen for termination signals
+ sigc := make(chan os.Signal, 1)
+ signal.Notify(sigc, os.Interrupt, syscall.SIGINT)
+ signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
+ sig := <-sigc
+
+ log := slog.New(slog.NewJSONHandler(os.Stdout, nil))
+ log.Info("Shutting down services",
+ "section", "server",
+ "app_event", "terminate",
+ "signal", sig.String())
+ close()
+ os.Exit(0)
+}
+
+func NewServer(cfg config.Config, log *slog.Logger) Server {
+ ctx, close := context.WithCancel(context.Background())
+ // s := service.NewService(ctx, cfg, store.MemCache)
+ actions := handlers.NewHandlers(cfg, log)
+
+ return &server{
+ config: cfg,
+ actions: actions,
+ ctx: ctx,
+ close: close,
+ }
+}
+
+// Listen for new events that affect the market and process them
+func (srv *server) Listen() {
+ // start the http server
+ go srv.ListenToRequests()
+ srv.stopOnSignal(srv.close)
+}
diff --git a/internal/server/router.go b/internal/server/router.go
new file mode 100644
index 0000000..ed33c19
--- /dev/null
+++ b/internal/server/router.go
@@ -0,0 +1,23 @@
+package server
+
+import (
+ "fmt"
+ "net/http"
+ "time"
+)
+
+func (srv *server) ListenToRequests() {
+ h := srv.actions
+ mux := http.NewServeMux()
+ server := &http.Server{
+ Addr: "localhost:9000",
+ Handler: mux,
+ ReadTimeout: time.Second * 5,
+ WriteTimeout: time.Second * 5,
+ }
+
+ mux.HandleFunc("/ping", h.Ping)
+
+ fmt.Printf("Listening on %v\n", server.Addr)
+ server.ListenAndServe()
+}
diff --git a/internal/service/main.go b/internal/service/main.go
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/internal/service/main.go
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..10c7fee
--- /dev/null
+++ b/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+ "runtime"
+ "apjournal/cmd"
+)
+
+func init() {
+ runtime.GOMAXPROCS(runtime.NumCPU())
+}
+
+func main() {
+ cmd.Execute()
+}