Ma Suhyeon

Change framework to echo

# Created by https://www.toptal.com/developers/gitignore/api/go,vscode
# Edit at https://www.toptal.com/developers/gitignore?templates=go,vscode
### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
### Go Patch ###
/vendor/
/Godeps/
### vscode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# End of https://www.toptal.com/developers/gitignore/api/go,vscode
__debug_bin
config.json
data
mf-server
\ No newline at end of file
package main
import (
"fmt"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
type Prop int
const (
PropUserNo Prop = iota
)
type App struct {
Config Config
db *sqlx.DB
echo *echo.Echo
}
func NewApp(config Config) *App {
app := new(App)
app.Config = config
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true", config.Database.User, config.Database.Password, config.Database.Host, config.Database.Name)
app.db = sqlx.MustOpen("mysql", dsn)
auth := middleware.JWTWithConfig(middleware.JWTConfig{
SigningKey: []byte(config.TokenSecret),
Claims: &AuthClaims{},
})
app.echo = echo.New()
app.echo.POST("/users", app.PostUsers)
app.echo.POST("/users/tokens", app.PostTokens)
app.echo.POST("/extractions", app.PostExtractions, auth)
extraction := app.echo.Group("/extractions/:no")
extraction.GET("/calls", app.GetCalls)
extraction.GET("/messages", app.GetMessages)
extraction.GET("/calls/analyses", app.GetCallsAnalyses)
extraction.GET("/apps/analyses", app.GetAppsAnalyses)
extraction.GET("/messages/analyses", app.GetMessagesAnalyses)
extraction.GET("/processes", app.GetProcesses)
extraction.GET("/alarms", app.GetAlarms)
return app
}
func (app *App) Serve() {
app.echo.Logger.Fatal(app.echo.Start(fmt.Sprintf(":%d", app.Config.Port)))
}
package main
import (
"encoding/json"
"io/ioutil"
)
type Config struct {
Port int `json:"port"`
Database struct {
Host string `json:"host"`
Name string `json:"name"`
User string `json:"user"`
Password string `json:"password"`
} `json:"database"`
TokenSecret string `json:"token_secret"`
}
func LoadConfig(path string) (Config, error) {
config := Config{}
data, err := ioutil.ReadFile(path)
if err == nil {
err = json.Unmarshal(data, &config)
}
return config, err
}
package main
import (
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/jmoiron/sqlx"
"github.com/labstack/echo/v4"
_ "github.com/mattn/go-sqlite3"
)
type Call struct {
ID int `json:"id" db:"id"`
Type int `json:"type" db:"type"`
Name *string `json:"name" db:"name"`
Number int `json:"number" db:"number"`
Duration int `json:"duration" db:"duration"`
Date time.Time `json:"date" db:"date"`
}
func (app *App) GetCalls(c echo.Context) error {
calls := []Call{}
query := `SELECT * FROM calls WHERE extraction_no=?`
app.db.Unsafe().Select(&calls, query, c.Param("no"))
return c.JSON(http.StatusOK, calls)
}
type CallStats struct {
Number string `json:"number" db:"number"`
Name *string `json:"name" db:"name"`
Incoming int `json:"incoming" db:"incoming"`
Outgoing int `json:"outgoing" db:"outgoing"`
Duration int `json:"duration" db:"duration"`
}
func (app *App) GetCallsAnalyses(c echo.Context) error {
calls := []CallStats{}
query := `SELECT number, name,
(SELECT COUNT(1) FROM calls s WHERE s.extraction_no=c.extraction_no AND s.number=c.number AND s.type=1) incoming,
(SELECT COUNT(1) FROM calls s WHERE s.extraction_no=c.extraction_no AND s.number=c.number AND s.type=2) outgoing,
SUM(duration) duration
FROM calls c WHERE extraction_no=? GROUP BY number ORDER BY duration DESC`
app.db.Select(&calls, query, c.Param("no"))
return c.JSON(http.StatusOK, calls)
}
type AppInfo struct {
PackageName string `json:"package_name" db:"package_name"`
Name string `json:"name" db:"name"`
Version string `json:"version" db:"version"`
WifiUsage int `json:"wifi_usage" db:"wifi_usage"`
CellularUsage int `json:"cellular_usage" db:"cellular_usage"`
LastUsed time.Time `json:"last_used" db:"last_used"`
ForegroundTime int64 `json:"foreground_time" db:"foreground_time"`
}
func (app *App) GetAppsAnalyses(c echo.Context) error {
apps := []AppInfo{}
query := `SELECT * FROM apps WHERE extraction_no=? ORDER BY foreground_time DESC LIMIT 0, 100`
app.db.Unsafe().Select(&apps, query, c.Param("no"))
return c.JSON(http.StatusOK, apps)
}
type Message struct {
ID int `json:"id" db:"id"`
Type int `json:"type" db:"type"`
Address string `json:"address" db:"address"`
Body string `json:"body" db:"body"`
Date time.Time `json:"date" db:"date"`
}
func (app *App) GetMessages(c echo.Context) error {
messages := []Message{}
query := `SELECT * FROM messages WHERE extraction_no=?`
app.db.Unsafe().Select(&messages, query, c.Param("no"))
return c.JSON(http.StatusOK, messages)
}
type MessageStats struct {
Address string `json:"address" db:"address"`
Receive int `json:"receive" db:"receive"`
Send int `json:"send" db:"send"`
}
func (app *App) GetMessagesAnalyses(c echo.Context) error {
messages := []MessageStats{}
query := `SELECT address,
(SELECT COUNT(1) FROM messages m WHERE m.extraction_no=s.extraction_no AND m.address=s.address AND m.type=1) receive,
(SELECT COUNT(1) FROM messages m WHERE m.extraction_no=s.extraction_no AND m.address=s.address AND m.type=2) send
FROM messages s WHERE extraction_no=? GROUP BY address ORDER BY receive + send DESC`
app.db.Select(&messages, query, c.Param("no"))
return c.JSON(http.StatusOK, messages)
}
type Process struct {
UID string `json:"uid" db:"UID"`
PID int `json:"pid" db:"PID"`
PPID int `json:"ppid" db:"PPID"`
STIME string `json:"stime" db:"STIME"`
TIME string `json:"time" db:"TIME"`
CMD string `json:"cmd" db:"CMD"`
}
func (app *App) GetProcesses(c echo.Context) error {
processes := []Process{}
db, err := sqlx.Connect("sqlite3", fmt.Sprintf("data/1/%s", c.Param("file")))
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not open db file")
}
defer db.Close()
query := `SELECT UID, CAST(PID AS INTEGER) PID, CAST(PPID AS INTEGER) PPID, STIME, TIME, CMD FROM process WHERE UID LIKE 'u%' ORDER BY TIME DESC`
db.Select(&processes, query)
return c.JSON(http.StatusOK, processes)
}
type Alarm struct {
ID string `json:"id"`
When time.Time `json:"when"`
History []AlarmHistory `json:"history"`
}
type AlarmHistory struct {
Type string `json:"type"`
When time.Time `json:"when"`
}
func (app *App) GetAlarms(c echo.Context) error {
db, err := sqlx.Connect("sqlite3", fmt.Sprintf("data/1/%s", c.Param("file")))
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not open db file")
}
defer db.Close()
alarms := map[string]Alarm{}
rows, _ := db.Queryx("SELECT * FROM alarm ORDER BY TIME")
for rows.Next() {
var tm string
var typ string
var detail string
rows.Scan(&tm, &typ, &detail)
detail = detail[strings.Index(detail, "{")+1 : strings.Index(detail, "}")]
s := strings.Split(detail, " ")
timestamp, _ := strconv.ParseInt(s[4], 10, 64)
timestamp /= 1000
if _, ok := alarms[s[0]]; !ok {
alarms[s[0]] = Alarm{ID: s[0], When: time.Unix(timestamp, 0)}
}
when, _ := time.Parse("2006-01-02 15:04:05", tm)
alarm := alarms[s[0]]
alarm.History = append(alarms[s[0]].History, AlarmHistory{
Type: typ,
When: when,
})
alarms[s[0]] = alarm
}
results := []Alarm{}
for _, v := range alarms {
results = append(results, v)
}
return c.JSON(http.StatusOK, results)
}
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"github.com/dgrijalva/jwt-go"
"github.com/jmoiron/sqlx"
"github.com/labstack/echo/v4"
)
func (app *App) PostExtractions(c echo.Context) error {
userNo := c.Get("user").(*jwt.Token).Claims.(*AuthClaims).UserNo
form, err := c.FormFile("file")
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Unknown error")
}
src, err := form.Open()
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Unknown error")
}
defer src.Close()
file, err := ioutil.TempFile("", "extraction")
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Unknown error")
}
defer os.Remove(file.Name())
if _, err := io.Copy(file, src); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Unknown error")
}
file.Close()
db, err := sqlx.Connect("sqlite3", file.Name())
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Could not open db file")
}
defer db.Close()
tx, err := app.db.Beginx()
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Unknown error")
}
res, _ := tx.Exec("INSERT INTO extractions (`owner`) VALUES (?)", userNo)
extNo, _ := res.LastInsertId()
rows, err := db.Queryx("SELECT * FROM calllog")
fmt.Println(err)
if err == nil {
for rows.Next() {
vals, _ := rows.SliceScan()
fmt.Println(vals)
_, err = tx.Exec("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?)", append([]interface{}{extNo}, vals...)...)
fmt.Println(err)
}
}
sql := `SELECT
a.packagename, a.name, a.version, a.wifiusage, a.cellularusage,
u.lasttimeused, u.totaltimeforeground
FROM AppInfo a JOIN AppUsageYear u ON a.packagename=u.packagename`
rows, err = db.Queryx(sql)
if err == nil {
for rows.Next() {
vals, _ := rows.SliceScan()
tx.Exec("INSERT INTO apps VALUES (?, ?, ?, ?, ?, ?, ?, ?)", append([]interface{}{extNo}, vals...)...)
}
}
rows, err = db.Queryx("SELECT mid, type, address, body, date FROM sms")
if err == nil {
for rows.Next() {
vals, _ := rows.SliceScan()
tx.Exec("INSERT INTO messages VALUES (?, ?, ?, ?, ?, ?)", append([]interface{}{extNo}, vals...)...)
}
}
rows, err = db.Queryx("SELECT PID, UID, PPID, STIME, TIME, CMD FROM process")
if err == nil {
for rows.Next() {
vals, _ := rows.SliceScan()
tx.Exec("INSERT INTO processes VALUES (?, ?, ?, ?, ?, ?, ?)", append([]interface{}{extNo}, vals...)...)
}
}
/*alarms := map[string]Alarm{}
rows, _ = db.Queryx("SELECT * FROM alarm ORDER BY TIME")
for rows.Next() {
var tm string
var typ string
var detail string
rows.Scan(&tm, &typ, &detail)
detail = detail[strings.Index(detail, "{")+1 : strings.Index(detail, "}")]
s := strings.Split(detail, " ")
timestamp, _ := strconv.ParseInt(s[4], 10, 64)
timestamp /= 1000
if _, ok := alarms[s[0]]; !ok {
alarms[s[0]] = Alarm{ID: s[0], When: time.Unix(timestamp, 0)}
}
when, _ := time.Parse("2006-01-02 15:04:05", tm)
alarm := alarms[s[0]]
alarm.History = append(alarms[s[0]].History, AlarmHistory{
Type: typ,
When: when,
})
alarms[s[0]] = alarm
}
for _, v := range alarms {
tx.Exec("INSERT INTO alarms VALUES (?, ?, ?)", extNo, v.ID, v.When)
for _, h := range v.History {
tx.Exec("INSERT INTO alarm_histories VALUES (?, ?, ?, ?)", extNo, v.ID, h.Type, h.When)
}
}*/
tx.Commit()
return c.NoContent(http.StatusNoContent)
}
module mf-server
go 1.15
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-sql-driver/mysql v1.5.0
github.com/google/uuid v1.1.2
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/jmoiron/sqlx v1.2.0
github.com/labstack/echo v3.3.10+incompatible
github.com/labstack/echo/v4 v4.2.2
github.com/mattn/go-sqlite3 v1.9.0
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
)
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8=
github.com/dgrijalva/jwt-go v1.0.2 h1:KPldsxuKGsS2FPWsNeg9ZO18aCrGKujPoWXn2yo+KQM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/goccy/go-json v0.4.8 h1:TfwOxfSp8hXH+ivoOk36RyDNmXATUETRdaNWDaZglf8=
github.com/goccy/go-json v0.4.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/labstack/echo v1.4.4 h1:1bEiBNeGSUKxcPDGfZ/7IgdhJJZx8wV/pICJh4W2NJI=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/echo/v4 v4.2.2 h1:bq2fdZCionY1jck8rzUpQEu2YSmI8QbX6LHrCa60IVs=
github.com/labstack/echo/v4 v4.2.2/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lestrrat-go/backoff/v2 v2.0.7/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
github.com/lestrrat-go/codegen v1.0.0/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
github.com/lestrrat-go/jwx v1.1.7 h1:+PNt2U7FfrK4xn+ZCG+9jPRq5eqyG30gwpVwcekrCjA=
github.com/lestrrat-go/jwx v1.1.7/go.mod h1:Tg2uP7bpxEHUDtuWjap/PxroJ4okxGzkQznXiG+a5Dc=
github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/pdebug/v3 v3.0.1/go.mod h1:za+m+Ve24yCxTEhR59N7UlnJomWwCiIqbJRmKeiADU4=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8=
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package main
import (
"log"
)
func main() {
config, err := LoadConfig("config.json")
if err != nil {
log.Fatal(err)
}
app := NewApp(config)
app.Serve()
}
package main
import (
"time"
)
type Time time.Time
func (t *Time) Scan(v interface{}) error {
p, err := time.Parse("2006-01-02 15:04:05", string(v.([]byte)))
*t = Time(p)
return err
}
func (t *Time) MarshalJSON() ([]byte, error) {
return time.Time(*t).MarshalJSON()
}
package main
import (
"context"
"net/http"
"strings"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/go-sql-driver/mysql"
"github.com/labstack/echo/v4"
"golang.org/x/crypto/sha3"
)
type User struct {
No uint64 `json:"no"`
ID string `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
ExpiredAt time.Time `json:"expired_at"`
}
func (app *App) PostUsers(c echo.Context) error {
body := struct {
ID string `json:"id"`
Password string `json:"password"`
Name string `json:"name"`
}{}
if c.Bind(&body) != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Failed to parse request json")
}
hash := sha3.Sum256([]byte(body.Password))
res, err := app.db.Exec("INSERT INTO users (`id`, `password`, `name`) VALUES (?, ?, ?)", body.ID, hash[:], body.Name)
if err != nil {
if merr, ok := err.(*mysql.MySQLError); ok {
if merr.Number == 1062 {
return echo.NewHTTPError(http.StatusConflict, "Already registered")
}
}
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to register")
}
no, _ := res.LastInsertId()
return c.JSON(http.StatusOK, echo.Map{"user_no": no})
}
type AuthClaims struct {
UserNo uint64 `json:"user_no"`
jwt.StandardClaims
}
func (app *App) PostTokens(c echo.Context) error {
body := struct {
ID string `json:"id"`
Password string `json:"password"`
}{}
if c.Bind(&body) != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Failed to parse request json")
}
hash := sha3.Sum256([]byte(body.Password))
rows, err := app.db.Query("SELECT `no` FROM users WHERE `id`=? AND `password`=?", body.ID, hash[:])
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to register")
}
if !rows.Next() {
return echo.NewHTTPError(http.StatusUnauthorized, "Login failed")
}
no := uint64(0)
rows.Scan(&no)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, AuthClaims{UserNo: no})
auth, err := token.SignedString([]byte(app.Config.TokenSecret))
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Login failed")
}
return c.JSON(http.StatusOK, map[string]interface{}{"token": auth})
}
func (app *App) Authenticate(next func(http.ResponseWriter, *http.Request)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization")
if len(auth) > 6 && strings.Index(auth, "Bearer ") == 0 {
token, err := jwt.ParseWithClaims(auth[7:], &AuthClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(app.Config.TokenSecret), nil
})
if err == nil {
claims := token.Claims.(*AuthClaims)
ctx := context.WithValue(r.Context(), PropUserNo, claims.UserNo)
next(w, r.WithContext(ctx))
return
}
}
WriteError(w, http.StatusUnauthorized, "Authorization failed")
})
}
package main
import (
"encoding/json"
"net/http"
)
func WriteJson(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)
}
func WriteError(w http.ResponseWriter, status int, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(map[string]interface{}{"msg": message})
}