Initial commit

This commit is contained in:
Andreas Mieke 2018-06-09 17:22:25 +02:00
commit 840cd1b471
No known key found for this signature in database
GPG key ID: 0C26F7695C85F85A
6 changed files with 547 additions and 0 deletions

85
.gitignore vendored Normal file
View file

@ -0,0 +1,85 @@
# Ignore folders in /vendor/ but allow vendor.json
/vendor/*/
!/vendor/vendor.json
# Created by https://www.gitignore.io/api/go,linux,macos,windows
### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.gitignore.io/api/go,linux,macos,windows

7
config.yml Normal file
View file

@ -0,0 +1,7 @@
DBType: "postgres"
DBConnection: "host=localhost user=shortdragon dbname=shortdragon sslmode=disable"
UseSocket: false
BindAddress: ":8080"
BindSocket: "/var/run/shortdragon.sock"
DefaultURL: "https://1750studios.com"
ShortURL: "https://1750.st"

41
database/database.go Normal file
View file

@ -0,0 +1,41 @@
package database
import (
"database/sql"
"log"
"os"
"time"
"github.com/jinzhu/gorm"
)
// Postgres database driver
import _ "github.com/jinzhu/gorm/dialects/postgres"
// Db is the open Database
var Db *gorm.DB
// URL defines the structure of a shortened URL
type URL struct {
ID uint `gorm:"primary_key:auto_increment"`
CreatedAt time.Time
UpdatedAt time.Time
Short sql.NullString `gorm:"unique_index:short_url;not null"`
Long sql.NullString `gorm:"unique;not null"`
Hits sql.NullInt64
}
// InitDb opens a database connection and runs the auto migration
func InitDb(DBType, DBConnection string) {
var err error
Db, err = gorm.Open(DBType, DBConnection)
if err != nil {
log.Fatal("Could not open database:", err)
}
if os.Getenv("GIN_MODE") != "release" {
Db.LogMode(true)
}
Db.AutoMigrate(&URL{})
}

46
main.go Normal file
View file

@ -0,0 +1,46 @@
package main
import (
"log"
"git.1750studios.com/ToddShepard/ShortDragon/database"
"git.1750studios.com/ToddShepard/ShortDragon/routes"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
func main() {
// Configuration
viper.SetDefault("DBType", "postgres")
viper.SetDefault("DBConnection", "host=localhost user=shortdragon dbname=shortdragon sslmode=disable")
viper.SetDefault("UseSocket", false)
viper.SetDefault("BindAddress", ":8080")
viper.SetDefault("BindSocket", "/var/run/shortdragon.sock")
viper.SetDefault("DefaultURL", "https://1750studios.com")
viper.SetDefault("ShortURL", "https://1750.st")
viper.SetConfigName("config")
viper.AddConfigPath("/etc/shortdragon/")
viper.AddConfigPath("$HOME/.shortdragon/")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
log.Fatal("Could not read config:", err)
}
database.InitDb(viper.GetString("DBType"), viper.GetString("DBConnection"))
router := gin.Default()
router.POST("/e", routes.Encode)
router.GET("/r/:short", routes.Redirect)
router.GET("/d/:short", routes.Decode)
router.GET("/i/:short", routes.Info)
if viper.GetBool("UseSocket") {
router.RunUnix(viper.GetString("BindSocket"))
} else {
router.Run(viper.GetString("BindAddress"))
}
}

109
routes/routes.go Normal file
View file

@ -0,0 +1,109 @@
package routes
import (
"crypto/sha512"
"encoding/base64"
"net/http"
"git.1750studios.com/ToddShepard/ShortDragon/database"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"github.com/spf13/viper"
)
// Redirect redirects a user to the long URL specified by the short URL
func Redirect(c *gin.Context) {
var URL database.URL
short := c.Param("short")
err := database.Db.First(&URL, "short = ?", short).Error
if err != nil {
c.Redirect(http.StatusFound, viper.GetString("DefaultURL"))
return
}
if !URL.Long.Valid {
c.Redirect(http.StatusFound, viper.GetString("DefaultURL"))
return
}
if URL.Hits.Valid && !(c.Request.Header.Get("DNT") == "1") {
URL.Hits.Int64 = URL.Hits.Int64 + 1
database.Db.Save(&URL)
}
c.Redirect(http.StatusFound, URL.Long.String)
return
}
// Encode encodes a long URL and returns the short one
func Encode(c *gin.Context) {
var URL database.URL
var count uint
URL.Long.String = c.PostForm("LongURL")
URL.Long.Valid = true
database.Db.Where("long = ?", URL.Long.String).FirstOrInit(&URL)
if URL.Short.Valid {
c.Data(http.StatusOK, "text/plain", []byte(viper.GetString("ShortURL")+"/"+URL.Short.String))
return
}
URL.Short.String = c.DefaultPostForm("ShortURL", "")
if URL.Short.String == "" {
hasher := sha512.New()
hasher.Write([]byte(URL.Long.String))
base := base64.StdEncoding.EncodeToString(hasher.Sum(nil))
i := 1
for {
database.Db.Model(&database.URL{}).Where("short = ?", base[0:i]).Count(&count)
if count > 0 && i < len(base) {
i = i + 1
} else if count > 0 {
c.AbortWithStatus(http.StatusConflict)
return
} else {
URL.Short.String = base[0:i]
break
}
}
}
URL.Short.Valid = true
URL.Hits.Int64 = 0
if c.DefaultPostForm("track", "true") == "true" {
URL.Hits.Valid = true
} else {
URL.Hits.Valid = false
}
err := database.Db.Create(&URL).Error
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
c.Data(http.StatusCreated, "text/plain", []byte(viper.GetString("ShortURL")+"/"+URL.Short.String))
return
}
// Decode decodes a short URL and returns the long one
func Decode(c *gin.Context) {
var URL database.URL
short := c.Param("short")
err := database.Db.Where("short = ?", short).Find(&URL).Error
if err != nil && err == gorm.ErrRecordNotFound {
c.Data(http.StatusNotFound, "text/plain", []byte("Record not found"))
return
} else if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.Data(http.StatusOK, "text/plain", []byte(URL.Long.String))
}
// Info decodes a short URL and returns the database content
func Info(c *gin.Context) {
var URL database.URL
short := c.Param("short")
err := database.Db.Where("short = ?", short).Find(&URL).Error
if err != nil && err == gorm.ErrRecordNotFound {
c.Data(http.StatusNotFound, "text/plain", []byte("Record not found"))
return
} else if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.JSON(http.StatusOK, URL)
}

259
vendor/vendor.json vendored Normal file
View file

@ -0,0 +1,259 @@
{
"comment": "",
"ignore": "test",
"package": [
{
"checksumSHA1": "7NP1qUMF8Kx1y0zANxx0e+oq9Oo=",
"path": "github.com/fsnotify/fsnotify",
"revision": "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9",
"revisionTime": "2018-01-10T05:33:47Z"
},
{
"checksumSHA1": "QeKwBtN2df+j+4stw3bQJ6yO4EY=",
"path": "github.com/gin-contrib/sse",
"revision": "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae",
"revisionTime": "2017-01-09T09:34:21Z"
},
{
"checksumSHA1": "J9Wy/bT7FckPvRzC0uSW6Tpg6Yc=",
"path": "github.com/gin-gonic/gin",
"revision": "caf3e350a548af1add9def68087ac53d1d000caa",
"revisionTime": "2018-05-31T06:13:40Z"
},
{
"checksumSHA1": "O2rrqYI7QQjJYVx8+E8DC8rlIh0=",
"path": "github.com/gin-gonic/gin/binding",
"revision": "caf3e350a548af1add9def68087ac53d1d000caa",
"revisionTime": "2018-05-31T06:13:40Z"
},
{
"checksumSHA1": "woO1qIxIeQ1bcbPSiMfAKk3r4xg=",
"path": "github.com/gin-gonic/gin/json",
"revision": "caf3e350a548af1add9def68087ac53d1d000caa",
"revisionTime": "2018-05-31T06:13:40Z"
},
{
"checksumSHA1": "DGs8sdsCkNHsU9+fQMM9XHb+ZCE=",
"path": "github.com/gin-gonic/gin/render",
"revision": "caf3e350a548af1add9def68087ac53d1d000caa",
"revisionTime": "2018-05-31T06:13:40Z"
},
{
"checksumSHA1": "JZV+pLo8Z/kIYExrZr1Zu9KSKyU=",
"path": "github.com/golang/protobuf/proto",
"revision": "05f48f4eaf0e05663b562bab533cdd472238ce29",
"revisionTime": "2018-06-06T20:26:30Z"
},
{
"checksumSHA1": "HtpYAWHvd9mq+mHkpo7z8PGzMik=",
"path": "github.com/hashicorp/hcl",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "XQmjDva9JCGGkIecOgwtBEMCJhU=",
"path": "github.com/hashicorp/hcl/hcl/ast",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "1GmX7G0Pgf5XprOh+T3zXMXX0dc=",
"path": "github.com/hashicorp/hcl/hcl/parser",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "encY+ZtDf4nJaMvsVL2c+EJ2r3Q=",
"path": "github.com/hashicorp/hcl/hcl/printer",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "+qJTCxhkwC7r+VZlPlZz8S74KmU=",
"path": "github.com/hashicorp/hcl/hcl/scanner",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "oS3SCN9Wd6D8/LG0Yx1fu84a7gI=",
"path": "github.com/hashicorp/hcl/hcl/strconv",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "c6yprzj06ASwCo18TtbbNNBHljA=",
"path": "github.com/hashicorp/hcl/hcl/token",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "PwlfXt7mFS8UYzWxOK5DOq0yxS0=",
"path": "github.com/hashicorp/hcl/json/parser",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "afrZ8VmAwfTdDAYVgNSXbxa4GsA=",
"path": "github.com/hashicorp/hcl/json/scanner",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "fNlXQCQEnb+B3k5UDL/r15xtSJY=",
"path": "github.com/hashicorp/hcl/json/token",
"revision": "ef8a98b0bbce4a65b5aa4c368430a80ddc533168",
"revisionTime": "2018-04-04T17:41:02Z"
},
{
"checksumSHA1": "nlEiwqdbwY1eclAGG1xcC+XPV9M=",
"path": "github.com/jinzhu/gorm",
"revision": "82eb9f8a5bbb5e6b929d2f0ae5b934e6a253f94e",
"revisionTime": "2018-05-12T06:29:00Z"
},
{
"checksumSHA1": "Sf/CFFl00e8nN8DDKaT2dKLUaFI=",
"path": "github.com/jinzhu/gorm/dialects/postgres",
"revision": "82eb9f8a5bbb5e6b929d2f0ae5b934e6a253f94e",
"revisionTime": "2018-05-12T06:29:00Z"
},
{
"checksumSHA1": "4eelJOSLZoDzLuaJvgilOBVNovU=",
"path": "github.com/jinzhu/inflection",
"revision": "04140366298a54a039076d798123ffa108fff46c",
"revisionTime": "2018-03-08T03:36:59Z"
},
{
"checksumSHA1": "gydhX7ik17tDsfT5t97UDRseQ7g=",
"path": "github.com/json-iterator/go",
"revision": "8744d7c5c7b40a53e018f78d8c508b3315260b96",
"revisionTime": "2018-05-26T01:43:29Z"
},
{
"checksumSHA1": "s6eyIXpJ7VAU06FM27GcEBM/5lo=",
"path": "github.com/lib/pq",
"revision": "90697d60dd844d5ef6ff15135d0203f65d2f53b8",
"revisionTime": "2018-05-23T17:54:26Z"
},
{
"checksumSHA1": "EnMmNGgpDa5sNw0Zs5RhvNDcP2g=",
"path": "github.com/lib/pq/hstore",
"revision": "90697d60dd844d5ef6ff15135d0203f65d2f53b8",
"revisionTime": "2018-05-23T17:54:26Z"
},
{
"checksumSHA1": "AU3fA8Sm33Vj9PBoRPSeYfxLRuE=",
"path": "github.com/lib/pq/oid",
"revision": "90697d60dd844d5ef6ff15135d0203f65d2f53b8",
"revisionTime": "2018-05-23T17:54:26Z"
},
{
"checksumSHA1": "AYZGozgchqe4WcKH/jCZI1flnP8=",
"path": "github.com/magiconair/properties",
"revision": "c2353362d570a7bfa228149c62842019201cfb71",
"revisionTime": "2018-05-15T20:40:05Z"
},
{
"checksumSHA1": "w5RcOnfv5YDr3j2bd1YydkPiZx4=",
"path": "github.com/mattn/go-isatty",
"revision": "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c",
"revisionTime": "2017-11-07T05:05:31Z"
},
{
"checksumSHA1": "ewGq4nGalpCQOHcmBTdAEQx1wW0=",
"path": "github.com/mitchellh/mapstructure",
"revision": "bb74f1db0675b241733089d5a1faa5dd8b0ef57b",
"revisionTime": "2018-05-11T14:21:26Z"
},
{
"checksumSHA1": "ZTcgWKWHsrX0RXYVXn5Xeb8Q0go=",
"path": "github.com/modern-go/concurrent",
"revision": "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94",
"revisionTime": "2018-03-06T01:26:44Z"
},
{
"checksumSHA1": "ntSr6NgBxGzQG1fgkl5IFgdjCLo=",
"path": "github.com/modern-go/reflect2",
"revision": "58118c1ea9161250907268a484af4dd6ed314280",
"revisionTime": "2018-05-11T05:30:14Z"
},
{
"checksumSHA1": "hlXUEyUn6u1ZhZbunDqTTEz1gyw=",
"path": "github.com/pelletier/go-toml",
"revision": "c01d1270ff3e442a8a57cddc1c92dc1138598194",
"revisionTime": "2018-03-06T02:43:58Z"
},
{
"checksumSHA1": "wsfbX7YxbkeBp+xm9M9nBFzrCnA=",
"path": "github.com/spf13/afero",
"revision": "787d034dfe70e44075ccc060d346146ef53270ad",
"revisionTime": "2018-05-31T09:43:57Z"
},
{
"checksumSHA1": "HcOjO9+cwl+xYnkiGuIeesqwBs8=",
"path": "github.com/spf13/afero/mem",
"revision": "787d034dfe70e44075ccc060d346146ef53270ad",
"revisionTime": "2018-05-31T09:43:57Z"
},
{
"checksumSHA1": "Hc2i9OOK34PAImuNftTaHdbdLgs=",
"path": "github.com/spf13/cast",
"revision": "8965335b8c7107321228e3e3702cab9832751bac",
"revisionTime": "2018-02-14T17:35:30Z"
},
{
"checksumSHA1": "+JFKK0z5Eutk29rUz1lEhLxHMfk=",
"path": "github.com/spf13/jwalterweatherman",
"revision": "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394",
"revisionTime": "2018-01-09T13:55:06Z"
},
{
"checksumSHA1": "OJI0OgC5V8gZtfS1e0CDYMhkDNc=",
"path": "github.com/spf13/pflag",
"revision": "3ebe029320b2676d667ae88da602a5f854788a8a",
"revisionTime": "2018-06-01T13:25:42Z"
},
{
"checksumSHA1": "Kd8R7CLqI5pceYpTircWmxZzbvE=",
"path": "github.com/spf13/viper",
"revision": "15738813a09db5c8e5b60a19d67d3f9bd38da3a4",
"revisionTime": "2018-05-06T01:38:06Z"
},
{
"checksumSHA1": "kdas0zQNtQqfUMEMFGkUF1OcgPc=",
"path": "github.com/ugorji/go/codec",
"revision": "f3cacc17c85ecb7f1b6a9e373ee85d1480919868",
"revisionTime": "2018-04-07T10:30:00Z"
},
{
"checksumSHA1": "VUb+SqeoloAw1A2Jz9IzkDNs06c=",
"path": "golang.org/x/sys/unix",
"revision": "9527bec2660bd847c050fda93a0f0c6dee0800bb",
"revisionTime": "2018-06-06T19:45:53Z"
},
{
"checksumSHA1": "ziMb9+ANGRJSSIuxYdRbA+cDRBQ=",
"path": "golang.org/x/text/transform",
"revision": "5c1cf69b5978e5a34c5f9ba09a83e56acc4b7877",
"revisionTime": "2018-05-10T04:57:31Z"
},
{
"checksumSHA1": "BCNYmf4Ek93G4lk5x3ucNi/lTwA=",
"path": "golang.org/x/text/unicode/norm",
"revision": "5c1cf69b5978e5a34c5f9ba09a83e56acc4b7877",
"revisionTime": "2018-05-10T04:57:31Z"
},
{
"checksumSHA1": "P/k5ZGf0lEBgpKgkwy++F7K1PSg=",
"path": "gopkg.in/go-playground/validator.v8",
"revision": "5f1438d3fca68893a817e4a66806cea46a9e4ebf",
"revisionTime": "2017-07-30T05:02:35Z"
},
{
"checksumSHA1": "ZSWoOPUNRr5+3dhkLK3C4cZAQPk=",
"path": "gopkg.in/yaml.v2",
"revision": "5420a8b6744d3b0345ab293f6fcba19c978f1183",
"revisionTime": "2018-03-28T19:50:20Z"
}
],
"rootPath": "git.1750studios.com/ToddShepard/ShortDragon"
}