Skip to content

Commit

Permalink
init project
Browse files Browse the repository at this point in the history
  • Loading branch information
ChronosXYZ committed Jul 22, 2020
0 parents commit 5b974ee
Show file tree
Hide file tree
Showing 22 changed files with 909 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.idea
/shortify
/devzone/mongodb-data
/.config.toml
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Shortify

## Requirements

- Go 1.14.4
- MongoDB

## Building

```bash
go build ./cmd/shortify
```

## Running

1. Firstly, you need to generate config:
```bash
./shortify -genconfig > backend.toml
```

2. Then just edit it, and start executable with specified config:
```bash
./shortify -config ./backend.toml
```
78 changes: 78 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package app

import (
"context"
"fmt"
"github.com/MoonSHRD/logger"
"net/http"

"github.com/MoonSHRD/shortify/config"
"github.com/gorilla/mux"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type App struct {
Config *config.Config
DBClient *mongo.Client
DB *mongo.Database

server *http.Server
}

func NewApp(config *config.Config) (*App, error) {
app := &App{}
app.Config = config
err := app.createMongoDBConnection(&config.MongoDB)
if err != nil {
return nil, err
}

port := app.Config.HTTP.Port
addr := app.Config.HTTP.Address
server := &http.Server{Addr: fmt.Sprintf("%s:%d", addr, port)}
app.server = server

return app, nil
}

func (app *App) createMongoDBConnection(config *config.MongoDB) error {
var mongoURI string
if config.User == "" && config.Password == "" {
mongoURI = fmt.Sprintf("mongodb://%s:%d", config.Host, config.Port)
} else {
mongoURI = fmt.Sprintf("mongodb://%s:%s@%s:%d", config.User, config.Password, config.Host, config.Port)
}
clientOptions := options.Client().ApplyURI(mongoURI)
client, err := mongo.Connect(context.Background(), clientOptions)
if err != nil {
return err
}
err = client.Ping(context.Background(), nil)
if err != nil {
return err
}

app.DBClient = client
app.DB = client.Database(app.Config.MongoDB.DatabaseName)
return nil
}

func (app *App) Run(r *mux.Router) {
port := app.Config.HTTP.Port
addr := app.Config.HTTP.Address
app.server.Handler = r

logger.Infof("HTTP server starts listening at %s:%d", addr, port)
go func() {
if err := app.server.ListenAndServe(); err != nil {
logger.Info(err)
}
}()
}

func (app *App) Destroy() {
ctx := context.Background()
_ = app.DBClient.Disconnect(ctx)
_ = app.server.Shutdown(ctx)
}
70 changes: 70 additions & 0 deletions cmd/shortify/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/signal"
"syscall"

"github.com/MoonSHRD/logger"
"github.com/MoonSHRD/shortify/app"
"github.com/MoonSHRD/shortify/config"
"github.com/MoonSHRD/shortify/router"
)

func main() {
var configPath string
var generateConfig bool
var verboseLogging bool
var syslogLogging bool
flag.StringVar(&configPath, "config", "", "Path to server config")
flag.BoolVar(&generateConfig, "genconfig", false, "Generate new config")
flag.BoolVar(&verboseLogging, "verbose", true, "Verbose logging")
flag.BoolVar(&syslogLogging, "syslog", false, "Log to system logging daemon")
flag.Parse()
defer logger.Init("shortify", verboseLogging, syslogLogging, ioutil.Discard).Close() // TODO Make ability to use file for log output
if generateConfig {
confStr, err := config.Generate()
if err != nil {
log.Fatalf("Cannot generate config! %s", err.Error())
}
fmt.Print(confStr)
os.Exit(0)
}
logger.Info("Starting Shortify...")
if configPath == "" {
logger.Fatal("Path to config isn't specified!")
}

cfg, err := config.Parse(configPath)
if err != nil {
logger.Fatal(err)
}
app, err := app.NewApp(cfg)
if err != nil {
logger.Fatal(err)
}
router, err := router.NewRouter(app)
if err != nil {
logger.Fatalf("Failed to initialize router: %s", err.Error())
}

// CTRL+C handler.
signalHandler := make(chan os.Signal, 1)
shutdownDone := make(chan bool, 1)
signal.Notify(signalHandler, os.Interrupt, syscall.SIGTERM)
go func() {
<-signalHandler
logger.Info("CTRL+C or SIGTERM received, shutting down Shortify...")
app.Destroy()
shutdownDone <- true
}()

app.Run(router)

<-shutdownDone
os.Exit(0)
}
51 changes: 51 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package config

import (
"github.com/pelletier/go-toml"
"log"
"os"
)

type Config struct {
HTTP HTTP `toml:"http"`
MongoDB MongoDB `toml:"mongoDB"`
AuthServerURL string `toml:"authServerURL"`
}

type HTTP struct {
Address string `toml:"address"`
Port int `toml:"port"`
}

type MongoDB struct {
Host string `toml:"host"`
Port int `toml:"port"`
User string `toml:"user"`
Password string `toml:"password"`
DatabaseName string `toml:"databaseName"`
}

func Parse(path string) (*Config, error) {
file, err := os.Open(path)
defer file.Close()
if err != nil {
log.Fatal(err)
}
decoder := toml.NewDecoder(file)
cfg := Config{}
err = decoder.Decode(&cfg)
if err != nil {
log.Fatal(err)
}

return &cfg, nil
}

func Generate() (string, error) {
config := Config{}
configStr, err := toml.Marshal(config)
if err != nil {
return "", err
}
return string(configStr), nil
}
71 changes: 71 additions & 0 deletions controllers/links.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package controllers

import (
"encoding/json"
"github.com/MoonSHRD/logger"
"net/http"

"github.com/MoonSHRD/shortify/app"
httpModels "github.com/MoonSHRD/shortify/models/http"
"github.com/MoonSHRD/shortify/services"
)

type LinksController struct {
app *app.App
linksService *services.LinksService
}

func NewLinksController(a *app.App, ls *services.LinksService) *LinksController {
return &LinksController{
app: a,
linksService: ls,
}
}

func (lc *LinksController) CreateLink(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
decoder := json.NewDecoder(r.Body)
var createLinkRequest httpModels.CreateLinkRequest
err := decoder.Decode(&createLinkRequest)
if err != nil {
logger.Error(err)
ReturnHTTPError(w, err.Error(), http.StatusBadRequest)
return
}
res, err := lc.linksService.Put(&createLinkRequest)
if err != nil {
if err == services.ErrEmptyLinkValue {
ReturnHTTPError(w, err.Error(), http.StatusBadRequest)
return
}
logger.Error(err)
ReturnHTTPError(w, err.Error(), http.StatusInternalServerError)
return
}
json, _ := json.Marshal(res)
w.Write(json)
}

func (lc *LinksController) GetLink(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

linkIDParam, ok := r.URL.Query()["linkID"]
if !ok || len(linkIDParam[0]) < 1 {
logger.Error("Url Param 'linkID' is missing")
return
}
linkID := linkIDParam[0]

res, err := lc.linksService.GetByLinkID(linkID)
if err != nil {
if err == services.ErrNoSuchLink {
ReturnHTTPError(w, err.Error(), http.StatusBadRequest)
return
}
logger.Error(err)
ReturnHTTPError(w, err.Error(), http.StatusInternalServerError)
return
}
json, _ := json.Marshal(res)
w.Write(json)
}
26 changes: 26 additions & 0 deletions controllers/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package controllers

import (
"encoding/json"
"fmt"
"net/http"

httpModels "github.com/MoonSHRD/shortify/models/http"
)

func ReturnHTTPError(w http.ResponseWriter, err string, code int) {
res := httpModels.HTTPError{
Error: httpModels.ErrorMessage{
Message: err,
},
}
r, _ := json.Marshal(res)
writeHTTPJSONError(w, string(r), code)
}

func writeHTTPJSONError(w http.ResponseWriter, err string, code int) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
fmt.Fprintln(w, err)
}
Loading

0 comments on commit 5b974ee

Please sign in to comment.