Compare commits

..

11 Commits

Author SHA1 Message Date
752a058ac3 Merge 'feature/dev-prepare-for-release_1' (#1) from feature/dev-prepare-for-release_1 into develop
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Reviewed-on: https://git.pbiernat.dev/golang/rest-api-prototype/pulls/1
2022-06-19 14:48:03 +02:00
90a0f562ea [release] release v1
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2022-06-19 14:45:38 +02:00
39ed417398 [feature] Updated go.mod to Go v1.18 and extended log func
Some checks failed
continuous-integration/drone/push Build is failing
2022-06-18 15:01:05 +02:00
316323587d [fix] Restored DB connection
Some checks failed
continuous-integration/drone/push Build is failing
2022-06-18 13:51:37 +02:00
0fb494f0fd [fix] systemd config update & http service fix
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-25 00:12:49 +02:00
30ee6e8c39 [fix] systemd config updated
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-23 21:23:28 +02:00
bf3f5a0fc0 [fix] Refactored handler initialization
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-23 21:13:14 +02:00
41292b25e3 [feature] Added systemd support
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-23 21:11:04 +02:00
ceb3c4bba5 [tmp] disabled db support
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-23 13:13:59 +02:00
69b4179102 [fix] Fixed empty response
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-21 23:35:49 +02:00
4dab09e66b [feature] Added CI config
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-21 22:37:05 +02:00
19 changed files with 82 additions and 198 deletions

View File

@ -32,7 +32,6 @@ steps:
- name: build_image
image: plugins/docker
commands:
- env
- sleep 5
- ./deploy/docker/build_image.sh
depends_on:

View File

@ -7,7 +7,7 @@ race:
go run --race cmd/server/main.go
build:
go build -o build/server cmd/server/main.go
GOOS=linux GOARCH=amd64 go build -o build/server cmd/server/main.go
test:
go test -v -run=. test/**/*.go

View File

@ -2,7 +2,6 @@ package main
import (
"context"
"log"
"net"
"os"
"os/signal"
@ -17,20 +16,22 @@ import (
const (
defHttpIp = "127.0.0.1"
defHttpPort = "8080"
defDbUrl = "postgres://postgres:postgres@127.0.0.1:5432/Api" // FIXME use default container conf in future
defDbUrl = "postgres://postgres:postgres@127.0.0.1:5432/Api" // FIXME: use env
)
func main() {
if config.ErrLoadingEnvs != nil {
log.Fatalln("Error loading .env file")
app.Panicf("Error loading .env file")
}
httpAddr := net.JoinHostPort(config.GetEnv("SERVER_IP", defHttpIp), config.GetEnv("SERVER_PORT", defHttpPort))
dbConnStr := config.GetEnv("DATABASE_URL", defDbUrl)
//fmt.Println(dbConnStr)
//os.Exit(1)
dbc, err := database.Connect(dbConnStr)
if err != nil {
log.Panicf("Unable to connect to database: %v\n", err)
app.Panicf("Unable to connect to database: %v\n", err)
}
env := &handler.Env{httpAddr, dbc}

View File

@ -1,6 +1,5 @@
#!/bin/sh
set -e
set -x
set -evx
branch=${DRONE_TAG:=$CI_COMMIT_BRANCH}
branch=$(echo $branch | grep -v /) || echo $branch ;

View File

@ -1,6 +1,5 @@
#!/bin/sh
set -e
set -x
set -evx
branch=${DRONE_TAG:=$CI_COMMIT_BRANCH}
branch=$(echo $branch | grep -v /) || echo $branch ;

2
go.mod
View File

@ -1,6 +1,6 @@
module git.pbiernat.dev/golang/rest-api-prototype
go 1.17
go 1.18
require (
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible

21
init/api-server.service Normal file
View File

@ -0,0 +1,21 @@
#FIXME: FIX PATHS
[Unit]
Requires=network-online.target
After=network-online.target
Requires=api-server.socket
[Service]
ExecStart=/home/keedosn/go/src/git.pbiernat.dev/golang/rest-api-prototype/build/server
ExecStop=/bin/kill $MAINPID
WorkingDirectory=/home/keedosn/go/src/git.pbiernat.dev/golang/rest-api-prototype
User=keedosn
Group=keedosn
NonBlocking=true
StandardOutput=append:/home/keedosn/go/src/git.pbiernat.dev/golang/rest-api-prototype/build/api-server.log
StandardError=append:/home/keedosn/go/src/git.pbiernat.dev/golang/rest-api-prototype/build/api-server.log
SyslogIdentifier=api-server
[Install]
WantedBy=multi-user.target

7
init/api-server.socket Normal file
View File

@ -0,0 +1,7 @@
[Unit]
Description=API Server socket
[Socket]
ListenStream=10080
NoDelay=true

View File

@ -12,9 +12,6 @@ func init() {
ErrLoadingEnvs = godotenv.Load()
}
func init() {
}
func GetEnv(name, defVal string) string {
env := os.Getenv(name)
if env == "" {

View File

@ -1,16 +0,0 @@
package definition
import "git.pbiernat.dev/golang/rest-api-prototype/internal/app/entity"
type CreateArticleRequest struct {
CategoryID int `json:"category_id"`
Title string `json:"title"`
Intro string `json:"intro"`
Text string `json:"text"`
}
type CreateArticleResponse struct {
Status string `json:"status"`
Data *entity.Article `json:"data"`
Err string `json:"err,omitempty"`
}

View File

@ -1,27 +0,0 @@
package definition
import (
"git.pbiernat.dev/golang/rest-api-prototype/internal/app/entity"
validation "github.com/go-ozzo/ozzo-validation"
)
type CreateCategoryRequest struct {
Name string `json:"name"`
}
func (c CreateCategoryRequest) Validate() error {
return validation.ValidateStruct(&c,
validation.Field(&c.Name, validation.Required, validation.Length(3, 255)),
)
}
type CreateCategoryResponse struct {
Data *entity.Category `json:"data"`
Err string `json:"err,omitempty"` // FIXME: omitempty on/off?
}
type DeleteCategoryRequest struct {
}
type DeleteCategoryResponse struct {
}

View File

@ -1,15 +0,0 @@
package entity
import (
"time"
)
type Article struct {
ID int `json:"id"`
CategoryID int `json:"category_id"`
Title string `json:"title"`
Intro string `json:"intro"`
Text string `json:"text"`
CreateDate time.Time `json:"create_date"`
ModifyDate time.Time `json:"modify_date"`
}

View File

@ -1,18 +0,0 @@
package entity
import (
"time"
)
type Category struct {
ID int `json:"id"`
Name string `json:"name"`
CreateDate time.Time `json:"create_date"`
ModifyDate time.Time `json:"modify_date"` // FIXME: zero-value issue
}
// func (c Category) Validate() error {
// return validation.ValidateStruct(&c,
// validation.Field(&c.Name, validation.Required, validation.Length(3, 255)),
// )
// }

View File

@ -1,34 +0,0 @@
package handler
import (
"log"
"net/http"
"time"
def "git.pbiernat.dev/golang/rest-api-prototype/internal/app/definition"
"git.pbiernat.dev/golang/rest-api-prototype/internal/app/entity"
)
var CreateArticleHandler *Handler
func init() {
CreateArticleHandler = &Handler{
Handle: CreateArticleHandlerFunc,
Request: &def.CreateArticleRequest{},
Response: &def.CreateArticleResponse{},
}
}
func CreateArticleHandlerFunc(h *Handler, w http.ResponseWriter) (interface{}, int, error) {
var art = h.Request.(*def.CreateArticleRequest)
log.Println(art)
return &entity.Article{
ID: 1,
CategoryID: 1,
Title: "Dummy article",
Intro: "Intro",
Text: "Text",
CreateDate: time.Now(),
}, http.StatusCreated, nil
}

View File

@ -1,57 +0,0 @@
package handler
import (
"log"
"net/http"
"strconv"
"time"
def "git.pbiernat.dev/golang/rest-api-prototype/internal/app/definition"
"git.pbiernat.dev/golang/rest-api-prototype/internal/app/entity"
)
var CreateCategoryHandler *Handler
var DeleteCategoryHandler *Handler
func init() {
CreateCategoryHandler = &Handler{
Handle: CreateCategoryHandlerFunc,
Request: &def.CreateCategoryRequest{},
Response: &def.CreateCategoryResponse{},
}
DeleteCategoryHandler = &Handler{
Handle: DeleteCategoryHandlerFunc,
Request: &def.DeleteCategoryRequest{},
Response: &def.DeleteCategoryResponse{},
}
}
func CreateCategoryHandlerFunc(h *Handler, w http.ResponseWriter) (interface{}, int, error) {
var cat = h.Request.(*def.CreateCategoryRequest)
log.Println("Cat input:", cat)
if err := cat.Validate(); err != nil {
log.Println("Create category validation errors:", err)
return nil, http.StatusUnprocessableEntity, err
}
return &entity.Category{
Name: cat.Name,
CreateDate: time.Now(),
}, http.StatusCreated, nil
}
func DeleteCategoryHandlerFunc(h *Handler, w http.ResponseWriter) (interface{}, int, error) {
var cat = h.Request.(*def.DeleteCategoryRequest)
log.Println(cat)
id, _ := strconv.Atoi(h.Params["id"])
log.Println(h.Params)
if id != 1 {
return nil, http.StatusNotFound, nil
}
return nil, http.StatusNoContent, nil
}

View File

@ -34,8 +34,11 @@ type response struct {
Data interface{}
}
func New(e *Env, h *Handler) *Handler {
return &Handler{e, h.Handle, h.Request, h.Response, Set{}}
func Init(e *Env, h *Handler) *Handler {
// return &Handler{e, h.Handle, h.Request, h.Response, Set{}}
h.Env = e
return h
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -68,7 +71,9 @@ func encodeResponse(w http.ResponseWriter, res *response, err error) {
}
w.WriteHeader(res.Status)
json.NewEncoder(w).Encode(res.Data)
if res.Data != nil {
json.NewEncoder(w).Encode(res.Data)
}
}
func encodeError(w http.ResponseWriter, status int, e error) {

16
internal/app/log.go Normal file
View File

@ -0,0 +1,16 @@
package app
import "log"
func Panic(v ...any) {
log.Panicln(Name + ":", v)
}
func Panicf(format string, v ...any) {
log.Panicf(Name + ": " + format, v...)
}
func Panicln(v ...any) {
v = append([]any{Name + ":"}, v...)
log.Panicln(v...)
}

View File

@ -4,7 +4,6 @@ import (
"net/http"
"git.pbiernat.dev/golang/rest-api-prototype/internal/app/handler"
"git.pbiernat.dev/golang/rest-api-prototype/internal/app/service"
"github.com/gorilla/mux"
)
@ -18,18 +17,10 @@ func SetupRouter(env *handler.Env) *mux.Router {
r.Use(LoggingMiddleware)
hc := r.PathPrefix("/health").Subrouter()
hc.Handle("", handler.New(env, handler.HealthCheckHandler)).Methods(http.MethodGet)
hc.Handle("", handler.Init(env, handler.HealthCheckHandler)).Methods(http.MethodGet)
auth := r.PathPrefix("/auth").Subrouter()
auth.Handle("/login", handler.New(env, handler.AuthLoginHandler)).Methods(http.MethodPost)
api := r.PathPrefix("/api").Subrouter()
api.Use(service.AuthService.ValidateUserTokenMiddleware) // only /api/** endpoints use this middleware
api.Handle("/article", handler.New(env, handler.CreateArticleHandler)).Methods(http.MethodPost)
api.Handle("/category", handler.New(env, handler.CreateCategoryHandler)).Methods(http.MethodPost)
api.Handle("/category/{id:[0-9]+}", handler.New(env, handler.DeleteCategoryHandler)).Methods(http.MethodDelete)
auth.Handle("/login", handler.Init(env, handler.AuthLoginHandler)).Methods(http.MethodPost)
return r
}

View File

@ -5,13 +5,18 @@ import (
"encoding/json"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"strconv"
"time"
def "git.pbiernat.dev/golang/rest-api-prototype/internal/app/definition"
"git.pbiernat.dev/golang/rest-api-prototype/internal/app/handler"
)
const Name = "REST API Service"
type Server struct {
*http.Server
}
@ -29,9 +34,20 @@ func NewServer(env *handler.Env) *Server {
}
func (s *Server) Start() {
log.Println("Server listening on " + s.Addr)
if err := s.ListenAndServe(); err != nil {
log.Println(err)
if os.Getenv("LISTEN_PID") == strconv.Itoa(os.Getpid()) {
// systemd run
f := os.NewFile(3, "from systemd")
l, err := net.FileListener(f)
if err != nil {
log.Fatalln(err)
}
log.Println("Server listening on " + l.Addr().String())
s.Serve(l)
} else {
log.Println("Server listening on " + s.Addr)
log.Fatalln(s.ListenAndServe())
}
}