├── .gitignore
├── .vscode
└── launch.json
├── Dockerfile
├── LICENSE
├── api
├── response
│ └── response.go
├── route.go
└── v1
│ ├── articles.go
│ ├── authentication.go
│ ├── commands.go
│ ├── oauth.go
│ ├── roles.go
│ ├── upload.go
│ └── users.go
├── backend.dockerfile
├── config.yml
├── db
├── gorm.go
├── init_db.sql
└── migration.go
├── docker-compose.override.yml
├── docker-compose.yml
├── docs
├── docs.go
├── swagger.json
└── swagger.yaml
├── document
├── API.adoc
├── API.html
├── images
│ ├── goyangi-howitworks.png
│ ├── goyangi-stack.png
│ ├── goyangi-vuejs-board-kr.JPG
│ ├── goyangi-vuejs-board.JPG
│ └── goyangi-vuejs-home.JPG
├── packages
│ ├── backend.md
│ └── frontend.md
└── stylesheets
│ ├── asciidoctor.css
│ ├── colony.css
│ ├── foundation-lime.css
│ ├── foundation-potion.css
│ ├── foundation.css
│ ├── github.css
│ ├── golo.css
│ ├── iconic.css
│ ├── maker.css
│ ├── readthedocs.css
│ ├── riak.css
│ ├── rocket-panda.css
│ └── rubygems.css
├── form
└── common.go
├── frontend
└── vuejs
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── babel.config.js
│ ├── nginx-backend-not-found.conf
│ ├── nginx.conf
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ └── index.html
│ ├── src
│ ├── api
│ │ ├── ArticleAPI.js
│ │ ├── AuthenticationAPI.js
│ │ ├── BaseAPI.js
│ │ ├── UserAPI.js
│ │ └── index.js
│ ├── assets
│ │ ├── goyangi.jpg
│ │ └── logo.png
│ ├── components
│ │ ├── articles
│ │ │ ├── Form.vue
│ │ │ ├── Item.vue
│ │ │ └── List.vue
│ │ ├── comments
│ │ │ ├── Form.vue
│ │ │ ├── List.vue
│ │ │ └── index.vue
│ │ └── likings
│ │ │ └── index.vue
│ ├── layouts
│ │ └── BasicLayout.vue
│ ├── locales
│ │ ├── en
│ │ │ └── translation.json
│ │ └── ko
│ │ │ └── translation.json
│ ├── main.js
│ ├── router
│ │ ├── articles
│ │ │ └── index.js
│ │ ├── index.js
│ │ └── users
│ │ │ └── index.js
│ ├── settings
│ │ ├── api_base.js
│ │ └── i18next.js
│ ├── store
│ │ ├── actions.js
│ │ ├── getters.js
│ │ ├── index.js
│ │ ├── modules
│ │ │ ├── articles
│ │ │ │ ├── actions.js
│ │ │ │ ├── getters.js
│ │ │ │ ├── index.js
│ │ │ │ └── mutations.js
│ │ │ ├── authentications
│ │ │ │ ├── actions.js
│ │ │ │ ├── getters.js
│ │ │ │ ├── index.js
│ │ │ │ └── mutations.js
│ │ │ ├── languages
│ │ │ │ ├── actions.js
│ │ │ │ ├── getters.js
│ │ │ │ ├── index.js
│ │ │ │ └── mutations.js
│ │ │ └── users
│ │ │ │ ├── actions.js
│ │ │ │ ├── getters.js
│ │ │ │ ├── index.js
│ │ │ │ └── mutations.js
│ │ ├── mutation-types.js
│ │ ├── mutations.js
│ │ └── state.js
│ └── views
│ │ ├── App.vue
│ │ ├── Home.vue
│ │ ├── articles
│ │ └── index.vue
│ │ └── users
│ │ ├── Login.vue
│ │ └── Registration.vue
│ └── yarn.lock
├── go.mod
├── go.sum
├── log
└── .gitignore
├── main.go
├── model
├── app.go
├── article.go
├── common.go
├── file.go
├── language.go
├── role.go
└── user.go
├── readme.md
├── readme_ko.md
├── script
├── commands.go
├── constant.go
├── generateAPI.go
├── init.go
├── script.go
├── server.go
└── test.go
├── server
├── exec
│ └── main.go
└── server.go
├── service
├── articleService
│ ├── article.go
│ ├── comment.go
│ ├── form.go
│ ├── liking.go
│ └── share.go
├── commentService
│ ├── comment.go
│ └── form.go
├── doc.go
├── likingService
│ ├── form.go
│ └── liking.go
├── oauthService
│ ├── common.go
│ ├── facebook.go
│ ├── github.go
│ ├── google.go
│ ├── kakao.go
│ ├── linkedin.go
│ ├── model.go
│ ├── naver.go
│ ├── twitter.go
│ └── yahoo.go
├── roleService
│ ├── form.go
│ └── role.go
├── uploadService
│ ├── file.go
│ ├── form.go
│ └── image.go
└── userService
│ ├── authentication.go
│ ├── cookie.go
│ ├── form.go
│ ├── locale
│ ├── en-us.all.json
│ └── ko-kr.all.json
│ ├── password.go
│ ├── user.go
│ ├── userLiking
│ └── liking.go
│ ├── userPermission
│ └── permission.go
│ └── verification.go
└── util
├── authHelper
├── app.go
├── authHelper_suite_test.go
├── authHelper_test.go
├── header.go
└── user.go
├── aws
├── aws.go
├── elasticache.go
└── s3.go
├── concurrency
├── concurrency.go
├── concurrency_suite_test.go
└── concurrency_test.go
├── crypto
├── crypto_suite_test.go
├── md5.go
├── md5_test.go
├── token.go
└── token_test.go
├── email
└── email.go
├── file
├── file.go
├── file_suite_test.go
└── file_test.go
├── ginHelper
└── request.go
├── httpHelper
└── http.go
├── image
├── image.go
├── image_suite_test.go
├── image_test.go
└── testdata
│ ├── cat.gif
│ ├── cat.jpg
│ └── cat.png
├── interfaceHelper
├── interfaceHelper.go
├── interfaceHelper_suite_test.go
└── interfaceHelper_test.go
├── jwt
├── jwt.go
├── jwt_suite_test.go
└── jwt_test.go
├── log
├── error.go
├── ginLogger.go
└── logger.go
├── modelHelper
├── modelHelper.go
├── modelHelper_suite_test.go
└── modelHelper_test.go
├── oauth2
├── facebook
│ └── facebook.go
├── github
│ └── github.go
├── google
│ └── google.go
├── helper.go
├── kakao
│ └── kakao.go
├── linkedin
│ └── linkedin.go
├── naver
│ └── naver.go
├── oauth2.go
├── twitter
│ └── twitter.go
└── yahoo
│ └── yahoo.go
├── pagination
├── pagination.go
├── pagination_suite_test.go
└── pagination_test.go
├── random
├── random.go
├── random_suite_test.go
└── random_test.go
├── redis
├── cache.go
├── redis.go
├── redis_suite_test.go
└── redis_test.go
├── retrieveHelper
├── app.go
└── user.go
├── stringHelper
├── stringHelper.go
├── stringHelper_suite_test.go
└── stringHelper_test.go
├── timeHelper
├── timeHelper.go
├── timeHelper_suite_test.go
└── timeHelper_test.go
├── upload
└── image.go
├── userHelper
└── user.go
├── validation
├── validation.go
├── validation_suite_test.go
└── validation_test.go
└── viper
└── viper.go
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .webassets-cache
3 | .DS_Store
4 | *.log
5 | *.pid
6 | *.bak
7 | *.css~
8 | *.swp
9 | *.zip
10 | *.coverprofile
11 | node_modules
12 | *.private
13 | .vendor
14 | vendor
15 | vendor/*
16 | gin-bin
17 | *.legacy
18 | .vscode
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch",
6 | "type": "go",
7 | "request": "launch",
8 | "mode": "debug",
9 | "remotePath": "",
10 | "port": 2345,
11 | "host": "127.0.0.1",
12 | "program": "${workspaceRoot}",
13 | "env": {},
14 | "args": [],
15 | "showLog": true
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use the offical Golang image to create a build artifact.
2 | FROM golang:1.20-alpine as builder
3 |
4 | RUN apk update && apk upgrade && \
5 | apk --update add git make bash build-base
6 |
7 | # RUN apt-get update && apt-get upgrade
8 | # RUN apt-get -y install git make bash
9 |
10 | # Set destination for COPY
11 | WORKDIR /app
12 |
13 | # Download Go modules
14 | COPY go.mod go.sum ./
15 | RUN go mod download
16 |
17 | # Set PKG_CONFIG_PATH for libvips
18 | # ENV PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
19 | # RUN pkg-config --libs vips
20 |
21 | COPY . .
22 |
23 | # Build the command inside the container.
24 | # RUN CGO_ENABLED=0 GOOS=linux go build -v -o app main.go # with this option, I got 'executor failed' error
25 | RUN go build -v -o backend main.go
26 |
27 | # Distribution
28 | FROM alpine:latest
29 |
30 | RUN apk update && apk upgrade && \
31 | apk --update --no-cache add tzdata && \
32 | mkdir /app
33 |
34 | WORKDIR /app
35 |
36 | # EXPOSE 9090
37 |
38 | COPY --from=builder /app /app
39 |
40 | CMD ["/app/backend"]
41 |
42 | # Use a Docker multi-stage build to create a lean production image.
43 | # FROM gcr.io/distroless/base-debian11
44 |
45 | # RUN apt-get update && apt-get upgrade && \
46 | # apt-get install -y libvips-dev --no-install-recommends \
47 | # mkdir /app
48 |
49 | # COPY --from=builder /app/ .
50 |
51 | # Run the service binary.
52 | # CMD ["/app"]
53 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2015 JoongSeob Vito Kim
2 | All Rights Reserved.
3 |
4 | MIT LICENSE
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | this software and associated documentation files (the "Software"), to deal in
8 | the Software without restriction, including without limitation the rights to
9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | the Software, and to permit persons to whom the Software is furnished to do so,
11 | subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/api/route.go:
--------------------------------------------------------------------------------
1 | // @APIVersion 1.0.0
2 | // @Title Goyangi API
3 | // @Description Goyangi API usually works as expected. But sometimes its not true
4 | // @Contact api@contact.me
5 | // @TermsOfServiceUrl http://google.com/
6 | // @License BSD
7 | // @LicenseUrl http://opensource.org/licenses/BSD-2-Clause
8 | // @SubApi Authentication [/authentications]
9 | // @SubApi Users [/users]
10 | // @SubApi Oauth [/oauth]
11 | // @SubApi Roles [/roles]
12 | // @SubApi Articles [/articles]
13 | // @SubApi Upload [/upload]
14 | // @SubApi Commands [/commands]
15 | package api
16 |
17 | import (
18 | "github.com/gin-gonic/gin"
19 | "github.com/spf13/viper"
20 |
21 | v1 "github.com/dorajistyle/goyangi/api/v1"
22 | )
23 |
24 | // RouteAPI contains router groups for API
25 | func RouteAPI(parentRoute *gin.Engine) {
26 |
27 | route := parentRoute.Group(viper.GetString("api.url"))
28 | {
29 | v1.Users(route)
30 | v1.Roles(route)
31 | v1.Authentications(route)
32 | v1.Articles(route)
33 | v1.Upload(route)
34 | v1.Commands(route)
35 | v1.Oauth(route)
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/api/v1/authentication.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 |
6 | "github.com/dorajistyle/goyangi/api/response"
7 | "github.com/dorajistyle/goyangi/service/userService"
8 | "github.com/dorajistyle/goyangi/util/log"
9 | )
10 |
11 | // @Title Authentications
12 | // @Description Authentications's router group.
13 | func Authentications(parentRoute *gin.RouterGroup) {
14 | route := parentRoute.Group("/authentications")
15 | route.POST("", createUserAuthentication)
16 | route.DELETE("", deleteUserAuthentication)
17 | }
18 |
19 | // @Title createUserAuthentication
20 | // @Description Create a user session.
21 | // @Accept json
22 | // @Param loginEmail formData string true "User email."
23 | // @Param loginPassword formData string true "User password."
24 | // @Success 201 {object} response.BasicResponse "User authentication created"
25 | // @Failure 401 {object} response.BasicResponse "Password incorrect"
26 | // @Failure 404 {object} response.BasicResponse "User is not found"
27 | // @Resource /authentications
28 | // @Router /authentications [post]
29 | func createUserAuthentication(c *gin.Context) {
30 | status, err := userService.CreateUserAuthentication(c)
31 | messageTypes := &response.MessageTypes{OK: "login.done",
32 | Unauthorized: "login.error.passwordIncorrect",
33 | NotFound: "login.error.userNotFound"}
34 | messages := &response.Messages{OK: "User logged in successfully."}
35 | log.Debugf("header : %s", c.Request)
36 | response.JSON(c, status, messageTypes, messages, err)
37 | }
38 |
39 | // @Title deleteUserAuthentication
40 | // @Description Delete a user session.
41 | // @Accept json
42 | // @Success 200 {object} response.BasicResponse "User logged out successfully"
43 | // @Resource /authentications
44 | // @Router /authentications [delete]
45 | func deleteUserAuthentication(c *gin.Context) {
46 | status, err := userService.ClearCookie(c)
47 | messageTypes := &response.MessageTypes{OK: "logout.done"}
48 | messages := &response.Messages{OK: "User logged out successfully."}
49 | response.JSON(c, status, messageTypes, messages, err)
50 | }
51 |
--------------------------------------------------------------------------------
/api/v1/commands.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "github.com/dorajistyle/goyangi/db"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | // @Title Commands
9 | // @Description Commands' router group. It contains command API.
10 | func Commands(parentRoute *gin.RouterGroup) {
11 | route := parentRoute.Group("/commands")
12 | route.GET("migrate", migrate)
13 | }
14 |
15 | func migrate(c *gin.Context) {
16 | db.Migrate()
17 | }
--------------------------------------------------------------------------------
/backend.dockerfile:
--------------------------------------------------------------------------------
1 | # Use the offical Golang image to create a build artifact.
2 | FROM golang:1.20-alpine as builder
3 |
4 | RUN apk update && apk upgrade && \
5 | apk --update add git make bash build-base
6 |
7 | # RUN apt-get update && apt-get upgrade
8 | # RUN apt-get -y install git make bash
9 |
10 | # Set destination for COPY
11 | WORKDIR /app
12 |
13 | # Download Go modules
14 | COPY go.mod go.sum ./
15 | RUN go mod download
16 |
17 | # Set PKG_CONFIG_PATH for libvips
18 | # ENV PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig
19 | # RUN pkg-config --libs vips
20 |
21 | COPY . .
22 |
23 | # Build the command inside the container.
24 | # RUN CGO_ENABLED=0 GOOS=linux go build -v -o app main.go # with this option, I got 'executor failed' error
25 | RUN go build -v -o goyangi-backend main.go
26 |
27 | # Distribution
28 | FROM golang:1.20-alpine as prod
29 |
30 | RUN apk update && apk upgrade && \
31 | apk --update --no-cache add tzdata && \
32 | mkdir /app
33 |
34 | WORKDIR /app
35 |
36 | # EXPOSE 9090
37 |
38 | COPY --from=builder /app /app
39 |
40 | ENTRYPOINT ["/app/goyangi-backend"]
41 |
42 | # Use a Docker multi-stage build to create a lean production image.
43 | # FROM gcr.io/distroless/base-debian11
44 |
45 | # RUN apt-get update && apt-get upgrade && \
46 | # apt-get install -y libvips-dev --no-install-recommends \
47 | # mkdir /app
48 |
49 | # COPY --from=builder /app/ .
50 |
51 | # Run the service binary.
52 | # CMD ["/app"]
53 |
--------------------------------------------------------------------------------
/config.yml:
--------------------------------------------------------------------------------
1 | # App
2 | app:
3 | name: Goyangi
4 | environment: DEVELOPMENT
5 |
6 | api:
7 | hostURL: "http://localhost"
8 | url: "api/v1"
9 |
10 | # Gin
11 | gin:
12 | port: 8080
13 | mode: debug
14 |
15 | # Database
16 | database:
17 | type: postgres
18 | # host: 127.0.0.1 # when you directly run 'go run main.go'
19 | host: host.docker.internal # for docker
20 | port: 5432
21 | user: postgres
22 | password: 1234
23 | database: goyangi_dev
24 | options: "sslmode=disable"
25 |
26 | # db table public fields
27 | publicFields:
28 | user: "id, username, md5, description, created_at, liking_count, liked_count"
29 |
30 | # db table select pagination
31 | pagination:
32 | article: 10
33 | liking: 5
34 | liked: 5
35 | comment: 5
36 |
37 | # db table select order
38 | order:
39 | article: "created_at desc"
40 | liking: "created_at desc"
41 | liked: "created_at desc"
42 | comment: "created_at desc"
43 |
44 |
45 | # Redis
46 | redis:
47 | addr: "172.19.102.44"
48 | port: "6379"
49 |
50 | # Upload
51 | upload:
52 | path:
53 | local: "/tmp/upload/"
54 | S3Image: "images/"
55 | target: "LOCAL" # LOCAL | S3
56 | bucket: "TEST" # TEST | PRODUCTION
57 | timeout: 30
58 |
59 | auth:
60 | tokenExpiration: 1000 # Unit: day
61 |
62 | aws:
63 | accessKeyID: ""
64 | secretAccessKey: ""
65 | s3:
66 | region: ""
67 | bucket:
68 | prefix: ""
69 | name: ""
70 | testBucket:
71 | prefix: ""
72 | name: ""
73 | staticBucket:
74 | name: ""
75 | elasticbeanstalk:
76 | region: ""
77 | appName: ""
78 | environment:
79 | id: ""
80 | name: ""
81 | s3:
82 | bucket:
83 | name: ""
84 | cloudfrontURL: ""
85 |
86 |
87 | # Email
88 | email:
89 | send: false # true | false
90 | from: ""
91 | testTo: ""
92 | host: ""
93 | username: ""
94 | password: ""
95 | port: 465
96 | timeout: 10 # seconds
97 |
98 | # Image
99 | image:
100 | default:
101 | width: 500
102 | height: 500
103 | large:
104 | width: 1920
105 | height: 1080
106 | medium:
107 | width: 1024
108 | height: 768
109 | thumbnail:
110 | width: 340
111 | height: 270
112 |
113 | # Log
114 | log:
115 | access:
116 | filepath: "log/access.txt"
117 | maxSize: 5 # megabytes
118 | maxBackups: 7
119 | maxAge: 30 # days
120 | error:
121 | filepath: "log/error.json"
122 | maxSize: 5 # megabytes
123 | maxBackups: 7
124 | maxAge: 30 # days
125 |
126 | # Oauth
127 | oauth:
128 | google:
129 | id: ""
130 | secret: ""
131 | github:
132 | id: ""
133 | secret: ""
134 | facebook:
135 | id: ""
136 | secret: ""
137 | twitter:
138 | id: ""
139 | secret: ""
140 | linkedin:
141 | id: ""
142 | secret: ""
143 | kakao:
144 | id: ""
145 | secret: ""
146 | naver:
147 | id: ""
148 | secret: ""
149 | yahoo:
150 | id: ""
151 | secret: ""
152 |
153 | # JWT
154 | jwt:
155 | client:
156 | method: HMAC256 # HMAC256 | RSA256
157 | key:
158 | private: "secretRSA" # secret
159 | public: "secretRSAPUBLIC"
160 |
161 | server:
162 | method: HMAC256 # HMAC256 | RSA256
163 | expirationHour: 24 # Hours
164 | key:
165 | private: > # secret
166 | -----BEGIN RSA PRIVATE KEY-----
167 | -----END RSA PRIVATE KEY-----
168 | public: >
169 | -----BEGIN PUBLIC KEY-----
170 | -----END PUBLIC KEY-----
--------------------------------------------------------------------------------
/db/gorm.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "github.com/dorajistyle/goyangi/util/log"
5 | "github.com/spf13/viper"
6 |
7 | // _ "github.com/go-sql-driver/mysql"
8 | "github.com/jinzhu/gorm"
9 | _ "github.com/lib/pq"
10 | )
11 |
12 | var ORM *gorm.DB
13 |
14 | // GormInit init gorm ORM.
15 | func GormInit() {
16 | appEnvironmnet := viper.GetString("app.environment")
17 | dbType := viper.GetString("database.type")
18 | dbHost := viper.GetString("database.host")
19 | dbPort := viper.GetString("database.port")
20 | dbUser := viper.GetString("database.user")
21 | dbPassword := viper.GetString("database.password")
22 | dbDatabase := viper.GetString("database.database")
23 | dbOptions := viper.GetString("database.options")
24 |
25 | var databaseDSL string
26 |
27 | switch dbType {
28 | case "postgres":
29 | databaseDSL = "host=" + dbHost + " port=" + dbPort + " dbname=" + dbDatabase + " " + dbOptions + " user=" + dbUser + " password=" + dbPassword
30 | case "mysql":
31 | databaseDSL = dbUser + ":" + dbPassword + "@tcp(" + dbHost + ":" + dbPort + ")/" + dbDatabase + "?" + dbOptions
32 |
33 | }
34 | println(databaseDSL)
35 | db, err := gorm.Open(dbType, databaseDSL)
36 |
37 | db.DB()
38 |
39 | // Then you could invoke `*sql.DB`'s functions with it
40 | db.DB().Ping()
41 | db.DB().SetMaxIdleConns(10)
42 | db.DB().SetMaxOpenConns(100)
43 |
44 | // Disable table name's pluralization
45 | // db.SingularTable(true)
46 | if appEnvironmnet == "DEVELOPMENT" {
47 | db.LogMode(true)
48 | }
49 | log.CheckError(err)
50 | ORM = db
51 | }
52 |
--------------------------------------------------------------------------------
/db/init_db.sql:
--------------------------------------------------------------------------------
1 | CREATE DATABASE goyangi;
2 | CREATE DATABASE goyangi_dev;
3 | CREATE DATABASE goyangi_test;
--------------------------------------------------------------------------------
/db/migration.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "time"
5 | "github.com/dorajistyle/goyangi/model"
6 | )
7 |
8 | func Migrate() {
9 | MigrateUserAndRole()
10 | ORM.AutoMigrate(&model.Article{}, &model.CommentList{}, &model.Comment{}, &model.LikingList{}, &model.LikedList{}, &model.Image{}, &model.Tag{}, &model.Link{}, &model.App{}, &model.ExpiredTokenLog{})
11 | ORM.AutoMigrate(&model.File{}, &model.Language{})
12 | ORM.AutoMigrate(&model.UsersFollowers{}, &model.Connection{})
13 | }
14 |
15 | func MigrateUserAndRole() {
16 | var adminRole model.Role
17 | var userRole model.Role
18 |
19 | hasRole := ORM.HasTable(&model.Role{})
20 | hasUser := ORM.HasTable(&model.User{})
21 |
22 | if !hasRole {
23 | ORM.CreateTable(&model.Role{})
24 | adminRole.Name = "admin"
25 | adminRole.Description = "administrator"
26 | ORM.Create(&adminRole)
27 | userRole.Name = "user"
28 | userRole.Description = "ordinary user"
29 | ORM.Create(&userRole)
30 | }
31 |
32 | if !hasUser {
33 | ORM.CreateTable(&model.User{})
34 | var user model.User
35 | user.Username = "admin"
36 | user.Email = "admin@goyangi.github.io"
37 | user.Password = "$2a$10$voqxhv08H2eWHbLJo2rEeO1GwGlg8ZLW3Y8348aqe0XBqVgEZxGOu" // password
38 | user.Name = "Goyangi"
39 | user.Birthday = time.Now()
40 | user.Gender = 2
41 | user.Md5 = "10d17498672e2dd040e8c0cf5a337a61"
42 | user.Activation = true
43 | user.Token = "168355cf5b6d31827c694260ab24e3bc3e990290ca94c7c30c6489ae1c1f212c"
44 | user.TokenExpiration = time.Now().Add(time.Hour * 24 * 365 * 100)
45 | ORM.Create(&user)
46 | ORM.Model(&user).Association("Roles").Append(adminRole)
47 | ORM.Model(&user).Association("Roles").Append(userRole)
48 | }
49 | }
--------------------------------------------------------------------------------
/docker-compose.override.yml:
--------------------------------------------------------------------------------
1 | services:
2 |
3 | proxy:
4 | ports:
5 | - "80:80"
6 | command:
7 | # Enable Docker in Traefik, so that it reads labels from Docker services
8 | - --providers.docker
9 | # Add a constraint to only use services with the label for this stack
10 | # from the env var TRAEFIK_TAG
11 | - --providers.docker.constraints=Label(`traefik.constraint-label-stack`, `localhost`)
12 | # Do not expose all Docker services, only the ones explicitly exposed
13 | - --providers.docker.exposedbydefault=false
14 | # Disable Docker Swarm mode for local development
15 | # - --providers.docker.swarmmode
16 | # Enable the access log, with HTTP requests
17 | - --accesslog
18 | # Enable the Traefik log, for configurations and errors
19 | - --log
20 | # Enable the Dashboard and API
21 | - --api
22 | # Enable the Dashboard and API in insecure mode for local development
23 | - --api.insecure=true
24 | labels:
25 | - traefik.enable=true
26 | - traefik.http.routers.goyangi-traefik-public-http.rule=Host(`localhost`)
27 | - traefik.http.services.goyangi-traefik-public.loadbalancer.server.port=80
28 |
29 | db:
30 | ports:
31 | - "5432:5432"
32 |
33 | pgadmin:
34 | ports:
35 | - "5050:5050"
36 |
37 | backend:
38 | environment:
39 | - SERVER_HOST=http://localhost
40 | depends_on:
41 | - db
42 | build:
43 | dockerfile: backend.dockerfile
44 | target: prod
45 | args:
46 | INSTALL_DEV: ${INSTALL_DEV-true}
47 | labels:
48 | - traefik.enable=true
49 | - traefik.constraint-label-stack=localhost
50 | - traefik.http.routers.goyangi-backend-http.rule=PathPrefix(`/api`) || PathPrefix(`/swagger`)
51 | - traefik.http.services.goyangi-backend.loadbalancer.server.port=80
52 |
53 | frontend:
54 | build:
55 | context: ./frontend/vuejs
56 | args:
57 | FRONTEND_ENV: dev
58 | labels:
59 | - traefik.enable=true
60 | - traefik.constraint-label-stack=localhost
61 | - traefik.http.routers.goyangi-frontend-http.rule=PathPrefix(`/`)
62 | - traefik.http.services.goyangi-frontend.loadbalancer.server.port=80
63 |
64 | networks:
65 | traefik-public:
66 | # For local dev, don't expect an external Traefik network
67 | external: false
68 |
--------------------------------------------------------------------------------
/document/images/goyangi-howitworks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/document/images/goyangi-howitworks.png
--------------------------------------------------------------------------------
/document/images/goyangi-stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/document/images/goyangi-stack.png
--------------------------------------------------------------------------------
/document/images/goyangi-vuejs-board-kr.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/document/images/goyangi-vuejs-board-kr.JPG
--------------------------------------------------------------------------------
/document/images/goyangi-vuejs-board.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/document/images/goyangi-vuejs-board.JPG
--------------------------------------------------------------------------------
/document/images/goyangi-vuejs-home.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/document/images/goyangi-vuejs-home.JPG
--------------------------------------------------------------------------------
/document/packages/backend.md:
--------------------------------------------------------------------------------
1 | ### Packages
2 | * [golang.org/x/crypto/bcrypt](https://godoc.org/golang.org/x/crypto/bcrypt)[[code](https://github.com/golang/crypto/)], It implements Provos and Mazières's bcrypt adaptive hashing algorithm.
3 | * [gorilla/securecookie](http://www.gorillatoolkit.org/pkg/securecookie)[[code](https://github.com/gorilla/securecookie)], It encodes and decodes authenticated and optionally encrypted cookie values.
4 | * [gin-gonic/gin](https://gin-gonic.github.io/gin/)[[code](https://github.com/gin-gonic/gin)], Gin is a web framework written in Golang. If you need performance and good productivity, you will love Gin.
5 | * [go-sql-driver/mysql](http://godoc.org/github.com/go-sql-driver/mysql)[[code](https://github.com/go-sql-driver/mysql)], A MySQL-Driver for Go's database/sql package
6 | * [jinzhu/gorm](https://godoc.org/github.com/jinzhu/gorm)[[code](https://github.com/jinzhu/gorm)], The fantastic ORM library for Golang, aims to be developer friendly.
7 | * [uber-go/zap](https://github.com/uber-go/zap)[[code](https://github.com/uber-go/zap)], Blazing fast, structured, leveled logging in Go.
8 | * [onsi/ginkgo](http://onsi.github.io/ginkgo/)[[code](https://github.com/onsi/ginkgo/)], BDD Testing Framework for Go.
9 | * [onsi/gomega](http://onsi.github.io/gomega/)[[code](https://github.com/onsi/gomega)], Ginkgo's Preferred Matcher Library
10 | * [goamz/goamz](http://godoc.org/github.com/goamz/goamz)[[code](https://github.com/goamz/goamz)], It enables Go programs to interact with Amazon Web Services.
11 | * [disintegration/gift](http://godoc.org/github.com/disintegration/gift)[[code](https://github.com/disintegration/gift)], It provides a set of useful image processing filters.
12 | * [gopkg.in/gomail.v1](https://gopkg.in/gomail.v1)[[code](https://github.com/go-gomail/gomail/tree/v1)], A simple and powerful Go package to send emails.
13 | * [golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2)[[code](https://github.com/golang/oauth2)], It contains a client implementation for OAuth 2.0 spec.
14 | * [nicksnyder/go-i18n](https://godoc.org/github.com/nicksnyder/go-i18n)[[code](https://github.com/nicksnyder/go-i18n)], It is a Go package and a command that can be used to translate Go programs into multiple languages.
15 | * [DAddYE/vips](https://godoc.org/github.com/DAddYE/vips)[[code](https://github.com/DAddYE/vips)], It is powered by the blazingly fast libvips image processing library, originally created in 1989 at Birkbeck College and currently maintained by JohnCupitt.
16 | * [natefinch/lumberjack.v2](https://gopkg.in/natefinch/lumberjack.v2)[[code](https://github.com/natefinch/lumberjack/tree/v2.0)], It is a Go package for writing logs to rolling files.
17 | * [codegangsta/cli](https://godoc.org/github.com/codegangsta/cli)[[code](https://github.com/codegangsta/cli)], It is simple, fast, and fun package for building command line apps in Go.
18 | * [codegangsta/gin](https://godoc.org/github.com/codegangsta/gin)[[code](https://github.com/codegangsta/gin)], Live reload utility for Go web servers
19 | * [nitrous-io/goop](https://godoc.org/github.com/nitrous-io/goop)[[code](https://github.com/nitrous-io/goop)], A dependency manager for Go (golang), inspired by Bundler.
20 | * []()[[code]()],
21 |
--------------------------------------------------------------------------------
/document/packages/frontend.md:
--------------------------------------------------------------------------------
1 | ## CanJS - Legacy
2 |
3 | ### Libraries
4 | * [Canjs](http://canjs.com/), Client-side JavaScript Framework.
5 | * [RequireJS](http://requirejs.org/), JavaScript file and module loader.
6 | * [RequireJS Optimizer](http://requirejs.org/docs/optimization.html), Optimization tool of RequireJS.
7 | * [RequireJS plugins](https://github.com/millermedeiros/requirejs-plugins), Small set of plugins for RequireJS.
8 | * [Initializr](http://www.initializr.com/), HTML5 templates generator.
9 | * [UIkit](http://getuikit.com/), A lightweight and modular front-end framework for developing fast and powerful web interfaces.
10 | * [Hammer](http://hammerjs.github.io/), A open-source library that can recognize gestures made by touch, mouse and pointerEvents.
11 | * [Moment.js](http://momentjs.com/), Parse, validate, manipulate, and display dates in JavaScript.
12 | * [mustache](http://mustache.github.io/), Logic-less templates.
13 | * [i18next](http://i18next.com/), JavaScript translating toolkit.
14 | * [loglevel](https://github.com/pimterry/loglevel), Minimal lightweight simple logging for JavaScript.
15 | * [typeahead.js](https://github.com/twitter/typeahead.js), It is a fast and fully-featured autocomplete library.
16 | * [spin.js](http://fgnass.github.io/spin.js/), It dynamically creates spinning activity indicators that can be used as resolution-independent replacement for AJAX loading GIFs.
17 | * [Placeholders.js](http://jamesallardice.github.io/Placeholders.js/), It is a JavaScript polyfill for the HTML5 placeholder attribute.
18 | * [Magnific Popup](http://dimsemenov.com/plugins/magnific-popup/), A responsive lightbox & dialog script with focus on performance and providing best experience for user with any device.
19 | * [Trumbowyg](http://alex-d.github.io/Trumbowyg/), A light, translatable and customisable jQuery plugin.
20 | * [jQuery](http://jquery.com/), It is a fast, small, and feature-rich JavaScript library.
21 | * [jQuery BBQ](http://benalman.com/projects/jquery-bbq-plugin/), It leverages the HTML5 hashchange event to allow simple, yet powerful bookmarkable #hash history.
22 | * [jQuery File Upload](https://blueimp.github.io/jQuery-File-Upload/), File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery.
23 | * [googlemaps-amd](https://github.com/aerisweather/googlemaps-amd) Google Maps AMD Loader Plugin.
24 |
25 | ### Management
26 | * [Bower](http://bower.io/), A package manager for the web
27 | * [Gulp](http://gulpjs.com/), Automate and enhance your workflow.
28 | * [gulp-util](https://github.com/gulpjs/gulp-util), Utilities for gulp plugins.
29 | * [gulp-uncss](https://github.com/ben-eb/gulp-uncss), Remove unused CSS selectors.
30 | * [yargs](https://github.com/bcoe/yargs), The modern, pirate-themed successor to optimist.
31 | * [gulp-concat](https://github.com/wearefractal/gulp-concat), Streaming concat middleware for gulp.
32 | * [gulp-uglify](https://github.com/terinjokes/gulp-uglify), Minify files with UglifyJS
33 | * [gulp-replace](https://github.com/lazd/gulp-replace), A string replace plugin for gulp.
34 | * [gulp-sass](https://github.com/dlmanning/gulp-sass), SASS plugin for gulp
35 | * [gulp-minify-css](https://github.com/jonathanepollack/gulp-minify-css), A Gulp plugin that minifies css with clean-css.
36 | * [gulp-autoprefixer](https://github.com/sindresorhus/gulp-autoprefixer), Prefix CSS.
37 | * [merge-stream](https://github.com/grncdr/merge-stream), Merge multiple streams into one interleaved stream.
38 | * [run-sequence](https://github.com/OverZealous/run-sequence), Run a series of dependent gulp tasks in order.
39 | * [Can-Compile](http://daffl.github.io/can-compile/), Compiles CanJS EJS and Mustache views into a single JavaScript file.
40 |
--------------------------------------------------------------------------------
/form/common.go:
--------------------------------------------------------------------------------
1 | package form
2 |
3 | // RetrieveListForm used when retrieving any kind of list.
4 | type RetrieveListForm struct {
5 | CurrentPage int `form:"currentPage" binding:"required"`
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/vuejs/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/frontend/vuejs/Dockerfile:
--------------------------------------------------------------------------------
1 | # Stage 0, "build-stage", based on Node.js, to build and compile the frontend
2 | # inspired by followings
3 | # * https://snyk.io/blog/10-best-practices-to-containerize-nodejs-web-applications-with-docker/
4 | # * https://dev.to/otomato_io/how-to-optimize-production-docker-images-running-nodejs-with-yarn-504b
5 | # * https://github.com/pyb4430/full-stack-fastapi-postgresql/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/frontend/Dockerfile
6 | FROM node:16-alpine as build-stage
7 |
8 | WORKDIR /app
9 |
10 | COPY package*.json /app/
11 |
12 | COPY yarn.lock /app/
13 |
14 | RUN yarn install --immutable --immutable-cache --check-cache .
15 |
16 | COPY ./ /app/
17 |
18 | ARG FRONTEND_ENV=production
19 |
20 | ENV VUE_APP_ENV=${FRONTEND_ENV}
21 |
22 | RUN yarn build
23 |
24 |
25 | FROM nginx:1.24-alpine
26 |
27 | COPY --from=build-stage /app/dist/ /usr/share/nginx/html
28 |
29 | COPY ./nginx.conf /etc/nginx/conf.d/default.conf
30 | COPY ./nginx-backend-not-found.conf /etc/nginx/extra-conf.d/backend-not-found.conf
--------------------------------------------------------------------------------
/frontend/vuejs/README.md:
--------------------------------------------------------------------------------
1 | # vuejs
2 |
3 | ## Project setup
4 | ```
5 | yarn install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | yarn run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | yarn run build
16 | ```
17 |
18 | ### Run your tests
19 | ```
20 | yarn run test
21 | ```
22 |
23 | ### Lints and fixes files
24 | ```
25 | yarn run lint
26 | ```
27 |
28 | ### Customize configuration
29 | See [Configuration Reference](https://cli.vuejs.org/config/).
30 |
--------------------------------------------------------------------------------
/frontend/vuejs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/vuejs/nginx-backend-not-found.conf:
--------------------------------------------------------------------------------
1 | # location /api {
2 | # return 404;
3 | # }
4 | # location /docs {
5 | # return 404;
6 | # }
7 | # location /redoc {
8 | # return 404;
9 | # }
--------------------------------------------------------------------------------
/frontend/vuejs/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 |
4 | location / {
5 | root /usr/share/nginx/html/;
6 | index index.html index.htm;
7 | try_files $uri $uri/ /index.html =404;
8 | }
9 |
10 | include /etc/nginx/extra-conf.d/*.conf;
11 | }
--------------------------------------------------------------------------------
/frontend/vuejs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "goyangi",
3 | "version": "1.13.0",
4 | "description": "Goyangi is a web foundation to help you getting started with a new web project based on Golang.",
5 | "author": "Joongseob Vito Kim",
6 | "private": true,
7 | "scripts": {
8 | "serve": "vue-cli-service serve",
9 | "build": "vue-cli-service build",
10 | "lint": "vue-cli-service lint"
11 | },
12 | "dependencies": {
13 | "@panter/vue-i18next": "^0.5.1",
14 | "axios": "^0.16.2",
15 | "buefy": "^0.7.0",
16 | "bulma": "^0.5.0",
17 | "bulma-divider": "^0.2.0",
18 | "bulma-extensions": "^6.2.7",
19 | "core-js": "^2.6.5",
20 | "humps": "^2.0.1",
21 | "i18next": "^8.4.3",
22 | "jquery": "^3.2.1",
23 | "vee-validate": "^2.1.0-beta.11",
24 | "vue": "^2.6.10",
25 | "vue-meta": "^1.0.5",
26 | "vue-moment": "^4.0.0",
27 | "vue-router": "^2.6.0",
28 | "vuex": "^2.3.1",
29 | "vuex-router-sync": "^4.2.0"
30 | },
31 | "devDependencies": {
32 | "@vue/cli-plugin-babel": "^3.6.0",
33 | "@vue/cli-plugin-eslint": "^3.6.0",
34 | "@vue/cli-service": "^3.6.0",
35 | "babel-eslint": "^10.0.1",
36 | "eslint": "^5.16.0",
37 | "eslint-plugin-vue": "^5.0.0",
38 | "vue-template-compiler": "^2.5.21"
39 | },
40 | "eslintConfig": {
41 | "root": true,
42 | "env": {
43 | "node": true
44 | },
45 | "extends": [
46 | "plugin:vue/essential",
47 | "eslint:recommended"
48 | ],
49 | "rules": {
50 | "arrow-parens": 0,
51 | "generator-star-spacing": 0,
52 | "no-console": "off",
53 | "no-debugger": "off"
54 | },
55 | "parserOptions": {
56 | "parser": "babel-eslint"
57 | }
58 | },
59 | "postcss": {
60 | "plugins": {
61 | "autoprefixer": {}
62 | }
63 | },
64 | "browserslist": [
65 | "> 1%",
66 | "last 2 versions",
67 | "not ie <= 8"
68 | ]
69 | }
70 |
--------------------------------------------------------------------------------
/frontend/vuejs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/frontend/vuejs/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/vuejs/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | goyangi
9 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
22 |
23 |
24 |
32 |
33 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/api/ArticleAPI.js:
--------------------------------------------------------------------------------
1 | import BaseAPI from './BaseAPI'
2 |
3 | class ArticleAPI extends BaseAPI {
4 | constructor () {
5 | super('/articles')
6 | }
7 |
8 | create (formData) {
9 | // for (var pair of formData.entries()) {
10 | // console.log(`API ${pair[0]}: ${pair[1]}`)
11 | // }
12 | return super.post('', null, formData)
13 | }
14 |
15 | update (id, formData) {
16 | console.log(`ID : ${formData.get('id')}`)
17 | for (var pair of formData.entries()) {
18 | console.log(`API ${pair[0]}: ${pair[1]}`)
19 | }
20 | return super.put(id, null, formData)
21 | }
22 |
23 | delete (id) {
24 | return super.delete(id, null)
25 | }
26 |
27 | list (currentPage = 1) {
28 | return super.get('', {currentPage: currentPage})
29 | }
30 |
31 | retrieve (id) {
32 | return super.get(id)
33 | }
34 |
35 | listComments(id, currentPage = 1) {
36 | return super.get(`${id}/comments`, {currentPage: currentPage})
37 | }
38 |
39 | createComment (id, formData) {
40 | return super.post(`${id}/comments`, null, formData)
41 | }
42 |
43 | updateComment (id, commentId, formData) {
44 | return super.put(`${id}/comments/${commentId}`, null, formData)
45 | }
46 |
47 | deleteComment (id, commentId) {
48 | return super.delete(`${id}/comments/${commentId}`, null)
49 | }
50 |
51 | listLikings(id, currentPage = 1) {
52 | return super.get(`${id}/likings`, {currentPage: currentPage})
53 | }
54 |
55 | createLiking (id, formData) {
56 | return super.post(`${id}/likings`, null, formData)
57 | }
58 |
59 | deleteLiking (id, userId) {
60 | return super.delete(`${id}/likings/${userId}`, null)
61 | }
62 |
63 |
64 |
65 | }
66 |
67 | export default ArticleAPI
68 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/api/AuthenticationAPI.js:
--------------------------------------------------------------------------------
1 | import BaseAPI from './BaseAPI'
2 |
3 | class AuthenticationAPI extends BaseAPI {
4 | constructor () {
5 | super('/authentications')
6 | }
7 |
8 | login (cred) {
9 | return this.post('', cred)
10 | }
11 |
12 | }
13 |
14 | export default AuthenticationAPI
15 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/api/UserAPI.js:
--------------------------------------------------------------------------------
1 | import BaseAPI from './BaseAPI'
2 |
3 | class UserAPI extends BaseAPI {
4 | constructor () {
5 | super('/users')
6 | }
7 |
8 | create (cred) {
9 | return this.post('', cred)
10 | }
11 |
12 | }
13 |
14 | export default UserAPI
15 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/api/index.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import baseURI from '../settings/api_base'
3 | import { Toast } from 'buefy/dist/components/toast'
4 | import i18next from 'i18next'
5 |
6 | axios.interceptors.response.use((response) => {
7 | // Do something with response data
8 | if (response.data) {
9 | Toast.open({
10 | duration: 5000,
11 | message: i18next.t(response.data.messageType),
12 | type: 'is-success'})
13 | }
14 | return response
15 | }, (error) => {
16 | // Do something with response error
17 | if (error.response) {
18 | // The request was made and the server responded with a status code
19 | // that falls out of the range of 2xx
20 | if (error.response.data) {
21 | Toast.open({
22 | duration: 2000,
23 | message: i18next.t(error.response.data.messageType),
24 | type: 'is-danger'})
25 | } else {
26 | if (error.response.status) {
27 | console.log('status : ', error.response.status)
28 | }
29 | if (error.response.headers) {
30 | console.log('header : ' + JSON.stringify(error.response.headers))
31 | }
32 | }
33 | } else if (error.request) {
34 | // The request was made but no response was received
35 | // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
36 | // http.ClientRequest in node.js
37 | console.log('request : ' + error.request)
38 | } else {
39 | // Something happened in setting up the request that triggered an Error
40 | console.log('Error', error.message)
41 | console.log('config error : ' + JSON.stringify(error.config))
42 | }
43 |
44 | // return Promise.reject(error)
45 | })
46 |
47 | export default (method, uri, params = null) => {
48 | if (!method) {
49 | console.error('API function call requires method argument')
50 | return
51 | }
52 |
53 | if (!uri) {
54 | console.error('API function call requires uri argument')
55 | return
56 | }
57 |
58 | var url = baseURI + uri
59 | return axios({ method, url, params })
60 | }
61 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/assets/goyangi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/frontend/vuejs/src/assets/goyangi.jpg
--------------------------------------------------------------------------------
/frontend/vuejs/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/frontend/vuejs/src/assets/logo.png
--------------------------------------------------------------------------------
/frontend/vuejs/src/components/articles/List.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ $t('article.view.list.write') }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{article.id}} |
20 | {{article.title}} |
21 | {{article.author.username}} |
22 | {{article.createdAt | moment("MM-DD-YYYY, h:mm:ss a")}} |
23 |
24 |
25 |
26 |
27 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
86 |
87 |
88 |
91 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/components/comments/Form.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/components/comments/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/components/likings/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/layouts/BasicLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
67 |
68 |
69 |
92 |
93 |
106 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | import { sync } from 'vuex-router-sync'
5 | import App from './views/App'
6 | import router from './router'
7 | import store from './store'
8 | import Buefy from 'buefy'
9 | import 'buefy/dist/buefy.css'
10 | import 'bulma-divider'
11 | import Moment from 'vue-moment'
12 | import i18next from 'i18next'
13 | import i18nOption from './settings/i18next'
14 | import VueI18Next from '@panter/vue-i18next'
15 | import VeeValidate, { Validator } from 'vee-validate'
16 | import ko from 'vee-validate/dist/locale/ko'
17 | import $ from 'jquery'
18 |
19 | Vue.use({
20 | install: function(Vue){
21 | Vue.prototype.$jQuery = $; // you'll have this.$jQuery anywhere in your vue project
22 | }
23 | })
24 | Vue.use(Buefy)
25 | Vue.use(Moment)
26 | Vue.use(VueI18Next)
27 | sync(store, router)
28 |
29 | var lang = $('html').attr('lang')
30 |
31 | lang !== undefined
32 | ? i18nOption.lng = lang
33 | : i18nOption.lng = 'en'
34 | i18next.init(i18nOption)
35 | const i18n = new VueI18Next(i18next)
36 |
37 | Vue.config.productionTip = false
38 |
39 | Vue.use(VeeValidate, {
40 | locale: 'ko'
41 | })
42 | Validator.localize('ko', ko)
43 |
44 | /* eslint-disable no-new */
45 | new Vue({
46 | el: '#app',
47 | router: router,
48 | store: store,
49 | i18n: i18n,
50 | render: h => h(App)
51 | })
52 |
53 | // Check local storage to handle refreshes
54 | if (window.localStorage) {
55 | var localUserString = window.localStorage.getItem('user') || 'null'
56 | var localUser = JSON.parse(localUserString)
57 |
58 | if (localUser && store.state.user !== localUser) {
59 | store.commit('SET_USER', localUser)
60 | store.commit('SET_IS_AUTHENTICATED', window.localStorage.getItem('isAuthenticated'))
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/router/articles/index.js:
--------------------------------------------------------------------------------
1 | import Articles from '@/views/articles'
2 | import ArticleList from '@/components/articles/List'
3 | import ArticleItem from '@/components/articles/Item'
4 | import ArticleForm from '@/components/articles/Form'
5 |
6 | export default [
7 | {
8 | path: '/articles',
9 | component: Articles,
10 | children: [
11 | {
12 | path: '',
13 | component: ArticleList,
14 | props: (route) => ({ currentPage: route.query.currentPage })
15 | },
16 | {
17 | path: 'new',
18 | component: ArticleForm,
19 | meta: { requiresAuth: true },
20 | props: {action: 'new'}
21 | },
22 | {
23 | path: ':articleId',
24 | component: ArticleItem,
25 | props: true
26 | },
27 | {
28 | path: ':articleId/edit',
29 | component: ArticleForm,
30 | props: {action: 'edit'}
31 | }
32 | ]
33 | }
34 | ]
35 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import BasicLayout from '@/layouts/BasicLayout'
4 | import Home from '@/views/Home'
5 | import usersRoute from './users'
6 | import articlesRoute from './articles'
7 | import store from '@/store'
8 | Vue.use(Router)
9 |
10 | const router = new Router({
11 | mode: 'history',
12 | routes: [
13 | {
14 | path: '/',
15 | component: BasicLayout,
16 | children: [
17 | {
18 | path: '',
19 | component: Home
20 | }, ...usersRoute,
21 | ...articlesRoute
22 | ]
23 | }
24 | ]
25 | })
26 |
27 | router.beforeEach((to, from, next) => {
28 | if (to.matched.some(record => record.meta.requiresAuth) && !store.state.authentications.isAuthenticated) return next("/login")
29 | next()
30 | })
31 |
32 | export default router
33 |
34 |
35 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/router/users/index.js:
--------------------------------------------------------------------------------
1 | import Login from '@/views/users/Login'
2 | import Registration from '@/views/users/Registration'
3 |
4 | export default [
5 | {
6 | path: '/login',
7 | component: Login
8 | }, {
9 | path: '/registration',
10 | component: Registration
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/settings/api_base.js:
--------------------------------------------------------------------------------
1 | const HOST = '//localhost'
2 | const API_BASE_URI = HOST + '/api/v1'
3 |
4 | export default API_BASE_URI
5 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/settings/i18next.js:
--------------------------------------------------------------------------------
1 | const en = require('@/locales/en/translation.json')
2 | const ko = require('@/locales/ko/translation.json')
3 |
4 | export default {debug: true,
5 | fallbackLng: 'en',
6 | resources: {
7 | en: { translation: en },
8 | ko: { translation: ko }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/actions.js:
--------------------------------------------------------------------------------
1 | // import types from './action_types'
2 | // import api from '../api'
3 | // export default {
4 | // login ({ commit }, creds) {
5 | // commit(types.LOGIN) // show spinner
6 | // return api('post', '/authentications', creds)
7 | // .then((response) => {
8 | // if (response) {
9 | // // console.log('response', response)
10 |
11 | // localStorage.setItem('token', 'JWT')
12 | // commit(types.LOGIN_SUCCESS)
13 | // }
14 | // })
15 | // // return new Promise(resolve => {
16 | // // setTimeout(() => {
17 | // // localStorage.setItem('token', 'JWT')
18 | // // commit(types.LOGIN_SUCCESS)
19 | // // resolve()
20 | // // }, 1000)
21 | // // })
22 | // },
23 | // logout ({ commit }) {
24 | // localStorage.removeItem('token')
25 | // commit(types.LOGOUT)
26 | // }
27 | // }
28 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/getters.js:
--------------------------------------------------------------------------------
1 | // export default {
2 | // currentLocale: state => {
3 | // return state.locale
4 | // }
5 |
6 | // }
7 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import authentications from './modules/authentications'
4 | import users from './modules/users'
5 | import articles from './modules/articles'
6 | import languages from './modules/languages'
7 |
8 | Vue.use(Vuex)
9 |
10 | const store = new Vuex.Store({
11 | modules: {
12 | authentications: authentications,
13 | users: users,
14 | articles: articles,
15 | languages: languages
16 | }
17 | })
18 |
19 | export default store
20 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/articles/getters.js:
--------------------------------------------------------------------------------
1 | export default {
2 | articleList: state => state.articleList,
3 | canWrite: (_, rootGetters) => userId => userId == rootGetters.currentUserId,
4 | article: state => state.article,
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/articles/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import getters from './getters'
3 | import mutations from './mutations'
4 |
5 | const state = {
6 | articleAPI: null,
7 | pending: false,
8 | callingAPI: false,
9 | searching: '',
10 | articleList: {},
11 | article: {}
12 | }
13 |
14 | export default {
15 | state,
16 | actions,
17 | getters,
18 | mutations
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/articles/mutations.js:
--------------------------------------------------------------------------------
1 | import ArticleAPI from '../../../api/ArticleAPI'
2 |
3 | import { articleTypes } from '../../mutation-types'
4 |
5 | export default {
6 | [articleTypes.SET_API] (state) {
7 | if (state.articleAPI === null) {
8 | state.articleAPI = new ArticleAPI()
9 | }
10 | },
11 | [articleTypes.PENDING] (state) {
12 | state.pending = true
13 | },
14 | [articleTypes.DONE] (state) {
15 | state.pending = false
16 | },
17 | [articleTypes.NEW_ARTICLE] (state) {
18 | state.article = {}
19 | },
20 | [articleTypes.SET_ARTICLE_LIST] (state, response) {
21 | state.articleList = response.articleList
22 | },
23 | [articleTypes.SET_ARTICLE] (state, response) {
24 | console.log(`SET_ARTICLE response : ${JSON.stringify(response)}`)
25 | state.article = response.article
26 | console.log(`state.article.id : ${state.article.id}`)
27 | },
28 | [articleTypes.SET_ARTICLE_COMMENT_LIST] (state, response) {
29 | let comments = state.article.commentList.comments
30 | state.article.commentList = response.commentList
31 | if(state.article.commentList.currentPage !== 1 && state.article.commentList.comments != null) {
32 | state.article.commentList.comments = comments.concat(state.article.commentList.comments)
33 | }
34 | },
35 | [articleTypes.SET_ARTICLE_LIKING_LIST] (state, response) {
36 | state.article.likingList = response.likingList
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/authentications/actions.js:
--------------------------------------------------------------------------------
1 | import {authenticationTypes} from '../../mutation-types'
2 |
3 | export default {
4 | initAuthenticationAPI ({commit}) {
5 | commit(authenticationTypes.SET_API)
6 | },
7 | login ({commit, state, dispatch}, creds) {
8 | dispatch('initAuthenticationAPI')
9 | commit(authenticationTypes.LOGIN) // show spinner
10 |
11 | state
12 | .authenticationAPI
13 | .login(creds)
14 | .then((response) => {
15 | if (response) {
16 | localStorage.setItem('isAuthenticated', true)
17 | commit(authenticationTypes.LOGIN_SUCCESS)
18 | }
19 | })
20 | // return new Promise(resolve => {
21 | // setTimeout(() => {
22 | // localStorage.setItem('token', 'JWT')
23 | // commit(types.LOGIN_SUCCESS)
24 | // resolve()
25 | // }, 1000)
26 | // })
27 | },
28 | logout ({ commit }) {
29 | localStorage.removeItem('isAuthenticated')
30 | commit(authenticationTypes.LOGOUT)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/authentications/getters.js:
--------------------------------------------------------------------------------
1 | export default {
2 | isAuthenticated: state => state.isAuthenticated,
3 | currentUserId: state => state.currentUser.id
4 | }
5 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/authentications/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import getters from './getters'
3 | import mutations from './mutations'
4 |
5 | const state = {
6 | isAuthenticated: !!localStorage.getItem('isAuthenticated'),
7 | authenticationAPI: null,
8 | pending: false,
9 | callingAPI: false,
10 | searching: '',
11 | currentUser: {id: 1}
12 | }
13 |
14 | export default {
15 | state,
16 | actions,
17 | getters,
18 | mutations
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/authentications/mutations.js:
--------------------------------------------------------------------------------
1 | import AuthenticationAPI from '../../../api/AuthenticationAPI'
2 |
3 | import { authenticationTypes } from '../../mutation-types'
4 |
5 | export default {
6 | [authenticationTypes.SET_API] (state) {
7 | if (state.authenticationAPI === null) {
8 | state.authenticationAPI = new AuthenticationAPI()
9 | }
10 | },
11 | [authenticationTypes.LOGIN] (state) {
12 | state.pending = true
13 | },
14 | [authenticationTypes.LOGIN_SUCCESS] (state) {
15 | state.isAuthenticated = true
16 | state.pending = false
17 | },
18 | [authenticationTypes.LOGOUT] (state) {
19 | state.isAuthenticated = false
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/languages/actions.js:
--------------------------------------------------------------------------------
1 | import {languageTypes} from '../../mutation-types'
2 | export default {
3 | changeLocale ({ commit }, locale) {
4 | console.log('locale to : '+ locale)
5 | commit(languageTypes.CHANGE_LOCALE, locale)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/languages/getters.js:
--------------------------------------------------------------------------------
1 | export default {
2 | currentLocale: state => state.locale
3 | }
4 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/languages/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import getters from './getters'
3 | import mutations from './mutations'
4 |
5 | const state = {
6 | locale: 'en'
7 | }
8 |
9 | export default {
10 | state,
11 | actions,
12 | getters,
13 | mutations
14 | }
15 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/languages/mutations.js:
--------------------------------------------------------------------------------
1 | import i18next from 'i18next'
2 | import { languageTypes } from '../../mutation-types'
3 |
4 | export default {
5 | [languageTypes.CHANGE_LOCALE] (state, locale) {
6 | state.locale = locale
7 | i18next.changeLanguage(locale)
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/users/actions.js:
--------------------------------------------------------------------------------
1 | import {userTypes} from '../../mutation-types'
2 |
3 | export default {
4 | initUserAPI ({commit}) {
5 | commit(userTypes.SET_API)
6 | },
7 | createUser ({commit, state, dispatch}, creds) {
8 | dispatch('initUserAPI')
9 | commit(userTypes.CREATE_USER) // show spinner
10 |
11 | state
12 | .userAPI
13 | .create(creds)
14 | .then((response) => {
15 | if (response) {
16 | localStorage.setItem('token', 'JWT')
17 | commit(userTypes.CREATE_USER_SUCCESS)
18 | }
19 | })
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/users/getters.js:
--------------------------------------------------------------------------------
1 | export default {
2 | createdUser: state => state.createdUser
3 | }
4 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/users/index.js:
--------------------------------------------------------------------------------
1 | import actions from './actions'
2 | import getters from './getters'
3 | import mutations from './mutations'
4 |
5 | const state = {
6 | createdUser: !!localStorage.getItem('token'),
7 | userAPI: null,
8 | pending: false,
9 | callingAPI: false,
10 | searching: '',
11 | user: null
12 | }
13 |
14 | export default {
15 | state,
16 | actions,
17 | getters,
18 | mutations
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/modules/users/mutations.js:
--------------------------------------------------------------------------------
1 | import UserAPI from '../../../api/UserAPI'
2 |
3 | import { userTypes } from '../../mutation-types'
4 |
5 | export default {
6 | [userTypes.SET_API] (state) {
7 | if (state.userAPI === null) {
8 | state.userAPI = new UserAPI()
9 | }
10 | },
11 | [userTypes.CREATE_USER] (state) {
12 | state.pending = true
13 | },
14 | [userTypes.CREATE_USER_SUCCESS] (state) {
15 | state.createdUser = true
16 | state.pending = false
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/mutation-types.js:
--------------------------------------------------------------------------------
1 | export const authenticationTypes = {
2 | SET_API: 'SET_API',
3 | LOGIN: 'LOGIN',
4 | LOGIN_SUCCESS: 'LOGIN_SUCCESS',
5 | LOGOUT: 'LOGOUT'
6 | }
7 |
8 | export const userTypes = {
9 | SET_API: 'SET_API',
10 | CREATE_USER: 'CREATE_USER',
11 | CREATE_USER_SUCCESS: 'CREATE_USER_SUCCESS'
12 | }
13 |
14 | export const articleTypes = {
15 | SET_API: 'SET_API',
16 | PENDING: 'PENDING',
17 | DONE: 'DONE',
18 | SET_ARTICLE_LIST: 'SET_ARTICLE_LIST',
19 | NEW_ARTICLE: 'NEW_ARTICLE',
20 | SET_ARTICLE: 'SET_ARTICLE',
21 | SET_ARTICLE_COMMENT_LIST: 'SET_ARTICLE_COMMENT_LIST',
22 | SET_ARTICLE_LIKING_LIST: 'SET_ARTICLE_LIKING_LIST'
23 | }
24 |
25 | export const languageTypes = {
26 | CHANGE_LOCALE: 'CHANGE_LOCALE'
27 | }
28 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/mutations.js:
--------------------------------------------------------------------------------
1 | // import types from './action_types'
2 | // import i18next from 'i18next'
3 |
4 | // export default {
5 |
6 | // [types.CHANGE_LOCALE] (state, locale) {
7 | // state.locale = locale
8 | // i18next.changeLanguage(locale)
9 | // },
10 | // TOGGLE_LOADING (state) {
11 | // state.callingAPI = !state.callingAPI
12 | // },
13 | // TOGGLE_SEARCHING (state) {
14 | // state.searching = (state.searching === '') ? 'loading' : ''
15 | // },
16 | // SET_USER (state, user) {
17 | // state.user = user
18 | // },
19 | // SET_IS_AUTHENTICATED (state, status) {
20 | // state.isAuthenticated = status
21 | // }
22 | // }
23 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/store/state.js:
--------------------------------------------------------------------------------
1 | // import baseURI from '../settings/api_base'
2 |
3 | // export default {
4 | // isAuthenticated: !!localStorage.getItem('token'),
5 | // locale: 'en',
6 | // callingAPI: false,
7 | // searching: '',
8 | // baseURI: baseURI,
9 | // user: null,
10 | // token: null,
11 | // userInfo: {
12 | // messages: [{1: 'test', 2: 'test'}],
13 | // notifications: [],
14 | // tasks: []
15 | // }
16 | // }
17 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/views/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ $t('main.title') }}
4 |
5 |

6 |
7 | {{ $t('main.description') }}
8 |
9 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
43 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/views/articles/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
16 |
17 |
18 |
37 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/views/users/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Login
9 |
10 |
28 |
29 |
30 | Registration
31 |
32 | |
33 | Need help?
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
63 |
64 |
65 |
68 |
--------------------------------------------------------------------------------
/frontend/vuejs/src/views/users/Registration.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Register an Account
10 |
11 |
37 |
38 |
39 | Login
40 |
41 | |
42 | Need help?
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
75 |
76 |
77 |
80 |
--------------------------------------------------------------------------------
/log/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 |
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "os"
6 |
7 | "github.com/dorajistyle/goyangi/db"
8 | "github.com/dorajistyle/goyangi/script"
9 | "github.com/dorajistyle/goyangi/server"
10 | "github.com/spf13/viper"
11 | "github.com/urfave/cli"
12 | )
13 |
14 | func init() {
15 | viper.AddConfigPath(".")
16 | viper.SetConfigType("yaml")
17 |
18 | // switch os.Getenv("ENV") {
19 | // case "DEVELOPMENT":
20 | // viper.SetConfigName(".env.dev")
21 | // case "TEST":
22 | // viper.SetConfigName(".env.test")
23 | // case "PRODUCTION":
24 | // viper.SetConfigName(".env.prod")
25 | // }
26 |
27 | viper.SetConfigName("config.yml")
28 |
29 | viper.AutomaticEnv()
30 |
31 | if err := viper.ReadInConfig(); err != nil {
32 | log.Fatalln("Fatal error config file", err)
33 | }
34 | }
35 |
36 | func main() {
37 | app := cli.NewApp()
38 | app.Name = "Goyangi script tool"
39 | app.Usage = "run scripts!"
40 | app.Version = "0.1.0"
41 |
42 | app.Author = "https://github.com/dorajistyle(JoongSeob Vito Kim)"
43 | app.Commands = script.Commands()
44 | app.Action = func(c *cli.Context) {
45 | println("Run Server.")
46 | println(viper.GetString("app.name"))
47 |
48 | server.Run()
49 | }
50 | println(viper.GetString("database.type"))
51 | db.GormInit()
52 |
53 | app.Run(os.Args)
54 | }
55 |
--------------------------------------------------------------------------------
/model/app.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type App struct {
8 | Id uint `json:"id"`
9 | Name string `json:"name",sql:"size:255"`
10 | Key string `json:"name",sql:"size:255"`
11 | Token string `json:"token"`
12 | TokenExpiration time.Time `json:"tokenExperiation"`
13 | }
14 |
15 | type ExpiredTokenLog struct{
16 | UserId uint `json:"userId"`
17 | AccessedAt time.Time `json:"activatedAt"`
18 | }
--------------------------------------------------------------------------------
/model/article.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | // Article is a article model.
8 | type Article struct {
9 | Id uint `json:"id"`
10 | Title string `json:"title",sql:"size:255"`
11 | Url string `json:"url",sql:"size:512"`
12 | Content string `json:"content"`
13 | UserId uint `json:"userId"`
14 | Author User `json:"author"`
15 | ReferralId uint `json:"referralId"`
16 | ReferralUserId uint `json:"referralUserId"`
17 | CategoryId int `json:"categoryId"`
18 | PrevId uint `json:"prevId"`
19 | NextId uint `json:"nextId"`
20 | LikingCount int `json:"likingCount"`
21 | CommentCount int `json:"commentCount"`
22 | SharingCount int `json:"sharingCount"`
23 | ImageName string `json:"imageName",sql:"size:512"`
24 | ThumbnailName string `json:"thumbnailName",sql:"size:512"`
25 | Activate bool `json:"active"`
26 | CreatedAt time.Time `json:"createdAt"`
27 | UpdatedAt time.Time `json:"updatedAt"`
28 | DeletedAt *time.Time `json:"deletedAt"`
29 | Comments []Comment `gorm:"many2many:articles_comments;association_autoupdate:false;association_autocreate:false"`
30 | CommentList CommentList `json:"commentList"`
31 | Likings []User `gorm:"many2many:articles_users;association_autoupdate:false;association_autocreate:false"`
32 | LikingList LikingList `json:"likingList"`
33 | }
34 |
35 | // ArticleList is list that contains articles and meta.
36 | type ArticleList struct {
37 | Articles []Article `json:"articles"`
38 | Category int `json:"category"`
39 | HasPrev bool `json:"hasPrev"`
40 | HasNext bool `json:"hasNext"`
41 | Count int `json:"count"`
42 | CurrentPage int `json:"currentPage"`
43 | PerPage int `json:"perPage"`
44 | }
--------------------------------------------------------------------------------
/model/common.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | // omit is the bool type for omitting a field of struct.
8 | type omit bool
9 |
10 | // CommentList is list that contains comments and meta.
11 | type CommentList struct {
12 | Comments []Comment `json:"comments"`
13 | HasPrev bool `json:"hasPrev"`
14 | HasNext bool `json:"hasNext"`
15 | Count int `json:"count"`
16 | CurrentPage int `json:"currentPage"`
17 | }
18 |
19 | // Comment is a comment model.
20 | type Comment struct {
21 | Id uint `json:"id"`
22 | Content string `json:"content"`
23 | UserId uint `json:"userId"`
24 | LikingCount int `json:"likingCount"`
25 | CreatedAt time.Time `json:"createdAt"`
26 | UpdatedAt time.Time `json:"updatedAt"`
27 | DeletedAt *time.Time `json:"deletedAt"`
28 | User User `json:"user"`
29 | }
30 |
31 | // LikingList is list that contains likings and meta.
32 | type LikingList struct {
33 | Likings []User `json:"likings"`
34 | HasPrev bool `json:"hasPrev"`
35 | HasNext bool `json:"hasNext"`
36 | Count int `json:"count"`
37 | CurrentPage int `json:"currentPage"`
38 | IsLiked bool `json:"isLiked"`
39 | }
40 |
41 | // LikedList is list that contains liked and meta.
42 | type LikedList struct {
43 | Liked []User `json:"liked"`
44 | HasPrev bool `json:"hasPrev"`
45 | HasNext bool `json:"hasNext"`
46 | Count int `json:"count"`
47 | CurrentPage int `json:"currentPage"`
48 |
49 | }
50 |
51 | // Image is a image model.
52 | type Image struct {
53 | Id uint `json:"id"`
54 | Kind int `json:"kind"`
55 | Large string `json:"large"`
56 | Medium string `json:"medium"`
57 | Thumbnail string `json:"thumbnail"`
58 | CreatedAt time.Time `json:"createdAt"`
59 | }
60 |
61 | // Tag is a tag model.
62 | type Tag struct {
63 | Id uint `json:"id"`
64 | Name string `json:"name",sql:"size:255"`
65 | }
66 |
67 | // Link is a link model.
68 | type Link struct {
69 | Id uint `json:"id"`
70 | Kind int `json:"kind"`
71 | Name string `json:"title",sql:"size:255"`
72 | Url string `json:"url",sql:"size:512"`
73 | CreatedAt time.Time `json:"createdAt"`
74 | Icon string `json:"icon",sql:"size:255"`
75 | }
76 |
--------------------------------------------------------------------------------
/model/file.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | // File is a file model that contains meta.
8 | type File struct {
9 | Id uint `json:"id"`
10 | UserId uint `json:"userId"`
11 | Name string `json:"name",sql:"size:255"`
12 | Size int `json:"size",sql:"size:255"`
13 | CreatedAt time.Time `json:"createdAt"`
14 | }
15 |
--------------------------------------------------------------------------------
/model/language.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | // Language is a language model.
4 | type Language struct {
5 | Id uint `json:"id"`
6 | Name string `json:"name"`
7 | }
8 |
--------------------------------------------------------------------------------
/model/role.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | // Role is a role model for user permission.
4 | type Role struct {
5 | Id uint `json:"id"`
6 | Name string `json:"name",sql:"size:255"`
7 | Description string `json:"description",sql:"size:255"`
8 | }
9 |
--------------------------------------------------------------------------------
/readme_ko.md:
--------------------------------------------------------------------------------
1 | # Goyangi
2 |
3 | 고양이는 고언어 기반의 새로운 웹 프로젝트 개발을 위한 토대입니다. SPA나 모바일 어플리케이션을 제작하기에 좋습니다.
4 | 고언어 패키지들을 이용한 대규모 웹 어플리케이션 예제로도 좋습니다.
5 | [Goyangi](http://en.wikipedia.org/wiki/goyangi)의 뜻은 글자 그대로 고양이입니다.
6 |
7 | ## 왜 고양이를 만들었나요?
8 | 모든것을 갖춘 고 언어용 웹 프레임웍이 몇 있습니다.([Beego](http://beego.me/), [Revel](http://revel.github.io/)...). 그렇지만 그 프레임웍들은 너무 많은 것을 갖추고 있습니다.
9 |
10 | _______________________
11 | ## 사용권
12 |
13 | Goyangi는 [MIT license](./LICENSE)를 따릅니다.
14 |
--------------------------------------------------------------------------------
/script/commands.go:
--------------------------------------------------------------------------------
1 | package script
2 |
3 | import (
4 | "regexp"
5 |
6 | "github.com/urfave/cli"
7 | "github.com/dorajistyle/goyangi/server"
8 | )
9 |
10 | func Commands() []cli.Command {
11 | return []cli.Command{
12 | // Init application
13 | {
14 | Name: "init",
15 | Usage: "Init application(go packages)",
16 | Action: func(c *cli.Context) {
17 | commands := InitApp()
18 | RunScript(commands)
19 | println("Init script done.")
20 | },
21 | },
22 | // Generate API document
23 | {
24 | Name: "generateAPI",
25 | Aliases: []string{"ga"},
26 | Usage: "Generate API document using swagger",
27 | Action: func(c *cli.Context) {
28 | // go|swagger|asciidoc|markdown|confluence
29 | format := "asciidoc"
30 | if len(c.Args()) > 0 {
31 | format = c.Args()[0]
32 | }
33 | commands := GenerateAPI(format)
34 | RunScript(commands)
35 | formatRegex, _ := regexp.Compile("^(go|swagger|asciidoc|markdown|confluence)$")
36 | if formatRegex.MatchString(format) {
37 | println("API document generated. You can find document at ./document/")
38 | } else {
39 | println("Invalid -format specified. Must be one of go|swagger|asciidoc|markdown|confluence.")
40 | }
41 | },
42 | },
43 | // Run server
44 | {
45 | Name: "server",
46 | Aliases: []string{"s"},
47 | Usage: "Run server",
48 | Action: func(c *cli.Context) {
49 | db := ""
50 | if len(c.Args()) > 0 {
51 | db = c.Args()[0]
52 | }
53 | commands := Server(db)
54 | RunScript(commands)
55 | server.Run()
56 | println("Server is running now.")
57 | },
58 | },
59 | // Run test
60 | {
61 | Name: "test",
62 | Aliases: []string{"t"},
63 | Usage: "Run test using Ginkgo",
64 | Action: func(c *cli.Context) {
65 | ci := ""
66 | if len(c.Args()) > 0 {
67 | ci = c.Args()[0]
68 | }
69 | commands := Test(ci)
70 | RunScript(commands)
71 | println("Test script done.")
72 | },
73 | },
74 | } // end []cli.Command
75 | } // end Commands()
76 |
--------------------------------------------------------------------------------
/script/constant.go:
--------------------------------------------------------------------------------
1 | package script
2 |
3 | // Constants for script commands.
4 | const (
5 | Bash = "/bin/bash"
6 | GoRun = "go run "
7 | )
8 |
--------------------------------------------------------------------------------
/script/generateAPI.go:
--------------------------------------------------------------------------------
1 | package script
2 |
3 | func GenerateAPI(format string) []string {
4 | commands := make([]string, 2)
5 | prefix := "swagger -apiPackage=\"github.com/dorajistyle/goyangi/api/v1\" -mainApiFile=\"github.com/dorajistyle/goyangi/api/route.go\" -format=\"" + format + "\" "
6 | switch format {
7 | case "go":
8 | fallthrough
9 | case "swagger":
10 | commands = append(commands, prefix+"-output=\"document/\"")
11 | case "asciidoc":
12 | commands = append(commands, prefix+"-output=\"document/API.adoc\"")
13 | commands = append(commands, "asciidoctor -a icons -a toc2 -a stylesheet=github.css -a stylesdir=./stylesheets document/API.adoc")
14 | case "markdown":
15 | commands = append(commands, prefix+"-output=\"document/API.md\"")
16 | case "confluence":
17 | commands = append(commands, prefix+"-output=\"document/API.confluence\"")
18 | }
19 | return commands
20 | }
21 |
--------------------------------------------------------------------------------
/script/init.go:
--------------------------------------------------------------------------------
1 | package script
2 |
3 | import (
4 | "log"
5 | "os"
6 | )
7 |
8 | func InitApp() []string {
9 | commands := make([]string, 8)
10 | dir, err := os.Getwd()
11 | if err != nil {
12 | log.Fatal(err)
13 | }
14 | commands = append(commands, "SCRIPTPATH="+dir)
15 | commands = append(commands, "dep ensure")
16 | commands = append(commands, "cd \"$SCRIPTPATH/frontend/vuejs\"")
17 | commands = append(commands, "yarn")
18 | return commands
19 | }
20 |
--------------------------------------------------------------------------------
/script/script.go:
--------------------------------------------------------------------------------
1 | package script
2 |
3 | import (
4 | "io"
5 | "os"
6 | "os/exec"
7 | "strings"
8 | "sync"
9 | )
10 |
11 | func RunScript(commands []string) {
12 | entireScript := strings.NewReader(strings.Join(commands, "\n"))
13 | bash := exec.Command(Bash)
14 | stdin, _ := bash.StdinPipe()
15 | stdout, _ := bash.StdoutPipe()
16 | stderr, _ := bash.StderrPipe()
17 |
18 | wait := sync.WaitGroup{}
19 | wait.Add(3)
20 | go func() {
21 | io.Copy(stdin, entireScript)
22 | stdin.Close()
23 | wait.Done()
24 | }()
25 | go func() {
26 | io.Copy(os.Stdout, stdout)
27 | wait.Done()
28 | }()
29 | go func() {
30 | io.Copy(os.Stderr, stderr)
31 | wait.Done()
32 | }()
33 |
34 | bash.Start()
35 | wait.Wait()
36 | bash.Wait()
37 | }
38 |
--------------------------------------------------------------------------------
/script/server.go:
--------------------------------------------------------------------------------
1 | package script
2 |
3 | func Server(db string) []string {
4 | commands := make([]string, 2)
5 | switch db {
6 | case "mysql":
7 | commands = append(commands, "/usr/bin/mysqld_safe &")
8 | commands = append(commands, "sleep 10s")
9 | }
10 | // commands = append(commands, GoopGoRun+"server.go")
11 | return commands
12 | }
13 |
--------------------------------------------------------------------------------
/script/test.go:
--------------------------------------------------------------------------------
1 | package script
2 |
3 | func Test(ci string) []string {
4 | commands := make([]string, 3)
5 | command := "ginkgo -r --randomizeAllSpecs --randomizeSuites --failOnPending --trace"
6 | switch ci {
7 | case "travis":
8 | command = command + " --cover --race --compilers=2"
9 | }
10 | commands = append(commands, command)
11 | return commands
12 | }
13 |
--------------------------------------------------------------------------------
/server/exec/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "github.com/dorajistyle/goyangi/server"
4 |
5 | func main() {
6 | server.Run()
7 | }
8 |
--------------------------------------------------------------------------------
/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/dorajistyle/goyangi/api"
7 | "github.com/spf13/viper"
8 |
9 | docs "github.com/dorajistyle/goyangi/docs"
10 | "github.com/dorajistyle/goyangi/util/log"
11 | "github.com/gin-contrib/cors"
12 | "github.com/gin-gonic/gin"
13 | "github.com/nicksnyder/go-i18n/i18n"
14 | swaggerfiles "github.com/swaggo/files"
15 | ginSwagger "github.com/swaggo/gin-swagger"
16 | )
17 |
18 | func initI18N() {
19 | i18n.MustLoadTranslationFile("service/userService/locale/en-us.all.json")
20 | i18n.MustLoadTranslationFile("service/userService/locale/ko-kr.all.json")
21 | }
22 |
23 | func init() {
24 |
25 | log.Init(viper.GetString("app.environment"))
26 | initI18N()
27 | }
28 |
29 | // CORSMiddleware for CORS
30 | func CORSMiddleware() gin.HandlerFunc {
31 | corsConfig := cors.New(cors.Config{
32 | AllowOrigins: []string{"http://localhost:8080", "http://localhost"},
33 | AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
34 | AllowHeaders: []string{"Origin", "access-control-allow-origin"},
35 | ExposeHeaders: []string{"Content-Length"},
36 | AllowCredentials: true,
37 | AllowOriginFunc: func(origin string) bool {
38 | return origin == "https://github.com"
39 | },
40 | MaxAge: 12 * time.Hour,
41 | })
42 | // corsConfig = cors.Default()
43 | return corsConfig
44 | }
45 |
46 | func Run() {
47 | r := gin.New()
48 | docs.SwaggerInfo.BasePath = "/" + viper.GetString("api.url")
49 |
50 | // Global middlewares
51 | // If use gin.Logger middlewares, it send duplicated request.
52 | switch viper.GetString("app.environment") {
53 | case "DEVELOPMENT":
54 | r.Use(gin.Logger())
55 | case "TEST":
56 | r.Use(log.AccessLogger())
57 | case "PRODUCTION":
58 | r.Use(log.AccessLogger())
59 | }
60 | r.Use(gin.Recovery())
61 | r.Use(CORSMiddleware())
62 |
63 | api.RouteAPI(r)
64 | r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
65 |
66 | // Listen and server on 0.0.0.0:3001
67 | // r.Run("localhost:3001")
68 | r.Run(":80")
69 | }
70 |
--------------------------------------------------------------------------------
/service/articleService/comment.go:
--------------------------------------------------------------------------------
1 | package articleService
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 |
7 | "github.com/dorajistyle/goyangi/db"
8 | "github.com/dorajistyle/goyangi/form"
9 | "github.com/dorajistyle/goyangi/model"
10 | "github.com/dorajistyle/goyangi/service/commentService"
11 | "github.com/dorajistyle/goyangi/util/log"
12 | "github.com/gin-gonic/gin"
13 | "github.com/gin-gonic/gin/binding"
14 | )
15 |
16 | // UpdateArticleCommentCount updates article's comment count.
17 | func UpdateArticleCommentCount(article *model.Article) (int, error) {
18 | article.CommentCount = db.ORM.Model(article).Association("Comments").Count()
19 | if db.ORM.Save(article).Error != nil {
20 | return http.StatusInternalServerError, errors.New("Article comment's count is not updated.")
21 | }
22 | return http.StatusOK, nil
23 | }
24 |
25 | // CreateCommentOnArticle creates a comment to an article.
26 | func CreateCommentOnArticle(c *gin.Context) (int, error) {
27 | article := &model.Article{}
28 | status, err := commentService.CreateComment(c, article)
29 | if err != nil {
30 | return status, err
31 | }
32 | status, err = UpdateArticleCommentCount(article)
33 | if err != nil {
34 | return status, err
35 | }
36 | return http.StatusCreated, nil
37 | }
38 |
39 | // RetrieveCommentsOnArticles retrieve comments on an article.
40 | func RetrieveCommentsOnArticle(c *gin.Context) (model.CommentList, int, error) {
41 | var article model.Article
42 | var commentList model.CommentList
43 | var retrieveListForm form.RetrieveListForm
44 |
45 | articleId := c.Params.ByName("id")
46 | if db.ORM.First(&article, articleId).RecordNotFound() {
47 | return commentList, http.StatusNotFound, errors.New("Article is not found.")
48 | }
49 | bindErr := c.MustBindWith(&retrieveListForm, binding.Form)
50 | log.Debugf("[RetrieveCommentsOnArticle] bind error : %s\n", bindErr)
51 | if bindErr != nil {
52 | return commentList, http.StatusBadRequest, errors.New("Comments are not retrieved.")
53 | }
54 | log.Debugf("retrieveListForm : %v", retrieveListForm)
55 | commentList = commentService.RetrieveComments(article, retrieveListForm.CurrentPage)
56 | return commentList, http.StatusOK, nil
57 | }
58 |
59 | // UpdateCommentOnArticle updates a comment on an article.
60 | func UpdateCommentOnArticle(c *gin.Context) (int, error) {
61 | article := &model.Article{}
62 | status, err := commentService.UpdateComment(c, article)
63 | if err != nil {
64 | return status, err
65 | }
66 | status, err = UpdateArticleCommentCount(article)
67 | if err != nil {
68 | return status, err
69 | }
70 | return http.StatusOK, err
71 | }
72 |
73 | // DeleteCommentOnArticle deletes a comment from an article.
74 | func DeleteCommentOnArticle(c *gin.Context) (int, error) {
75 | article := &model.Article{}
76 | status, err := commentService.DeleteComment(c, article)
77 | if err != nil {
78 | return status, err
79 | }
80 | status, err = UpdateArticleCommentCount(article)
81 | if err != nil {
82 | return status, err
83 | }
84 | return http.StatusOK, err
85 | }
86 |
--------------------------------------------------------------------------------
/service/articleService/form.go:
--------------------------------------------------------------------------------
1 | package articleService
2 |
3 | // ArticleFilter is a filter for retriving articles.
4 | type ArticleFilter struct {
5 | UserId int `form:"userId" json:"userId"`
6 | Categories []int `form:"categories" json:"categories"`
7 | CurrentPage int `form:"currentPage" json:"currentPage"`
8 | ArticlePerPage int `form:"articlePerPage" json:"articlePerPage"`
9 | }
10 |
11 | // ArticleForm is a form of article.
12 | type ArticleForm struct {
13 | Id uint `form:"id"`
14 | UserId uint `form:"userId"`
15 | CategoryId int `form:"categoryId"`
16 | Title string `form:"title" binding:"required"`
17 | Content string `form:"content" binding:"required"`
18 | Url string `form:"url"`
19 | ImageName string `form:"imageName"`
20 | ThumbnailName string `form:"thumbnailName"`
21 | }
22 |
23 | // ArticlesForm is used when creating or updating multiple Articles.
24 | type ArticlesForm struct {
25 | Articles []ArticleForm `form:"json" binding:"required"`
26 | }
27 |
--------------------------------------------------------------------------------
/service/articleService/liking.go:
--------------------------------------------------------------------------------
1 | package articleService
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 | "github.com/dorajistyle/goyangi/db"
7 | "github.com/dorajistyle/goyangi/form"
8 | "github.com/dorajistyle/goyangi/model"
9 | "github.com/dorajistyle/goyangi/service/userService"
10 | "github.com/dorajistyle/goyangi/service/likingService"
11 | "github.com/dorajistyle/goyangi/util/log"
12 | "github.com/gin-gonic/gin"
13 | "github.com/gin-gonic/gin/binding"
14 | )
15 |
16 | // UpdateArticleLikingCount updates a liking count on article.
17 | func UpdateArticleLikingCount(article *model.Article) (int, error) {
18 | article.LikingCount = db.ORM.Model(article).Association("Likings").Count()
19 | if db.ORM.Save(article).Error != nil {
20 | return http.StatusInternalServerError, errors.New("Article liking's count is not updated.")
21 | }
22 | return http.StatusOK, nil
23 | }
24 |
25 | // CreateLikingOnArticle creates a liking on article.
26 | func CreateLikingOnArticle(c *gin.Context) (int, error) {
27 | article := &model.Article{}
28 | status, err := likingService.CreateLiking(c, article)
29 | if err != nil {
30 | return status, err
31 | }
32 | status, err = UpdateArticleLikingCount(article)
33 | if err != nil {
34 | return status, err
35 | }
36 | return http.StatusOK, nil
37 | }
38 |
39 | // RetrieveLikingsOnArticles retrieves likings on article.
40 | func RetrieveLikingsOnArticles(c *gin.Context) (model.LikingList, int, error) {
41 | var article model.Article
42 | var likingList model.LikingList
43 | var retrieveListForm form.RetrieveListForm
44 |
45 | articleId := c.Params.ByName("id")
46 | log.Debugf("Liking params : %v", c.Params)
47 |
48 | bindErr := c.MustBindWith(&retrieveListForm, binding.Form)
49 | log.Debugf("[RetrieveLikingsOnArticles] bind error : %s\n", bindErr)
50 | if bindErr != nil {
51 | return likingList, http.StatusBadRequest, errors.New("Comments are not retrieved.")
52 | }
53 |
54 |
55 | log.Debugf("retrieveListForm %+v\n", retrieveListForm)
56 | if db.ORM.First(&article, articleId).RecordNotFound() {
57 | return likingList, http.StatusNotFound, errors.New("Article is not found.")
58 | }
59 | currentUser, _ := userService.CurrentUser(c)
60 | likingList = likingService.RetrieveLikings(article, currentUser.Id)
61 | // DEPRECATED likingMeta.SetLikingPageMeta(&likingList, currentPage, hasPrev, hasNext, article.LikingCount, currentUserlikedCount)
62 |
63 | return likingList, http.StatusOK, nil
64 | }
65 |
66 | // DeleteLikingOnArticle deletes liking on article.
67 | func DeleteLikingOnArticle(c *gin.Context) (int, error) {
68 | article := &model.Article{}
69 | status, err := likingService.DeleteLiking(c, article)
70 | if err != nil {
71 | return status, err
72 | }
73 | status, err = UpdateArticleLikingCount(article)
74 | if err != nil {
75 | return status, err
76 | }
77 | return http.StatusOK, nil
78 | }
79 |
--------------------------------------------------------------------------------
/service/commentService/form.go:
--------------------------------------------------------------------------------
1 | package commentService
2 |
3 | // CreateCommentForm is used when creating a comment.
4 | type CreateCommentForm struct {
5 | Content string `form:"content" binding:"required"`
6 | UserId uint `form:"userId"`
7 | }
8 |
9 | // CommentForm is used when updating a comment.
10 | type CommentForm struct {
11 | CommentId uint `form:"commentId" binding:"required"`
12 | Content string `form:"content"`
13 | UserId uint `form:"userId" binding:"required"`
14 | }
15 |
--------------------------------------------------------------------------------
/service/doc.go:
--------------------------------------------------------------------------------
1 | package service
2 | // Copyright 2015 JoongSeob Vito Kim. All rights reserved.
3 | // Use of this source code is governed by a BSD-style
4 | // license that can be found in the LICENSE file.
5 |
6 | /*
7 | Package service - bussiness logics are contained in separated packages.
8 |
9 | HttpStatusCode
10 |
11 | Goyangi uses 11 http status code for bussiness logics. It's from net/http/status.go
12 | StatusOK = 200
13 | StatusCreated = 201
14 | StatusSeeOther = 303
15 | StatusNotModified = 304
16 | StatusBadRequest = 400
17 | StatusUnauthorized = 401
18 | StatusPaymentRequired = 402
19 | StatusForbidden = 403
20 | StatusNotFound = 404
21 | StatusMethodNotAllowed = 405
22 | StatusInternalServerError = 500
23 | */
24 |
--------------------------------------------------------------------------------
/service/likingService/form.go:
--------------------------------------------------------------------------------
1 | package likingService
2 |
3 | // CreateLikingForm is used when creating a liking.
4 | type CreateLikingForm struct {
5 | UserId uint `form:"userId" binding:"required"`
6 | ParentId uint `form:"parentId" binding:"required"`
7 | }
8 |
--------------------------------------------------------------------------------
/service/oauthService/facebook.go:
--------------------------------------------------------------------------------
1 | package oauthService
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "net/http"
7 |
8 | "github.com/dorajistyle/goyangi/util/log"
9 | "github.com/dorajistyle/goyangi/util/modelHelper"
10 | "github.com/dorajistyle/goyangi/util/oauth2"
11 | "github.com/dorajistyle/goyangi/util/oauth2/facebook"
12 | "github.com/gin-gonic/gin"
13 | "github.com/gin-gonic/gin/binding"
14 | )
15 |
16 | // FacebookURL return facebook auth url.
17 | func FacebookURL() (string, int) {
18 | return oauth2.OauthURL(facebook.Config), http.StatusOK
19 | }
20 |
21 | // SetFacebookUser set facebook user.
22 | func SetFacebookUser(response *http.Response) (*FacebookUser, error) {
23 | facebookUser := &FacebookUser{}
24 | defer response.Body.Close()
25 | body, err := ioutil.ReadAll(response.Body)
26 | if err != nil {
27 | return facebookUser, err
28 | }
29 | unmarshalErr := json.Unmarshal(body, &facebookUser)
30 | if unmarshalErr != nil {
31 | return facebookUser, unmarshalErr
32 | }
33 | log.Debugf("\nfacebookUser: %v\n", facebookUser)
34 | return facebookUser, nil
35 | }
36 |
37 | // OauthFacebook link connection and user.
38 | func OauthFacebook(c *gin.Context) (int, error) {
39 | var authResponse oauth2.AuthResponse
40 | var oauthUser OauthUser
41 | bindErr := c.MustBindWith(&authResponse, binding.Form)
42 | log.Debugf("bind error : %s\n", bindErr)
43 | log.Debugf("oauthRedirect form: %v", authResponse)
44 | response, token, err := oauth2.OauthRequest(facebook.RequestURL, facebook.Config, authResponse)
45 | if err != nil {
46 | return http.StatusInternalServerError, err
47 | }
48 | facebookUser, err := SetFacebookUser(response)
49 | modelHelper.AssignValue(&oauthUser, facebookUser)
50 | log.Debugf("\noauthUser item : %v", oauthUser)
51 | if err != nil {
52 | return http.StatusInternalServerError, err
53 | }
54 | status, err := LoginOrCreateOauthUser(c, &oauthUser, facebook.ProviderId, token)
55 | if err != nil {
56 | return status, err
57 | }
58 | return http.StatusSeeOther, nil
59 | }
60 |
61 | // RevokeFacebook revokes facebook oauth connection.
62 | func RevokeFacebook(c *gin.Context) (map[string]bool, int, error) {
63 | return RevokeOauth(c, facebook.ProviderId)
64 | }
65 |
--------------------------------------------------------------------------------
/service/oauthService/github.go:
--------------------------------------------------------------------------------
1 | package oauthService
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "net/http"
7 | "strconv"
8 |
9 | "github.com/dorajistyle/goyangi/util/log"
10 | "github.com/dorajistyle/goyangi/util/modelHelper"
11 | "github.com/dorajistyle/goyangi/util/oauth2"
12 | "github.com/dorajistyle/goyangi/util/oauth2/github"
13 | "github.com/gin-gonic/gin"
14 | "github.com/gin-gonic/gin/binding"
15 | )
16 |
17 | // GithubURL return github auth url.
18 | func GithubURL() (string, int) {
19 | return oauth2.OauthURL(github.Config), http.StatusOK
20 | }
21 |
22 | // SetGithubUser set github user.
23 | func SetGithubUser(response *http.Response) (*GithubUser, error) {
24 | githubUser := &GithubUser{}
25 | defer response.Body.Close()
26 | body, err := ioutil.ReadAll(response.Body)
27 | if err != nil {
28 | return githubUser, err
29 | }
30 | unmarshalErr := json.Unmarshal(body, &githubUser)
31 | if unmarshalErr != nil {
32 | return githubUser, unmarshalErr
33 | }
34 | log.Debugf("\ngithubUser: %v\n", githubUser)
35 | return githubUser, err
36 | }
37 |
38 | // OauthGithub link connection and user.
39 | func OauthGithub(c *gin.Context) (int, error) {
40 | var authResponse oauth2.AuthResponse
41 | var oauthUser OauthUser
42 | bindErr := c.MustBindWith(&authResponse, binding.Form)
43 | log.Debugf("bind error : %s\n", bindErr)
44 | log.Debugf("oauthRedirect form: %v", authResponse)
45 | response, token, err := oauth2.OauthRequest(github.RequestURL, github.Config, authResponse)
46 | if err != nil {
47 | log.Error("get response error", err)
48 | return http.StatusInternalServerError, err
49 | }
50 | githubUser, err := SetGithubUser(response)
51 | if err != nil {
52 | log.Error("SetGithubUser error", err)
53 | return http.StatusInternalServerError, err
54 | }
55 | modelHelper.AssignValue(&oauthUser, githubUser)
56 | oauthUser.ID = strconv.Itoa(githubUser.UserID)
57 | log.Debugf("\noauthUser item : %v", oauthUser)
58 | status, err := LoginOrCreateOauthUser(c, &oauthUser, github.ProviderId, token)
59 | if err != nil {
60 | log.Errorf("LoginOrCreateOauthUser error", err, "status", status)
61 | return status, err
62 | }
63 | return http.StatusSeeOther, nil
64 | }
65 |
66 | // RevokeGithub revokes github oauth connection.
67 | func RevokeGithub(c *gin.Context) (map[string]bool, int, error) {
68 | log.Debug("RevokeGithub performed")
69 | return RevokeOauth(c, github.ProviderId)
70 | }
71 |
--------------------------------------------------------------------------------
/service/oauthService/google.go:
--------------------------------------------------------------------------------
1 | package oauthService
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "net/http"
7 |
8 | "github.com/dorajistyle/goyangi/util/log"
9 | "github.com/dorajistyle/goyangi/util/modelHelper"
10 | "github.com/dorajistyle/goyangi/util/oauth2"
11 | "github.com/dorajistyle/goyangi/util/oauth2/google"
12 | "github.com/gin-gonic/gin"
13 | "github.com/gin-gonic/gin/binding"
14 | )
15 |
16 | // GoogleURL return google auth url.
17 | func GoogleURL() (string, int) {
18 | return oauth2.OauthURL(google.Config), http.StatusOK
19 | }
20 |
21 | // SetGoogleUser set google user.
22 | func SetGoogleUser(response *http.Response) (*GoogleUser, error) {
23 | googleUser := &GoogleUser{}
24 | log.Debugf("\nresponse: %v\n", response)
25 | defer response.Body.Close()
26 | body, err := ioutil.ReadAll(response.Body)
27 | if err != nil {
28 | return googleUser, err
29 | }
30 | unmarshalErr := json.Unmarshal(body, &googleUser)
31 | if unmarshalErr != nil {
32 | return googleUser, unmarshalErr
33 | }
34 | return googleUser, err
35 | }
36 |
37 | // OauthGoogle link connection and user.
38 | func OauthGoogle(c *gin.Context) (int, error) {
39 | log.Debugf("c.Request.URL : %s", c.Request.URL)
40 | var authResponse oauth2.AuthResponse
41 | var oauthUser OauthUser
42 | bindErr := c.MustBindWith(&authResponse, binding.Form)
43 | log.Debugf("bind error : %s\n", bindErr)
44 | log.Debugf("oauthRedirect form: %v", authResponse)
45 | response, token, err := oauth2.OauthRequest(google.RequestURL, google.Config, authResponse)
46 | if err != nil {
47 | return http.StatusInternalServerError, err
48 | }
49 | googleUser, err := SetGoogleUser(response)
50 | if err != nil {
51 | return http.StatusInternalServerError, err
52 | }
53 | modelHelper.AssignValue(&oauthUser, googleUser)
54 | oauthUser.Email = googleUser.Emails[0].Value
55 | oauthUser.ImageUrl = googleUser.Image.URL
56 | status, err := LoginOrCreateOauthUser(c, &oauthUser, google.ProviderId, token)
57 | if err != nil {
58 | return status, err
59 | }
60 | return http.StatusSeeOther, nil
61 | }
62 |
63 | // RevokeGoogle revokes google oauth connection.
64 | func RevokeGoogle(c *gin.Context) (map[string]bool, int, error) {
65 | return RevokeOauth(c, google.ProviderId)
66 | }
67 |
--------------------------------------------------------------------------------
/service/oauthService/kakao.go:
--------------------------------------------------------------------------------
1 | package oauthService
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "net/http"
7 |
8 | "github.com/dorajistyle/goyangi/util/log"
9 | "github.com/dorajistyle/goyangi/util/modelHelper"
10 | "github.com/dorajistyle/goyangi/util/oauth2"
11 | "github.com/dorajistyle/goyangi/util/oauth2/kakao"
12 | "github.com/gin-gonic/gin"
13 | "github.com/gin-gonic/gin/binding"
14 | )
15 |
16 | // KakaoURL return kakao auth url.
17 | func KakaoURL() (string, int) {
18 | return oauth2.OauthURL(kakao.Config), http.StatusOK
19 | }
20 |
21 | // SetKakaoUser set kakao user.
22 | func SetKakaoUser(response *http.Response) (*KakaoUser, error) {
23 | kakaoUser := &KakaoUser{}
24 | defer response.Body.Close()
25 |
26 | log.Debugf("response.Body: %v\n", response.Body)
27 | body, err := ioutil.ReadAll(response.Body)
28 | if err != nil {
29 | return kakaoUser, err
30 | }
31 | unmarshalErr := json.Unmarshal(body, &kakaoUser)
32 | if unmarshalErr != nil {
33 | return kakaoUser, unmarshalErr
34 | }
35 | log.Debugf("\nkakaoUser: %v\n", kakaoUser)
36 | return kakaoUser, err
37 | }
38 |
39 | // OauthKakao link connection and user.
40 | func OauthKakao(c *gin.Context) (int, error) {
41 | var authResponse oauth2.AuthResponse
42 | var oauthUser OauthUser
43 | bindErr := c.MustBindWith(&authResponse, binding.Form)
44 | log.Debugf("bind error : %s\n", bindErr)
45 | log.Debugf("oauthRedirect form: %v", authResponse)
46 | response, token, err := oauth2.OauthRequest(kakao.RequestURL, kakao.Config, authResponse)
47 | if err != nil {
48 | return http.StatusInternalServerError, err
49 | }
50 | kakaoUser, err := SetKakaoUser(response)
51 | if err != nil {
52 | return http.StatusInternalServerError, err
53 | }
54 | modelHelper.AssignValue(&oauthUser, kakaoUser)
55 | log.Debugf("\noauthUser item : %v", oauthUser)
56 | log.Debugf("\noauthUser token : %v", token)
57 | status, err := LoginOrCreateOauthUser(c, &oauthUser, kakao.ProviderId, token)
58 | if err != nil {
59 | return status, err
60 | }
61 | return http.StatusSeeOther, nil
62 | }
63 |
64 | // RevokeKakao revokes kakao oauth connection.
65 | func RevokeKakao(c *gin.Context) (map[string]bool, int, error) {
66 | return RevokeOauth(c, kakao.ProviderId)
67 | }
68 |
--------------------------------------------------------------------------------
/service/oauthService/linkedin.go:
--------------------------------------------------------------------------------
1 | package oauthService
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "net/http"
7 |
8 | "github.com/dorajistyle/goyangi/util/log"
9 | "github.com/dorajistyle/goyangi/util/modelHelper"
10 | "github.com/dorajistyle/goyangi/util/oauth2"
11 | "github.com/dorajistyle/goyangi/util/oauth2/linkedin"
12 | "github.com/gin-gonic/gin"
13 | "github.com/gin-gonic/gin/binding"
14 | )
15 |
16 | // LinkedinUser is a struct that contained linkedin user information.
17 | type LinkedinUser struct {
18 | ID string `json:"id"`
19 | Email string `json:"emailAddress"`
20 | Username string `json:"firstName"`
21 | Name string `json:"lastName""`
22 | ImageUrl string `json:"pictureUrl"`
23 | ProfileUrl string `json:"publicProfileUrl"`
24 | }
25 |
26 | // LinkedinURL return linkedin auth url.
27 | func LinkedinURL() (string, int) {
28 | return oauth2.OauthURL(linkedin.Config), http.StatusOK
29 | }
30 |
31 | // SetLinkedinUser set linkedin user.
32 | func SetLinkedinUser(response *http.Response) (*LinkedinUser, error) {
33 | linkedinUser := &LinkedinUser{}
34 | defer response.Body.Close()
35 | body, err := ioutil.ReadAll(response.Body)
36 | if err != nil {
37 | return linkedinUser, err
38 | }
39 | unmarshalErr := json.Unmarshal(body, &linkedinUser)
40 | if unmarshalErr != nil {
41 | return linkedinUser, unmarshalErr
42 | }
43 | log.Debugf("\nlinkedinUser: %v\n", linkedinUser)
44 | return linkedinUser, err
45 | }
46 |
47 | // OauthLinkedin link connection and user.
48 | func OauthLinkedin(c *gin.Context) (int, error) {
49 | var authResponse oauth2.AuthResponse
50 | var oauthUser OauthUser
51 | bindErr := c.MustBindWith(&authResponse, binding.Form)
52 | log.Debugf("bind error : %s\n", bindErr)
53 | log.Debugf("oauthRedirect form: %v", authResponse)
54 | response, token, err := oauth2.OauthRequest(linkedin.RequestURL, linkedin.Config, authResponse)
55 | if err != nil {
56 | return http.StatusInternalServerError, err
57 | }
58 | linkedinUser, err := SetLinkedinUser(response)
59 | if err != nil {
60 | return http.StatusInternalServerError, err
61 | }
62 | modelHelper.AssignValue(&oauthUser, linkedinUser)
63 | log.Debugf("\noauthUser item : %v", oauthUser)
64 | log.Debugf("\nlinkedinUser id : %s", linkedinUser.ID)
65 | log.Debugf("\noauthUser id : %s", oauthUser.ID)
66 | status, err := LoginOrCreateOauthUser(c, &oauthUser, linkedin.ProviderId, token)
67 | if err != nil {
68 | return status, err
69 | }
70 | return http.StatusSeeOther, nil
71 | }
72 |
73 | // RevokeLinkedin revokes linkedin oauth connection.
74 | func RevokeLinkedin(c *gin.Context) (map[string]bool, int, error) {
75 | return RevokeOauth(c, linkedin.ProviderId)
76 | }
77 |
--------------------------------------------------------------------------------
/service/oauthService/model.go:
--------------------------------------------------------------------------------
1 | package oauthService
2 |
3 | // FacebookUser is a struct that contained facebook user information.
4 | type FacebookUser struct {
5 | ID string `json:"id"`
6 | Username string `json:"name"`
7 | ProfileUrl string `json:"link"`
8 | }
9 |
10 | // GithubUser is a struct that contained github user information.
11 | type GithubUser struct {
12 | UserID int `json:"id"`
13 | Email string `json:"email"`
14 | Username string `json:"login"`
15 | Name string `json:"name""`
16 | ImageUrl string `json:"avatar_url"`
17 | ProfileUrl string `json:"html_url"`
18 | }
19 |
20 | // Email is a struct that contained googleUser's email.
21 | type Email struct {
22 | Value string `json:"value" binding:"required"`
23 | Type string `json:"type" binding:"required"`
24 | }
25 |
26 | // Image is a struct that contained googleUser's image meta data.
27 | type Image struct {
28 | URL string `json:"url" binding:"required"`
29 | IsDefault string `json:"isDefault" binding:"required"`
30 | }
31 |
32 | // GoogleUser is a struct that contained google user information.
33 | type GoogleUser struct {
34 | ID string `json:"id"`
35 | Username string `json:"nickname"`
36 | Emails []Email `json:"emails"`
37 | Name string `json:"displayName"`
38 | Image Image `json:"image"`
39 | ProfileUrl string `json:"url"`
40 | }
41 |
42 | // KakaoUser is a struct that contained kakao user information.
43 | type KakaoUser struct {
44 | ID string `json:"id"`
45 | Email string `json:"email"`
46 | Username string `json:"login"`
47 | Name string `json:"name""`
48 | ImageUrl string `json:"avatar_url"`
49 | ProfileUrl string `json:"html_url"`
50 | }
51 |
52 | // NaverUser is a struct that contained naver user information.
53 | type NaverUser struct {
54 | ID string `json:"id"`
55 | Email string `json:"email"`
56 | Username string `json:"login"`
57 | Name string `json:"name""`
58 | ImageUrl string `json:"avatar_url"`
59 | ProfileUrl string `json:"html_url"`
60 | }
61 |
62 | // TwitterUser is a struct that contained twitter user information.
63 | type TwitterUser struct {
64 | ID string `json:"id"`
65 | Email string `json:"email"`
66 | Name string `json:"name"`
67 | Username string `json:"screen_name"`
68 | ProfileUrl string `json:"url"`
69 | ImageUrl string `json:"profile_image_url"`
70 | }
71 |
72 | // YahooUser is a struct that contained yahoo user information.
73 | type YahooUser struct {
74 | ID string `json:"guid"`
75 | Email string `json:"email"`
76 | Name string `json:"nickname"`
77 | Username string `json:"nickname"`
78 | ImageUrl string `json:"imageURL"`
79 | ProfileUrl string `json:"profileURL"`
80 | }
--------------------------------------------------------------------------------
/service/oauthService/naver.go:
--------------------------------------------------------------------------------
1 | package oauthService
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "net/http"
7 |
8 | "github.com/dorajistyle/goyangi/util/log"
9 | "github.com/dorajistyle/goyangi/util/modelHelper"
10 | "github.com/dorajistyle/goyangi/util/oauth2"
11 | "github.com/dorajistyle/goyangi/util/oauth2/naver"
12 | "github.com/gin-gonic/gin"
13 | "github.com/gin-gonic/gin/binding"
14 | )
15 |
16 | // NaverURL return naver auth url.
17 | func NaverURL() (string, int) {
18 | return oauth2.OauthURL(naver.Config), http.StatusOK
19 | }
20 |
21 | // SetNaverUser set naver user.
22 | func SetNaverUser(response *http.Response) (*NaverUser, error) {
23 | naverUser := &NaverUser{}
24 | defer response.Body.Close()
25 | log.Debugf("response.Body: %v\n", response.Body)
26 | body, err := ioutil.ReadAll(response.Body)
27 | if err != nil {
28 | return naverUser, err
29 | }
30 | unmarshalErr := json.Unmarshal(body, &naverUser)
31 | if unmarshalErr != nil {
32 | return naverUser, unmarshalErr
33 | }
34 | log.Debugf("\nnaverUser: %v\n", naverUser)
35 | return naverUser, err
36 | }
37 |
38 | // OauthNaver link connection and user.
39 | func OauthNaver(c *gin.Context) (int, error) {
40 | var authResponse oauth2.AuthResponse
41 | var oauthUser OauthUser
42 | bindErr := c.MustBindWith(&authResponse, binding.Form)
43 | log.Debugf("bind error : %s\n", bindErr)
44 | log.Debugf("oauthRedirect form: %v", authResponse)
45 | response, token, err := oauth2.OauthRequest(naver.RequestURL, naver.Config, authResponse)
46 | if err != nil {
47 | return http.StatusInternalServerError, err
48 | }
49 | naverUser, err := SetNaverUser(response)
50 | if err != nil {
51 | return http.StatusInternalServerError, err
52 | }
53 | modelHelper.AssignValue(&oauthUser, naverUser)
54 | log.Debugf("\noauthUser item : %v", oauthUser)
55 | log.Debugf("\noauthUser token : %v", token)
56 | status, err := LoginOrCreateOauthUser(c, &oauthUser, naver.ProviderId, token)
57 | if err != nil {
58 | return status, err
59 | }
60 | return http.StatusSeeOther, nil
61 | }
62 |
63 | // RevokeNaver revokes naver oauth connection.
64 | func RevokeNaver(c *gin.Context) (map[string]bool, int, error) {
65 | return RevokeOauth(c, naver.ProviderId)
66 | }
67 |
--------------------------------------------------------------------------------
/service/oauthService/twitter.go:
--------------------------------------------------------------------------------
1 | package oauthService
2 |
3 | import (
4 | // "encoding/json"
5 | "encoding/json"
6 | "io/ioutil"
7 | "net/http"
8 |
9 | "github.com/dorajistyle/goyangi/util/log"
10 | "github.com/dorajistyle/goyangi/util/modelHelper"
11 | "github.com/dorajistyle/goyangi/util/oauth2"
12 | "github.com/dorajistyle/goyangi/util/oauth2/twitter"
13 | "github.com/gin-gonic/gin"
14 | "github.com/gin-gonic/gin/binding"
15 | )
16 |
17 | // TwitterURL return twitter auth url.
18 | func TwitterURL() (string, int) {
19 | return oauth2.OauthURL(twitter.Config), http.StatusOK
20 | }
21 |
22 | // SetTwitterUser set twitter user.
23 | func SetTwitterUser(response *http.Response) (*TwitterUser, error) {
24 | twitterUser := &TwitterUser{}
25 | defer response.Body.Close()
26 | body, err := ioutil.ReadAll(response.Body)
27 | if err != nil {
28 | return twitterUser, err
29 | }
30 | unmarshalErr := json.Unmarshal(body, &twitterUser)
31 | if unmarshalErr != nil {
32 | return twitterUser, unmarshalErr
33 | }
34 | log.Debugf("\ntwitterUser: %v\n", twitterUser)
35 | return twitterUser, err
36 | }
37 |
38 | // OauthTwitter link connection and user.
39 | func OauthTwitter(c *gin.Context) (int, error) {
40 | var authResponse oauth2.AuthResponse
41 | var oauthUser OauthUser
42 | bindErr := c.MustBindWith(&authResponse, binding.Form)
43 | log.Debugf("bind error : %s\n", bindErr)
44 | log.Debugf("oauthRedirect form: %v", authResponse)
45 | response, token, err := oauth2.OauthRequest(twitter.RequestURL, twitter.Config, authResponse)
46 | if err != nil {
47 | return http.StatusInternalServerError, err
48 | }
49 | twitterUser, err := SetTwitterUser(response)
50 | if err != nil {
51 | return http.StatusInternalServerError, err
52 | }
53 | modelHelper.AssignValue(&oauthUser, twitterUser)
54 | log.Debugf("\noauthUser item : %v", oauthUser)
55 | status, err := LoginOrCreateOauthUser(c, &oauthUser, twitter.ProviderId, token)
56 | if err != nil {
57 | return status, err
58 | }
59 | return http.StatusSeeOther, nil
60 | }
61 |
62 | // RevokeTwitter revokes twitter oauth connection.
63 | func RevokeTwitter(c *gin.Context) (map[string]bool, int, error) {
64 | return RevokeOauth(c, twitter.ProviderId)
65 | }
66 |
--------------------------------------------------------------------------------
/service/oauthService/yahoo.go:
--------------------------------------------------------------------------------
1 | package oauthService
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "net/http"
7 |
8 | "github.com/dorajistyle/goyangi/util/log"
9 | "github.com/dorajistyle/goyangi/util/modelHelper"
10 | "github.com/dorajistyle/goyangi/util/oauth2"
11 | "github.com/dorajistyle/goyangi/util/oauth2/yahoo"
12 | "github.com/gin-gonic/gin"
13 | "github.com/gin-gonic/gin/binding"
14 | )
15 |
16 | // YahooURL return yahoo auth url.
17 | func YahooURL() (string, int) {
18 | return oauth2.OauthURL(yahoo.Config), http.StatusOK
19 | }
20 |
21 | // SetYahooUser set yahoo user.
22 | func SetYahooUser(response *http.Response) (*YahooUser, error) {
23 | yahooUser := &YahooUser{}
24 | defer response.Body.Close()
25 | body, err := ioutil.ReadAll(response.Body)
26 | if err != nil {
27 | return yahooUser, err
28 | }
29 | unmarshalErr := json.Unmarshal(body, &yahooUser)
30 | if unmarshalErr != nil {
31 | return yahooUser, unmarshalErr
32 | }
33 | log.Debugf("\nyahooUser: %v\n", yahooUser)
34 | return yahooUser, err
35 | }
36 |
37 | // OauthYahoo link connection and user.
38 | func OauthYahoo(c *gin.Context) (int, error) {
39 | var authResponse oauth2.AuthResponse
40 | var oauthUser OauthUser
41 | bindErr := c.MustBindWith(&authResponse, binding.Form)
42 | log.Debugf("bind error : %s\n", bindErr)
43 | log.Debugf("oauthRedirect form: %v", authResponse)
44 | response, token, err := oauth2.OauthRequest(yahoo.RequestURL, yahoo.Config, authResponse)
45 | if err != nil {
46 | return http.StatusInternalServerError, err
47 | }
48 | yahooUser, err := SetYahooUser(response)
49 | if err != nil {
50 | return http.StatusInternalServerError, err
51 | }
52 | modelHelper.AssignValue(&oauthUser, yahooUser)
53 | log.Debugf("\noauthUser item : %v", oauthUser)
54 | status, err := LoginOrCreateOauthUser(c, &oauthUser, yahoo.ProviderId, token)
55 | if err != nil {
56 | return status, err
57 | }
58 | return http.StatusSeeOther, nil
59 | }
60 |
61 | // RevokeYahoo revokes yahoo oauth connection.
62 | func RevokeYahoo(c *gin.Context) (map[string]bool, int, error) {
63 | return RevokeOauth(c, yahoo.ProviderId)
64 | }
65 |
--------------------------------------------------------------------------------
/service/roleService/form.go:
--------------------------------------------------------------------------------
1 | package roleService
2 |
3 | // RoleForm is used when creating or updating a role.
4 | type RoleForm struct {
5 | Name string `form:"name" binding:"required"`
6 | Description string `form:"description" binding:"required"`
7 | }
8 |
--------------------------------------------------------------------------------
/service/roleService/role.go:
--------------------------------------------------------------------------------
1 | package roleService
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 |
7 | "github.com/dorajistyle/goyangi/db"
8 | "github.com/dorajistyle/goyangi/model"
9 | "github.com/dorajistyle/goyangi/util/log"
10 | "github.com/gin-gonic/gin"
11 | "github.com/gin-gonic/gin/binding"
12 | )
13 |
14 | // CreateRole creates a role.
15 | func CreateRole(c *gin.Context) (model.Role, int, error) {
16 | var form RoleForm
17 | bindErr := c.MustBindWith(&form, binding.Form)
18 | log.Debugf("bind error : %s\n", bindErr)
19 | name := form.Name
20 | description := form.Description
21 | role := model.Role{Name: name, Description: description}
22 | if db.ORM.Create(&role).Error != nil {
23 | return role, http.StatusInternalServerError, errors.New("Role is not created.")
24 | }
25 | return role, http.StatusCreated, nil
26 | }
27 |
28 | // RetrieveRole retrieves a role.
29 | func RetrieveRole(c *gin.Context) (model.Role, int, error) {
30 | var role model.Role
31 | id := c.Params.ByName("id")
32 | if db.ORM.First(&role, id).RecordNotFound() {
33 | return role, http.StatusNotFound, errors.New("Role is not found.")
34 | }
35 | return role, http.StatusOK, nil
36 | }
37 |
38 | // RetrieveRoles retrieves roles.
39 | func RetrieveRoles(c *gin.Context) []model.Role {
40 | var roles []model.Role
41 | db.ORM.Find(&roles)
42 | return roles
43 | }
44 |
45 | // UpdateRole updates a role.
46 | func UpdateRole(c *gin.Context) (model.Role, int, error) {
47 | var role model.Role
48 | var form RoleForm
49 | id := c.Params.ByName("id")
50 | bindErr := c.MustBindWith(&form, binding.Form)
51 | log.Debugf("bind error : %s\n", bindErr)
52 | if db.ORM.First(&role, id).RecordNotFound() {
53 | return role, http.StatusNotFound, errors.New("Role is not found.")
54 | }
55 | role.Name = form.Name
56 | role.Description = form.Description
57 | if db.ORM.Save(&role).Error != nil {
58 | return role, http.StatusInternalServerError, errors.New("Role is not updated.")
59 | }
60 | return role, http.StatusOK, nil
61 | }
62 |
63 | // DeleteRole deletes a role.
64 | func DeleteRole(c *gin.Context) (int, error) {
65 | log.Debug("deleteRole performed")
66 | var role model.Role
67 | id := c.Params.ByName("id")
68 | if db.ORM.First(&role, id).RecordNotFound() {
69 | return http.StatusNotFound, errors.New("Role is not found.")
70 | }
71 | if db.ORM.Delete(&role).Delete(role).Error != nil {
72 | return http.StatusInternalServerError, errors.New("Role is not deleted.")
73 | }
74 | return http.StatusOK, nil
75 | }
76 |
--------------------------------------------------------------------------------
/service/uploadService/form.go:
--------------------------------------------------------------------------------
1 | package uploadService
2 |
3 | // FileForm is used when creating or updating a file.
4 | type FileForm struct {
5 | Id uint `form:"id"`
6 | UserId uint `form:"userId"`
7 | Name string `form:"name" binding:"required"`
8 | Size int `form:"size" binding:"required"`
9 | }
10 |
11 | // FilesForm is used when creating or updating multiple files.
12 | type FilesForm struct {
13 | Files []FileForm `form:"json" binding:"required"`
14 | }
15 |
--------------------------------------------------------------------------------
/service/uploadService/image.go:
--------------------------------------------------------------------------------
1 | package uploadService
2 |
3 | import (
4 | "io"
5 | "mime/multipart"
6 | "net/http"
7 | "strconv"
8 | "sync/atomic"
9 |
10 | "github.com/dorajistyle/goyangi/service/userService"
11 | "github.com/gin-gonic/gin"
12 | "github.com/spf13/viper"
13 |
14 | "github.com/dorajistyle/goyangi/model"
15 | "github.com/dorajistyle/goyangi/util/concurrency"
16 | "github.com/dorajistyle/goyangi/util/log"
17 | "github.com/dorajistyle/goyangi/util/upload"
18 | )
19 |
20 | var (
21 | 업로더 = imageUploader()
22 | ImageUploader = imageUploader()
23 | Cargador = imageUploader()
24 | Shàngchuán = imageUploader()
25 | загрузчик = imageUploader()
26 | s3UploadPath string = ""
27 | user model.User
28 | )
29 |
30 | // imageUploader is a uploader that uploading files.
31 | func imageUploader() concurrency.ConcurrencyManager {
32 | return func(request *http.Request) concurrency.Result {
33 | atomic.AddInt32(concurrency.BusyWorker, 1)
34 | var result concurrency.Result
35 | result.Code = http.StatusOK
36 | var reader *multipart.Reader
37 | var err error
38 | reader, err = request.MultipartReader()
39 | log.Debug("File upload start.")
40 | if err != nil {
41 | log.CheckErrorWithMessage(err, "Uploading failed.")
42 | result.Code = http.StatusInternalServerError
43 | result.Error = err
44 | return result
45 | }
46 | for {
47 | part, err := reader.NextPart()
48 |
49 | uploadedNow := atomic.AddUint32(concurrency.Done, 1)
50 | log.Debugf("count %d", uploadedNow)
51 | if err == io.EOF {
52 | log.Debug("End of file.")
53 | break
54 | }
55 | if part.FileName() == "" {
56 | log.Debug("File name is empty.")
57 | continue
58 | }
59 | err = upload.UploadImageFile(viper.GetString("upload.target"), viper.GetString("app.environment"), s3UploadPath, part)
60 | if err != nil {
61 | log.Error("Image uploading failed. : ", err)
62 | result.Code = http.StatusBadRequest
63 | result.Error = err
64 | return result
65 | }
66 | log.Debug("File uploaded.")
67 | }
68 | log.Debug("Iteration concurrency.Done.")
69 | return result
70 | }
71 | }
72 |
73 | // UploadImages uploads images to a storage.
74 | func UploadImages(c *gin.Context) (int, error) {
75 | r := c.Request
76 | // reader, err := r.MultipartReader()
77 | user, _ = userService.CurrentUser(c)
78 | s3UploadPath = viper.GetString("upload.path.S3Image") + strconv.FormatInt(int64(user.Id), 10) + "/"
79 | // if err != nil {
80 | // return http.StatusInternalServerError, err
81 | // }
82 | return concurrency.Concurrent(r, concurrency.ConcurrencyAgent(r, 업로더, ImageUploader, Cargador, Shàngchuán, загрузчик))
83 | }
84 |
--------------------------------------------------------------------------------
/service/userService/authentication.go:
--------------------------------------------------------------------------------
1 | package userService
2 |
3 | import (
4 | "github.com/dorajistyle/goyangi/util/log"
5 | "github.com/gin-gonic/gin"
6 | "github.com/gin-gonic/gin/binding"
7 | )
8 |
9 | // CreateUserAuthentication creates user authentication.
10 | func CreateUserAuthentication(c *gin.Context) (int, error) {
11 | var form LoginForm
12 | bindErr := c.MustBindWith(&form, binding.Form)
13 | log.Debugf("bind error : %s\n", bindErr)
14 | email := form.Email
15 | pass := form.Password
16 | status, err := SetCookieHandler(c, email, pass)
17 | return status, err
18 | }
19 |
--------------------------------------------------------------------------------
/service/userService/form.go:
--------------------------------------------------------------------------------
1 | package userService
2 |
3 | // RegistrationForm is used when creating a user.
4 | type RegistrationForm struct {
5 | Username string `form:"registrationUsername" binding:"required"`
6 | Email string `form:"registrationEmail" binding:"required"`
7 | Password string `form:"registrationPassword" binding:"required"`
8 | }
9 |
10 | // RegistrationForm is used when creating a user authentication.
11 | type LoginForm struct {
12 | Email string `form:"loginEmail" binding:"required"`
13 | Password string `form:"loginPassword" binding:"required"`
14 | }
15 |
16 | // UserForm is used when updating a user.
17 | type UserForm struct {
18 | Age uint `form:"age"`
19 | Name string `form:"name" binding:"required"`
20 | }
21 |
22 | // PasswordForm is used when updating a user password.
23 | type PasswordForm struct {
24 | CurrentPassword string `form:"currentPassword" binding:"required"`
25 | Password string `form:"newPassword" binding:"required"`
26 | }
27 |
28 | // SendPasswordResetForm is used when sending a password reset token.
29 | type SendPasswordResetForm struct {
30 | Email string `form:"email" binding:"required"`
31 | }
32 |
33 | // PasswordResetForm is used when reseting a password.
34 | type PasswordResetForm struct {
35 | PasswordResetToken string `form:"token" binding:"required"`
36 | Password string `form:"newPassword" binding:"required"`
37 | }
38 |
39 | // VerifyEmailForm is used when verifying an email.
40 | type VerifyEmailForm struct {
41 | ActivationToken string `form:"token" binding:"required"`
42 | }
43 |
44 | // ActivateForm is used when activating user.
45 | type ActivateForm struct {
46 | Activation bool `form:"activation" binding:"required"`
47 | }
48 |
49 | // UserRoleForm is used when adding or removing a role from a user.
50 | type UserRoleForm struct {
51 | UserId int `form:"userId" binding:"required"`
52 | RoleId int `form:"roleId" binding:"required"`
53 | }
54 |
--------------------------------------------------------------------------------
/service/userService/locale/en-us.all.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "link",
4 | "translation": "link"
5 | },
6 | {
7 | "id": "verify_email_title",
8 | "translation": "Verify your email address for Goyangi."
9 | },
10 | {
11 | "id": "verify_email_content",
12 | "translation": "Please click below link to verify your email."
13 | },
14 | {
15 | "id": "reset_password_title",
16 | "translation": "Reset your password for goyangi."
17 | },
18 | {
19 | "id": "reset_password_content",
20 | "translation": "Please click below link to reset your password."
21 | }
22 | ]
23 |
--------------------------------------------------------------------------------
/service/userService/locale/ko-kr.all.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "link",
4 | "translation": "연결"
5 | },
6 | {
7 | "id": "verify_email_title",
8 | "translation": "고양이 서비스 이메일을 검증해주세요."
9 | },
10 | {
11 | "id": "verify_email_content",
12 | "translation": "이메일을 검증하시려면 아래 링크를 클릭하세요."
13 | },
14 | {
15 | "id": "reset_password_title",
16 | "translation": "고양이 서비스 암호 초기화 메일입니다."
17 | },
18 | {
19 | "id": "reset_password_content",
20 | "translation": "암호를 변경하시려면 아래 링크를 클릭하세요."
21 | }
22 | ]
23 |
--------------------------------------------------------------------------------
/service/userService/password.go:
--------------------------------------------------------------------------------
1 | package userService
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 |
7 | "golang.org/x/crypto/bcrypt"
8 |
9 | "time"
10 |
11 | "github.com/dorajistyle/goyangi/db"
12 | "github.com/dorajistyle/goyangi/model"
13 | "github.com/dorajistyle/goyangi/util/crypto"
14 | "github.com/dorajistyle/goyangi/util/email"
15 | "github.com/dorajistyle/goyangi/util/log"
16 | "github.com/dorajistyle/goyangi/util/modelHelper"
17 | "github.com/dorajistyle/goyangi/util/timeHelper"
18 | "github.com/gin-gonic/gin"
19 | "github.com/gin-gonic/gin/binding"
20 | "github.com/nicksnyder/go-i18n/i18n"
21 | "github.com/spf13/viper"
22 | )
23 |
24 | // SendEmailPasswordResetToken sends a password reset token via email.
25 | func SendEmailPasswordResetToken(to string, token string, locale string) error {
26 | T, _ := i18n.Tfunc(locale)
27 | err := email.SendEmailFromAdmin(to,
28 | T("reset_password_title"),
29 | T("link")+" : "+viper.GetString("api.hostURL")+"/reset/password/"+token,
30 | T("reset_password_content")+"
"+viper.GetString("api.hostURL")+"/reset/password/"+token+"")
31 | return err
32 | }
33 |
34 | // SendPasswordResetToken sends a password reset token.
35 | func SendPasswordResetToken(c *gin.Context) (int, error) {
36 | var user model.User
37 | var sendPasswordResetForm SendPasswordResetForm
38 | var err error
39 | log.Debugf("c.Params : %v", c.Params)
40 | bindErr := c.MustBindWith(&sendPasswordResetForm, binding.Form)
41 | log.Debugf("bind error : %s\n", bindErr)
42 | if db.ORM.Where(&model.User{Email: sendPasswordResetForm.Email}).First(&user).RecordNotFound() {
43 | return http.StatusNotFound, errors.New("User is not found. Please Check the email.")
44 | }
45 | user.PasswordResetUntil = timeHelper.TwentyFourHoursLater()
46 | user.PasswordResetToken, err = crypto.GenerateRandomToken16()
47 | if err != nil {
48 | return http.StatusInternalServerError, err
49 | }
50 | log.Debugf("generated token : %s", user.PasswordResetToken)
51 | status, err := UpdateUserCore(&user)
52 | if err != nil {
53 | return status, err
54 | }
55 | err = SendEmailPasswordResetToken(user.Email, user.PasswordResetToken, "en-us")
56 | if err != nil {
57 | return http.StatusInternalServerError, err
58 | }
59 | return http.StatusOK, nil
60 | }
61 |
62 | // ResetPassword resets a password of user.
63 | func ResetPassword(c *gin.Context) (int, error) {
64 | var user model.User
65 | var passwordResetForm PasswordResetForm
66 | bindErr := c.MustBindWith(&passwordResetForm, binding.Form)
67 | log.Debugf("bind error : %s\n", bindErr)
68 | if db.ORM.Where(&model.User{PasswordResetToken: passwordResetForm.PasswordResetToken}).First(&user).RecordNotFound() {
69 | return http.StatusNotFound, errors.New("User is not found.")
70 | }
71 | isExpired := timeHelper.IsExpired(user.PasswordResetUntil)
72 | log.Debugf("passwordResetUntil : %s", user.PasswordResetUntil.UTC())
73 | log.Debugf("expired : %t", isExpired)
74 | if isExpired {
75 | return http.StatusForbidden, errors.New("token not valid.")
76 | }
77 | newPassword, err := bcrypt.GenerateFromPassword([]byte(passwordResetForm.Password), 10)
78 | if err != nil {
79 | return http.StatusInternalServerError, errors.New("User is not updated. Password not Generated.")
80 | }
81 | passwordResetForm.Password = string(newPassword)
82 | log.Debugf("user password before : %s ", user.Password)
83 | modelHelper.AssignValue(&user, &passwordResetForm)
84 | user.PasswordResetToken = ""
85 | user.PasswordResetUntil = time.Now()
86 | log.Debugf("user password after : %s ", user.Password)
87 | status, err := UpdateUserCore(&user)
88 | if err != nil {
89 | return status, err
90 | }
91 | status, err = SetCookie(c, user.Token)
92 | return status, err
93 | }
94 |
--------------------------------------------------------------------------------
/service/userService/userPermission/permission.go:
--------------------------------------------------------------------------------
1 | package userPermission
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 |
7 | "github.com/dorajistyle/goyangi/api/response"
8 | "github.com/dorajistyle/goyangi/model"
9 | "github.com/dorajistyle/goyangi/service/userService"
10 | "github.com/dorajistyle/goyangi/util/log"
11 | "github.com/gin-gonic/gin"
12 | )
13 |
14 | // HasAdmin checks that user has an admin permission.
15 | func HasAdmin(user *model.User) bool {
16 | name := "admin"
17 | for _, role := range user.Roles {
18 | log.Debugf("HasAdmin role.Name : %s", role.Name)
19 | if role.Name == name {
20 | return true
21 | }
22 | }
23 | return false
24 | }
25 |
26 | // CurrentOrAdmin check that user has admin permission or user is the current user.
27 | func CurrentOrAdmin(user *model.User, userId uint) bool {
28 | log.Debugf("user.Id == userId %d %d %s", user.Id, userId, user.Id == userId)
29 | return (HasAdmin(user) || user.Id == userId)
30 | }
31 |
32 | // CurrentUserIdentical check that userId is same as current user's Id.
33 | func CurrentUserIdentical(c *gin.Context, userId uint) (int, error) {
34 | currentUser, err := userService.CurrentUser(c)
35 | if err != nil {
36 | return http.StatusUnauthorized, errors.New("Auth failed.")
37 | }
38 | if currentUser.Id != userId {
39 | return http.StatusForbidden, errors.New("User is not identical.")
40 | }
41 |
42 | return http.StatusOK, nil
43 | }
44 |
45 | // AuthRequired run function when user logged in.
46 | func AuthRequired(f func(c *gin.Context)) gin.HandlerFunc {
47 | return func(c *gin.Context) {
48 | _, err := userService.CurrentUser(c)
49 | if err != nil {
50 | log.Error("Auth failed.", err)
51 | response.KnownErrorJSON(c, http.StatusUnauthorized, "error.loginPlease", errors.New("Auth failed."))
52 | return
53 | }
54 | f(c)
55 | return
56 | }
57 | }
58 |
59 | // AdminRequired run function when user logged in and user has an admin role.
60 | func AdminRequired(f func(c *gin.Context)) gin.HandlerFunc {
61 | return func(c *gin.Context) {
62 | user, err := userService.CurrentUser(c)
63 | if err == nil {
64 | if HasAdmin(&user) {
65 | f(c)
66 | log.Debug("User has admin role.")
67 | return
68 | }
69 | }
70 | log.Error("Admin role required.", err)
71 | response.KnownErrorJSON(c, http.StatusUnauthorized, "error.adminRequired", errors.New("Admin role required."))
72 | return
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/service/userService/verification.go:
--------------------------------------------------------------------------------
1 | package userService
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 | "time"
7 |
8 | "github.com/dorajistyle/goyangi/db"
9 | "github.com/dorajistyle/goyangi/model"
10 | "github.com/dorajistyle/goyangi/util/crypto"
11 | "github.com/dorajistyle/goyangi/util/email"
12 | "github.com/dorajistyle/goyangi/util/log"
13 | "github.com/dorajistyle/goyangi/util/timeHelper"
14 | "github.com/gin-gonic/gin"
15 | "github.com/gin-gonic/gin/binding"
16 | "github.com/nicksnyder/go-i18n/i18n"
17 | "github.com/spf13/viper"
18 | )
19 |
20 | // SendEmailVerfication sends an email verification token via email.
21 | func SendEmailVerfication(to string, token string, locale string) error {
22 | T, _ := i18n.Tfunc(locale)
23 | err := email.SendEmailFromAdmin(to,
24 | T("verify_email_title"),
25 | T("link")+" : "+viper.GetString("api.hostURL")+"/verify/email/"+token,
26 | T("verify_email_content")+"
"+viper.GetString("api.hostURL")+"/verify/email/"+token+"")
27 | return err
28 | }
29 |
30 | // SendVerificationToUser sends an email verification token to user.
31 | func SendVerificationToUser(user model.User) (int, error) {
32 | var status int
33 | var err error
34 | user.ActivateUntil = timeHelper.TwentyFourHoursLater()
35 | user.ActivationToken, err = crypto.GenerateRandomToken32()
36 | if err != nil {
37 | return http.StatusInternalServerError, err
38 | }
39 | user.Activation = false
40 | log.Debugf("generated token : %s", user.ActivationToken)
41 | status, err = UpdateUserCore(&user)
42 | if err != nil {
43 | return status, err
44 | }
45 | err = SendEmailVerfication(user.Email, user.ActivationToken, "en-us")
46 | if err != nil {
47 | return http.StatusInternalServerError, err
48 | }
49 | return http.StatusOK, err
50 | }
51 |
52 | // SendVerification sends an email verification token.
53 | func SendVerification(c *gin.Context) (int, error) {
54 |
55 | var user model.User
56 | currentUser, err := CurrentUser(c)
57 | if err != nil {
58 | return http.StatusUnauthorized, errors.New("Unauthorized.")
59 | }
60 | if db.ORM.First(&user, currentUser.Id).RecordNotFound() {
61 | return http.StatusNotFound, errors.New("User is not found.")
62 | }
63 | status, err := SendVerificationToUser(user)
64 | return status, err
65 | }
66 |
67 | // EmailVerification verifies an email of user.
68 | func EmailVerification(c *gin.Context) (int, error) {
69 | var user model.User
70 | var verifyEmailForm VerifyEmailForm
71 | bindErr := c.MustBindWith(&verifyEmailForm, binding.Form)
72 | log.Debugf("bind error : %s\n", bindErr)
73 | log.Debugf("verifyEmailForm.ActivationToken : %s", verifyEmailForm.ActivationToken)
74 | if db.ORM.Where(&model.User{ActivationToken: verifyEmailForm.ActivationToken}).First(&user).RecordNotFound() {
75 | return http.StatusNotFound, errors.New("User is not found.")
76 | }
77 | isExpired := timeHelper.IsExpired(user.ActivateUntil)
78 | log.Debugf("passwordResetUntil : %s", user.ActivateUntil.UTC())
79 | log.Debugf("expired : %t", isExpired)
80 | if isExpired {
81 | return http.StatusForbidden, errors.New("token not valid.")
82 | }
83 | user.ActivationToken = ""
84 | user.ActivateUntil = time.Now()
85 | user.ActivatedAt = time.Now()
86 | user.Activation = true
87 | status, err := UpdateUserCore(&user)
88 | if err != nil {
89 | return status, err
90 | }
91 | status, err = SetCookie(c, user.Token)
92 | return status, err
93 | }
94 |
--------------------------------------------------------------------------------
/util/authHelper/app.go:
--------------------------------------------------------------------------------
1 | package authHelper
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 |
7 | "github.com/dorajistyle/goyangi/db"
8 | "github.com/dorajistyle/goyangi/model"
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | // GetAuthorizedAppFromContext gets an authorized app from *gin.Context
13 | func GetAuthorizedAppFromContext(c *gin.Context) (model.App, int, error) {
14 | var app model.App
15 | var status int
16 | var err error
17 | _, claims, status, err := AuthenticateServer(c)
18 | if err != nil {
19 | return app, status, err
20 | }
21 | app, status, err = GetAuthorizedApp(claims["ak"], claims["sk"])
22 | return app, status, err
23 | }
24 |
25 | // CreateAuthorizedApp creates an authorized app
26 | func CreateAuthorizedAppAndUser(appKey string, secretkey string, name string, username string) (model.App, int, error) {
27 | var app model.App
28 | var user model.User
29 | var err error
30 | result := db.ORM.First(&app, "key = ? and token = ?", appKey, secretkey)
31 | if result.RowsAffected == 0 {
32 | app.Key = appKey
33 | app.Token = secretkey
34 | app.Name = name
35 | db.ORM.Create(&app)
36 | user.Name = username
37 | user.AppId = app.Id
38 | db.ORM.Create(&user)
39 | }
40 | return app, http.StatusOK, err
41 | }
42 |
43 | // RemoveAuthorizedApp removes an authorized app
44 | func RemoveAuthorizedApp(appKey string, secretkey string) (int, error) {
45 | result := db.ORM.Where("key = ? and token = ?", appKey, secretkey).Delete(&model.App{})
46 | return http.StatusOK, result.Error
47 | }
48 |
49 | // GetAuthorizedApp gets an authorized app from *gin.Context
50 | func GetAuthorizedApp(appKey string, secretkey string) (model.App, int, error) {
51 | var app model.App
52 | var status int
53 | var err error
54 | if db.ORM.First(&app, "key = ? and token = ?", appKey, secretkey).RecordNotFound() {
55 | return app, http.StatusNotFound, errors.New("App is not found.")
56 | }
57 | return app, status, err
58 | }
59 |
--------------------------------------------------------------------------------
/util/authHelper/authHelper_suite_test.go:
--------------------------------------------------------------------------------
1 | package authHelper_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestAuthHelper(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Authhelper Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/authHelper/header.go:
--------------------------------------------------------------------------------
1 | package authHelper
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 | "strings"
7 |
8 | "github.com/dorajistyle/goyangi/util/jwt"
9 | "github.com/dorajistyle/goyangi/util/log"
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | // GetTokenString extract a token from an authentication header.
14 | func GetTokenString(c *gin.Context) (string, int, error) {
15 | var token string
16 | if c.Request == nil || c.Request.Header == nil {
17 | return token, http.StatusBadRequest, errors.New("Request header is empty.")
18 | }
19 | authorization := c.Request.Header.Get("Authorization")
20 | if len(authorization) == 0 {
21 | return token, http.StatusUnauthorized, errors.New("Authorization header is empty.")
22 | }
23 | tokens := strings.Split(authorization, " ")
24 | if len(tokens) < 2 {
25 | return token, http.StatusUnauthorized, errors.New("Authorization header is not valid.")
26 | }
27 | token = tokens[1]
28 | log.Debugf("token : %s\n", token)
29 | return token, http.StatusOK, nil
30 | }
31 |
32 | // AuthenticateClient authenticate a token that generated from client that is valid.
33 | func AuthenticateClient(c *gin.Context) (string, map[string]string, int, error) {
34 | log.Debug("AuthenticateClient executed")
35 | token, status, err := GetTokenString(c)
36 | if err != nil {
37 | return token, nil, status, err
38 | }
39 | claims, status, err := jwt.ValidateTokenClient(token)
40 | if err != nil {
41 | return token, nil, status, err
42 | }
43 | claimArray := make(map[string]string)
44 | claimArray["ak"] = claims["ak"].(string)
45 | claimArray["sk"] = claims["sk"].(string)
46 | claimArray["un"] = claims["un"].(string)
47 | return token, claimArray, status, err
48 | }
49 |
50 | // AuthenticateServer authenticate a token that generated from API server that is valid.
51 | func AuthenticateServer(c *gin.Context) (string, map[string]string, int, error) {
52 | log.Debug("AuthenticateServer executed")
53 | token, status, err := GetTokenString(c)
54 | if err != nil {
55 | return token, nil, status, err
56 | }
57 | claims, status, err := jwt.ValidateTokenServer(token)
58 | if err != nil {
59 | return token, nil, status, err
60 | }
61 | claimArray := make(map[string]string)
62 | claimArray["ak"] = claims["ak"].(string)
63 | claimArray["sk"] = claims["sk"].(string)
64 | claimArray["un"] = claims["un"].(string)
65 | return token, claimArray, status, err
66 | }
67 |
--------------------------------------------------------------------------------
/util/authHelper/user.go:
--------------------------------------------------------------------------------
1 | package authHelper
2 |
3 | import (
4 | "github.com/dorajistyle/goyangi/model"
5 | "github.com/dorajistyle/goyangi/util/userHelper"
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | // GetAuthorizedUserFromContext gets an authorized user from *gin.Context
10 | func GetAuthorizedUserFromContext(c *gin.Context) (model.User, int, error) {
11 | var user model.User
12 | var status int
13 | var err error
14 | _, claims, status, err := AuthenticateServer(c)
15 | if err != nil {
16 | return user, status, err
17 | }
18 | user, status, err = GetAuthorizedUser(claims["ak"], claims["sk"], claims["un"])
19 | return user, status, err
20 | }
21 |
22 | // GetAuthorizedUser gets an authorized user from *gin.Context
23 | func GetAuthorizedUser(appKey string, secretkey string, userName string) (model.User, int, error) {
24 | var app model.App
25 | var user model.User
26 | var status int
27 | var err error
28 | app, status, err = GetAuthorizedApp(appKey, secretkey)
29 | if err != nil {
30 | return user, status, err
31 | }
32 | appId := int64(app.Id)
33 | user, status, err = userHelper.FindUserByUserName(appId, userName)
34 | return user, status, err
35 | }
36 |
--------------------------------------------------------------------------------
/util/aws/aws.go:
--------------------------------------------------------------------------------
1 | package aws
2 |
3 | import (
4 | "github.com/goamz/goamz/aws"
5 | "github.com/spf13/viper"
6 | )
7 |
8 | // Auth return the aws authentication.
9 | func Auth() aws.Auth {
10 | return aws.Auth{
11 | AccessKey: viper.GetString("aws.accessKeyID"),
12 | SecretKey: viper.GetString("aws.secretAccessKey"),
13 | }
14 | }
15 |
16 | // Region return the aws region from string.
17 | func Region(regionName string) aws.Region {
18 | switch regionName {
19 | case "APNortheast":
20 | return aws.APNortheast
21 | case "APSoutheast":
22 | return aws.APSoutheast
23 | case "APSoutheast2":
24 | return aws.APSoutheast2
25 | case "EUCentral":
26 | return aws.EUCentral
27 | case "EUWest":
28 | return aws.EUWest
29 | case "USEast":
30 | return aws.USEast
31 | case "USWest":
32 | return aws.USWest
33 | case "USWest2":
34 | return aws.USWest2
35 | case "USGovWest":
36 | return aws.USGovWest
37 | case "SAEast":
38 | return aws.SAEast
39 | // case "CNNorth":
40 | // return aws.CNNorth
41 | }
42 | return aws.Region{}
43 | }
44 |
--------------------------------------------------------------------------------
/util/aws/s3.go:
--------------------------------------------------------------------------------
1 | package aws
2 |
3 | import (
4 | "bytes"
5 |
6 | "github.com/goamz/goamz/aws"
7 | "github.com/goamz/goamz/s3"
8 | "github.com/spf13/viper"
9 | )
10 |
11 | // Connection create connection of S3.
12 | func Connection(auth aws.Auth, region aws.Region) *s3.S3 {
13 | return s3.New(auth, region)
14 | }
15 |
16 | // Bucket get bucket of S3 via bucket name.
17 | func Bucket(connection *s3.S3, bucketName string) *s3.Bucket {
18 | return connection.Bucket(bucketName)
19 | }
20 |
21 | // List get list from bucket.
22 | func List(bucket *s3.Bucket, prefix, delim, marker string, max int) (*s3.ListResp, error) {
23 | return bucket.List(prefix, delim, marker, max)
24 | }
25 |
26 | // MyTestBucket get bucket from config.
27 | func MyTestBucket() *s3.Bucket {
28 | return Bucket(Connection(Auth(), Region(viper.GetString("aws.s3.region"))), viper.GetString("aws.s3.testBucket.name"))
29 | }
30 |
31 | // MyTestBucketList get list from MyBucket.
32 | func MyTestBucketList(prefix, delim, marker string, max int) (*s3.ListResp, error) {
33 | return List(MyTestBucket(), prefix, delim, marker, max)
34 | }
35 |
36 | // PutToMyTestBucket put a file to a bucket.
37 | func PutToMyTestBucket(prefix string, keyname string, wb *bytes.Buffer, contentType string, aclType string) error {
38 | acl := s3.ACL(aclType)
39 | return MyTestBucket().Put(prefix+keyname, wb.Bytes(), contentType, acl, s3.Options{})
40 | }
41 |
42 | // PutToMyPrivateTestBucket put a file to the MyBucket.
43 | func PutToMyPrivateTestBucket(subdir string, keyname string, wb *bytes.Buffer, contentType string) error {
44 | return PutToMyTestBucket(viper.GetString("aws.s3.bucket.prefix"), subdir+keyname, wb, contentType, "private")
45 | }
46 |
47 | // PutToMyPublicTestBucket put a file to the MyBucket.
48 | func PutToMyPublicTestBucket(subdir string, keyname string, wb *bytes.Buffer, contentType string) error {
49 | return PutToMyTestBucket(viper.GetString("aws.s3.testBucket.prefix"), subdir+keyname, wb, contentType, "public-read")
50 | }
51 |
52 | // DelFromMyTestBucket delete a file from a bucket.
53 | func DelFromMyTestBucket(prefix string, keyname string) error {
54 | return MyTestBucket().Del(viper.GetString("aws.s3.bucket.prefix") + prefix + keyname)
55 | }
56 |
57 | // MyBucket get bucket from config.
58 | func MyBucket() *s3.Bucket {
59 | return Bucket(Connection(Auth(), Region(viper.GetString("aws.s3.region"))), viper.GetString("aws.s3.bucket.name"))
60 | }
61 |
62 | // MyBucketList get list from MyBucket.
63 | func MyBucketList(prefix, delim, marker string, max int) (*s3.ListResp, error) {
64 | return List(MyBucket(), prefix, delim, marker, max)
65 | }
66 |
67 | // PutToMyBucket put a file to a bucket.
68 | func PutToMyBucket(prefix string, keyname string, wb *bytes.Buffer, contentType string, aclType string) error {
69 | acl := s3.ACL(aclType)
70 | return MyBucket().Put(prefix+keyname, wb.Bytes(), contentType, acl, s3.Options{})
71 | }
72 |
73 | // PutToMyPrivateBucket put a file to the MyBucket.
74 | func PutToMyPrivateBucket(subdir string, keyname string, wb *bytes.Buffer, contentType string) error {
75 | return PutToMyBucket(viper.GetString("aws.s3.bucket.prefix"), subdir+keyname, wb, contentType, "private")
76 | }
77 |
78 | // PutToMyPublicBucket put a file to the MyBucket.
79 | func PutToMyPublicBucket(subdir string, keyname string, wb *bytes.Buffer, contentType string) error {
80 | return PutToMyBucket(viper.GetString("aws.s3.bucket.prefix"), subdir+keyname, wb, contentType, "public-read")
81 | }
82 |
83 | // DelFromMyBucket delete a file from a bucket.
84 | func DelFromMyBucket(prefix string, keyname string) error {
85 | return MyBucket().Del(viper.GetString("aws.s3.bucket.prefix") + prefix + keyname)
86 | }
87 |
--------------------------------------------------------------------------------
/util/concurrency/concurrency.go:
--------------------------------------------------------------------------------
1 | package concurrency
2 |
3 | import (
4 | "errors"
5 | "mime/multipart"
6 | "net/http"
7 | "sync/atomic"
8 | "time"
9 |
10 | "github.com/dorajistyle/goyangi/util/log"
11 | "github.com/spf13/viper"
12 | )
13 |
14 | type ConcurrencyStatus bool
15 | type ConcurrencyManagerLegacy func(reader *multipart.Reader) ConcurrencyStatus
16 |
17 | type ConcurrencyManager func(request *http.Request) Result
18 |
19 | var (
20 | Done *uint32 = new(uint32)
21 | BusyWorker *int32 = new(int32)
22 | )
23 |
24 | // Result is the struct that contain http status code and error.
25 | type Result struct {
26 | Code int
27 | Error error
28 | }
29 |
30 | // Concurrent.
31 | func Concurrent(request *http.Request, result Result) (int, error) {
32 | c := make(chan Result)
33 | defer close(c)
34 | go func() {
35 | c <- result
36 | }()
37 |
38 | timeout := time.After(viper.GetDuration("upload.timeout") * time.Second)
39 | select {
40 | case res := <-c:
41 | log.Debugf("End of Upload : %v", res)
42 | workingNow := atomic.AddInt32(BusyWorker, -1)
43 | log.Debugf("All files are Done. Working concurrencyer count : %d", workingNow)
44 | if workingNow == 0 {
45 | return res.Code, res.Error
46 | }
47 | case <-timeout:
48 | err := errors.New("Request timed out.")
49 | log.Warnf(err.Error())
50 | return http.StatusBadRequest, err
51 | }
52 | return http.StatusBadRequest, errors.New("Invalid Request.")
53 | }
54 |
55 | // ConcurrencyAgent is loadbalancer of concurrencier.
56 | func ConcurrencyAgent(request *http.Request, replicas ...ConcurrencyManager) Result {
57 | for {
58 | workingNow := atomic.LoadInt32(BusyWorker)
59 | if len(replicas) > int(workingNow) {
60 | break
61 | }
62 | time.Sleep(time.Second)
63 | // log.Debugf("working concurrencyer count full (BusyWorker/replicas) : (%d/%d) ", workingNow, len(replicas))
64 | }
65 | c := make(chan Result)
66 |
67 | concurrencyReplica := func(i int) {
68 | c <- replicas[i](request)
69 | }
70 | workingNow := atomic.LoadInt32(BusyWorker)
71 | log.Debugf("workingNow, len(replicas) : %d %d", workingNow, len(replicas))
72 |
73 | go concurrencyReplica(int(workingNow))
74 | // go concurrencyReplica(0)
75 | return <-c
76 | }
77 |
--------------------------------------------------------------------------------
/util/concurrency/concurrency_suite_test.go:
--------------------------------------------------------------------------------
1 | package concurrency_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestConcurrency(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Concurrency Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/concurrency/concurrency_test.go:
--------------------------------------------------------------------------------
1 | package concurrency_test
2 |
3 | import (
4 | "net/http"
5 | // "github.com/gin-gonic/gin"
6 | "bytes"
7 | "io"
8 | "io/ioutil"
9 | "mime/multipart"
10 | "sync/atomic"
11 |
12 | "github.com/dorajistyle/goyangi/util/concurrency"
13 | viper "github.com/dorajistyle/goyangi/util/viper"
14 | . "github.com/onsi/ginkgo"
15 | . "github.com/onsi/gomega"
16 | )
17 |
18 | func init() {
19 | viper.LoadConfig()
20 | }
21 |
22 | // mockCocurrencytManager
23 | func mockCocurrencytManager() concurrency.ConcurrencyManager {
24 | return func(request *http.Request) concurrency.Result {
25 | atomic.AddInt32(concurrency.BusyWorker, 1)
26 | var result concurrency.Result
27 | result.Code = http.StatusOK
28 | reader, err := request.MultipartReader()
29 |
30 | // user, _ = userService.CurrentUser(c)
31 |
32 | if err != nil {
33 | result.Code = http.StatusInternalServerError
34 | result.Error = err
35 | return result
36 | }
37 | userAgent := request.Header.Get("User-Agent")
38 | var count int
39 | count = 0
40 | for {
41 | count += 1
42 | part, err := reader.NextPart()
43 |
44 | // uploadedNow := atomic.AddUint32(concurrency.Done, 1)
45 |
46 | if err == io.EOF || part == nil {
47 |
48 | break
49 | }
50 | if part.FormName() == "" {
51 |
52 | continue
53 | }
54 | result.Code, result.Error = ConcurrencyRun(part, userAgent)
55 | }
56 | return result
57 | }
58 | }
59 |
60 | func ConcurrencyRun(part *multipart.Part, userAgent string) (int, error) {
61 | var err error
62 | return 200, err
63 | }
64 |
65 | var _ = Describe("concurrency", func() {
66 | var (
67 | err error
68 | req *http.Request
69 | mockManager1 concurrency.ConcurrencyManager
70 | mockManager2 concurrency.ConcurrencyManager
71 | mockManager3 concurrency.ConcurrencyManager
72 | mockManager4 concurrency.ConcurrencyManager
73 | mockManager5 concurrency.ConcurrencyManager
74 | )
75 |
76 | BeforeEach(func() {
77 | mockManager1 = mockCocurrencytManager()
78 | mockManager2 = mockCocurrencytManager()
79 | mockManager3 = mockCocurrencytManager()
80 | mockManager4 = mockCocurrencytManager()
81 | mockManager5 = mockCocurrencytManager()
82 | })
83 |
84 | Describe("Run a concurrent", func() {
85 |
86 | Context("when concurrent finished", func() {
87 | BeforeEach(func() {
88 |
89 | req = &http.Request{
90 | Method: "POST",
91 | Header: http.Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
92 | Body: ioutil.NopCloser(new(bytes.Buffer)),
93 | }
94 | // multipart, err := req.MultipartReader()
95 | // if multipart == nil {
96 | // t.Error("expected multipart;", err)
97 | // }
98 |
99 | // req.Header = Header{"Content-Type": {"text/plain"}}
100 | // multipart, err = req.MultipartReader()
101 | // if multipart != nil {
102 | // t.Error("unexpected multipart for text/plain")
103 | // }
104 |
105 | _, err = concurrency.Concurrent(req, concurrency.ConcurrencyAgent(req, mockManager1, mockManager2, mockManager3, mockManager4, mockManager5))
106 |
107 | })
108 | It("err should be nil.", func() {
109 | Expect(err).To(BeNil())
110 | })
111 |
112 | })
113 | })
114 |
115 | })
116 |
--------------------------------------------------------------------------------
/util/crypto/crypto_suite_test.go:
--------------------------------------------------------------------------------
1 | package crypto_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestCrypto(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Crypto Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/crypto/md5.go:
--------------------------------------------------------------------------------
1 | package crypto
2 |
3 | import (
4 | "crypto/md5"
5 | "fmt"
6 | "strings"
7 | )
8 |
9 | func GenerateMD5Hash(email string) string {
10 | email = strings.ToLower(strings.TrimSpace(email))
11 | hash := md5.New()
12 | hash.Write([]byte(email))
13 | return fmt.Sprintf("%x", hash.Sum(nil))
14 | }
15 |
--------------------------------------------------------------------------------
/util/crypto/md5_test.go:
--------------------------------------------------------------------------------
1 | package crypto_test
2 |
3 | import (
4 | . "github.com/dorajistyle/goyangi/util/crypto"
5 |
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | var _ = Describe("Md5", func() {
11 | var (
12 | emailOne string
13 | emailTwo string
14 | )
15 |
16 | BeforeEach(func() {
17 | emailOne = "testOne@test.com"
18 | emailTwo = "testTwo@test.com"
19 | })
20 |
21 | Describe("Generate MD5 hash from email", func() {
22 | Context("when the MD5 hash of emailOne generated successfully", func() {
23 | It("should be a correct hash", func() {
24 | Expect(GenerateMD5Hash(emailOne)).To(Equal("d45cfb8dfe120832336109537e52d1c7"))
25 | })
26 | })
27 | Context("when the MD5 hash of emailTwo generated successfully", func() {
28 | It("should be a correct hash", func() {
29 | Expect(GenerateMD5Hash(emailTwo)).To(Equal("327e27ad95910153a96d255833c45328"))
30 | })
31 | })
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/util/crypto/token.go:
--------------------------------------------------------------------------------
1 | package crypto
2 |
3 | import (
4 | "crypto/rand"
5 | "fmt"
6 | )
7 |
8 | func GenerateRandomToken16() (string, error) {
9 | return GenerateRandomToken(16)
10 | }
11 |
12 | func GenerateRandomToken32() (string, error) {
13 | return GenerateRandomToken(32)
14 | }
15 |
16 | func GenerateRandomToken(n int) (string, error) {
17 | token := make([]byte, n)
18 | _, err := rand.Read(token)
19 | // %x base 16, lower-case, two characters per byte
20 | return fmt.Sprintf("%x", token), err
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/util/crypto/token_test.go:
--------------------------------------------------------------------------------
1 | package crypto_test
2 |
3 | import (
4 | . "github.com/dorajistyle/goyangi/util/crypto"
5 |
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | var _ = Describe("Token", func() {
11 | var (
12 | num32 int
13 | num64 int
14 | num128 int
15 | token string
16 | err error
17 | )
18 |
19 | BeforeEach(func() {
20 | num32 = 32
21 | num64 = 64
22 | num128 = 128
23 | })
24 |
25 | Describe("Generate random token", func() {
26 | Context("when random token generated successfully", func() {
27 | BeforeEach(func() {
28 | token, err = GenerateRandomToken16()
29 | })
30 | It("should have length 32", func() {
31 | Expect(len(token)).To(Equal(num32))
32 | })
33 | It("should not error", func() {
34 | Expect(err).NotTo(HaveOccurred())
35 | })
36 | })
37 |
38 | Context("when random token generated successfully", func() {
39 | BeforeEach(func() {
40 | token, err = GenerateRandomToken32()
41 | })
42 | It("should have length 64", func() {
43 | Expect(len(token)).To(Equal(num64))
44 | })
45 | It("should not error", func() {
46 | Expect(err).NotTo(HaveOccurred())
47 | })
48 | })
49 |
50 | Context("when random token generated successfully", func() {
51 | BeforeEach(func() {
52 | token, err = GenerateRandomToken(num64)
53 | })
54 | It("should have length 128", func() {
55 | Expect(len(token)).To(Equal(num128))
56 | })
57 | It("should not error", func() {
58 | Expect(err).NotTo(HaveOccurred())
59 | })
60 | })
61 |
62 | })
63 | })
64 |
--------------------------------------------------------------------------------
/util/file/file.go:
--------------------------------------------------------------------------------
1 | package file
2 |
3 | import (
4 | "bytes"
5 | "io"
6 | "os"
7 |
8 | "github.com/spf13/viper"
9 | )
10 |
11 | // UploadPath gets upload path.
12 | func UploadPath() (string, error) {
13 | uploadPath := viper.GetString("upload.path.local")
14 | err := os.MkdirAll(uploadPath, 0777)
15 | return uploadPath, err
16 | }
17 |
18 | // SaveLocal save a file locally.
19 | func SaveLocal(fileName string, wb *bytes.Buffer) error {
20 | uploadPath, err := UploadPath()
21 | if err != nil {
22 | return err
23 | }
24 | var out *os.File
25 | out, err = os.Create(uploadPath + fileName)
26 | defer out.Close()
27 | if err != nil {
28 | return err
29 | }
30 | _, err = io.Copy(out, wb)
31 | return err
32 | }
33 |
34 | // DeleteLocal delete a file locally.
35 | func DeleteLocal(fileName string) error {
36 | uploadPath, err := UploadPath()
37 | if err != nil {
38 | return err
39 | }
40 | err = os.Remove(uploadPath + fileName)
41 | return err
42 | }
43 |
--------------------------------------------------------------------------------
/util/file/file_suite_test.go:
--------------------------------------------------------------------------------
1 | package file_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestFile(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "File Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/file/file_test.go:
--------------------------------------------------------------------------------
1 | package file_test
2 |
3 | import (
4 | "bytes"
5 |
6 | viper "github.com/dorajistyle/goyangi/util/viper"
7 |
8 | . "github.com/dorajistyle/goyangi/util/file"
9 |
10 | . "github.com/onsi/ginkgo"
11 | . "github.com/onsi/gomega"
12 | )
13 |
14 | func init() {
15 | viper.LoadConfig()
16 | }
17 |
18 | var _ = Describe("File", func() {
19 | var (
20 | filename string
21 | err error
22 | wb *bytes.Buffer
23 | )
24 |
25 | BeforeEach(func() {
26 | filename = "testfile.txt"
27 | wb = new(bytes.Buffer)
28 | })
29 |
30 | Describe("Create a directory", func() {
31 | Context("when the uploadPath is created successfully", func() {
32 | BeforeEach(func() {
33 | _, err = UploadPath()
34 | })
35 |
36 | It("should not error", func() {
37 | Expect(err).NotTo(HaveOccurred())
38 | })
39 | })
40 | })
41 | Describe("Save a file to local directory", func() {
42 | Context("when the file saved successfully", func() {
43 | BeforeEach(func() {
44 | err = SaveLocal(filename, wb)
45 | })
46 | It("should not error", func() {
47 | Expect(err).NotTo(HaveOccurred())
48 | })
49 | })
50 | })
51 | Describe("Delete a file to local directory", func() {
52 | Context("when the file deleted successfully", func() {
53 | BeforeEach(func() {
54 | err = SaveLocal(filename, wb)
55 | err = DeleteLocal(filename)
56 | })
57 | It("should not error", func() {
58 | Expect(err).NotTo(HaveOccurred())
59 | })
60 | })
61 | })
62 | })
63 |
--------------------------------------------------------------------------------
/util/ginHelper/request.go:
--------------------------------------------------------------------------------
1 | package ginHelper
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | )
6 |
7 | // PostFormArray returns a slice of strings for a given form key, plus
8 | // a boolean value whether at least one value exists for the given key.
9 | func PostFormArray(c *gin.Context, key string) ([]string, bool) {
10 | req := c.Request
11 | req.ParseForm()
12 | req.ParseMultipartForm(32 << 20) // 32 MB
13 | if values := req.PostForm[key]; len(values) > 0 {
14 | return values, true
15 | }
16 | if req.MultipartForm != nil && req.MultipartForm.File != nil {
17 | if values := req.MultipartForm.Value[key]; len(values) > 0 {
18 | return values, true
19 | }
20 | }
21 | return []string{}, false
22 | }
--------------------------------------------------------------------------------
/util/httpHelper/http.go:
--------------------------------------------------------------------------------
1 | package httpHelper
2 |
3 | import (
4 | // "bytes"
5 | "crypto/tls"
6 | "io/ioutil"
7 | "net/http"
8 | "time"
9 |
10 | "github.com/dorajistyle/goyangi/util/log"
11 | )
12 |
13 | // PostToTarget send a post request to a target address.
14 | func PostToTarget(req *http.Request) (string, int, error) {
15 | client := &http.Client{Timeout: time.Second * 10}
16 | return PostToTargeWithClient(client, req)
17 | }
18 |
19 | // PostToTargetSSLSelfSigned send a post request to a target.
20 | func PostToTargetSSLSelfSigned(req *http.Request) (string, int, error) {
21 | tr := &http.Transport{
22 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
23 | }
24 | client := &http.Client{Transport: tr,
25 | Timeout: time.Second * 10}
26 | return PostToTargeWithClient(client, req)
27 | }
28 |
29 | // PostToTargeWithClient send a post request to a target with custom client.
30 | func PostToTargeWithClient(client *http.Client, req *http.Request) (string, int, error) {
31 | var responseBody string
32 | resp, err := client.Do(req)
33 | if err != nil {
34 | return responseBody, http.StatusBadRequest, err
35 | // panic(err)
36 | }
37 | defer resp.Body.Close()
38 | //
39 | log.Debugf("response Status: %s\n", resp.Status)
40 | log.Debugf("response Headers: %s\n", resp.Header)
41 | body, err := ioutil.ReadAll(resp.Body)
42 | if err != nil {
43 | return responseBody, http.StatusBadRequest, err
44 | // panic(err)
45 | }
46 | responseBody = string(body)
47 | log.Debugf("response Body:\n", responseBody)
48 | return responseBody, http.StatusOK, nil
49 | }
50 |
--------------------------------------------------------------------------------
/util/image/image_suite_test.go:
--------------------------------------------------------------------------------
1 | package image_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestImage(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Image Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/image/testdata/cat.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/util/image/testdata/cat.gif
--------------------------------------------------------------------------------
/util/image/testdata/cat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/util/image/testdata/cat.jpg
--------------------------------------------------------------------------------
/util/image/testdata/cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dorajistyle/goyangi/dffa276e98f80ad1c5fd97cb5339c8a844491725/util/image/testdata/cat.png
--------------------------------------------------------------------------------
/util/interfaceHelper/interfaceHelper.go:
--------------------------------------------------------------------------------
1 | package interfaceHelper
2 |
3 | import (
4 | "errors"
5 | "strconv"
6 | )
7 |
8 | // GetInt64 recognize a type of interface and return it's value
9 | func GetInt64(unknown interface{}) (int64, error) {
10 | switch i := unknown.(type) {
11 | case float32:
12 | return int64(i), nil
13 | case float64:
14 | return int64(i), nil
15 | case int:
16 | return int64(i), nil
17 | case int8:
18 | return int64(i), nil
19 | case int16:
20 | return int64(i), nil
21 | case int32:
22 | return int64(i), nil
23 | case int64:
24 | return i, nil
25 | case uint:
26 | return int64(i), nil
27 | case uint8:
28 | return int64(i), nil
29 | case uint16:
30 | return int64(i), nil
31 | case uint32:
32 | return int64(i), nil
33 | case uint64:
34 | return int64(i), nil
35 | case string:
36 | v, err := strconv.ParseInt(i, 10, 64)
37 | return v, err
38 | // ...other cases...
39 | default:
40 | return -1, errors.New("get int64: unknown value is of incompatible type")
41 | }
42 | }
--------------------------------------------------------------------------------
/util/interfaceHelper/interfaceHelper_suite_test.go:
--------------------------------------------------------------------------------
1 | package interfaceHelper_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestInterfaceHelper(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "InterfaceHelper Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/interfaceHelper/interfaceHelper_test.go:
--------------------------------------------------------------------------------
1 | package interfaceHelper_test
2 |
3 | import (
4 | . "github.com/dorajistyle/goyangi/util/interfaceHelper"
5 | viper "github.com/dorajistyle/goyangi/util/viper"
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | func init() {
11 | viper.LoadConfig()
12 | }
13 |
14 | var _ = Describe("InterfaceHelper", func() {
15 | var (
16 | // inputInt64 interface{}
17 | valueFloat32 float32
18 | valueFloat64 float64
19 | valueInt int
20 | valueInt8 int8
21 | valueInt16 int16
22 | valueInt32 int32
23 | valueInt64 int64
24 | valueUInt uint
25 | valueUInt8 uint8
26 | valueUInt16 uint16
27 | valueUInt32 uint32
28 | valueUInt64 uint64
29 | valueString string
30 | outputInt64 int64
31 | )
32 |
33 | BeforeEach(func() {
34 | valueFloat32 = 124.00
35 | valueFloat64 = 124.00
36 | valueInt = 124
37 | valueInt8 = 124
38 | valueInt16 = 124
39 | valueInt32 = 124
40 | valueInt64 = 124
41 | valueUInt = 124
42 | valueUInt8 = 124
43 | valueUInt16 = 124
44 | valueUInt32 = 124
45 | valueUInt64 = 124
46 | valueString = "124"
47 | })
48 |
49 | Describe("Get int64 value from interface{}", func() {
50 |
51 | Context("when getting the int64 value successfully", func() {
52 | BeforeEach(func() {
53 |
54 | })
55 | It("that casted from float32 type should be valueInt64.", func() {
56 | outputInt64, _ = GetInt64(valueFloat32)
57 | Expect(outputInt64).To(Equal(valueInt64))
58 | })
59 | It("that casted from float64 type should be valueInt64.", func() {
60 | outputInt64, _ = GetInt64(valueFloat64)
61 | Expect(outputInt64).To(Equal(valueInt64))
62 | })
63 | It("that casted from int type should be valueInt64.", func() {
64 | outputInt64, _ = GetInt64(valueInt)
65 | Expect(outputInt64).To(Equal(valueInt64))
66 | })
67 | It("that casted from int8 type should be valueInt64.", func() {
68 | outputInt64, _ = GetInt64(valueInt8)
69 | Expect(outputInt64).To(Equal(valueInt64))
70 | })
71 | It("that casted from int16 type should be valueInt64.", func() {
72 | outputInt64, _ = GetInt64(valueInt16)
73 | Expect(outputInt64).To(Equal(valueInt64))
74 | })
75 | It("that casted from int32 type should be valueInt64.", func() {
76 | outputInt64, _ = GetInt64(valueInt32)
77 | Expect(outputInt64).To(Equal(valueInt64))
78 | })
79 | It("that casted from int64 type should be valueInt64.", func() {
80 | outputInt64, _ = GetInt64(valueInt64)
81 | Expect(outputInt64).To(Equal(valueInt64))
82 | })
83 | It("that casted from uint type should be valueInt64.", func() {
84 | outputInt64, _ = GetInt64(valueUInt)
85 | Expect(outputInt64).To(Equal(valueInt64))
86 | })
87 | It("that casted from uint8 type should be valueInt64.", func() {
88 | outputInt64, _ = GetInt64(valueUInt8)
89 | Expect(outputInt64).To(Equal(valueInt64))
90 | })
91 | It("that casted from uint16 type should be valueInt64.", func() {
92 | outputInt64, _ = GetInt64(valueUInt16)
93 | Expect(outputInt64).To(Equal(valueInt64))
94 | })
95 | It("that casted from uint32 type should be valueInt64.", func() {
96 | outputInt64, _ = GetInt64(valueUInt32)
97 | Expect(outputInt64).To(Equal(valueInt64))
98 | })
99 | It("that casted from uint64 type should be valueInt64.", func() {
100 | outputInt64, _ = GetInt64(valueUInt64)
101 | Expect(outputInt64).To(Equal(valueInt64))
102 | })
103 | It("that casted from string type should be valueInt64.", func() {
104 | outputInt64, _ = GetInt64(valueString)
105 | Expect(outputInt64).To(Equal(valueInt64))
106 | })
107 |
108 | })
109 | })
110 | })
111 |
--------------------------------------------------------------------------------
/util/jwt/jwt_suite_test.go:
--------------------------------------------------------------------------------
1 | package jwt_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestJwt(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Jwt Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/log/error.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "runtime"
5 |
6 | "golang.org/x/exp/slog"
7 | )
8 |
9 | // CheckError check error and return true if error is nil and return false if error is not nil.
10 | func CheckError(err error) bool {
11 | return CheckErrorWithMessage(err, "")
12 | }
13 |
14 | // CheckError check error and return true if error is nil and return false if error is not nil.
15 | func CheckErrorNoStack(err error) bool {
16 | return CheckErrorNoStackWithMessage(err, "")
17 | }
18 |
19 | // CheckErrorWithMessage check error with message and log messages with stack. And then return true if error is nil and return false if error is not nil.
20 | func CheckErrorWithMessage(err error, msg string, args ...interface{}) bool {
21 | if err != nil {
22 | var stack [4096]byte
23 | runtime.Stack(stack[:], false)
24 | // slog.Errorf(msg, args)
25 | if len(args) == 0 {
26 | // slog.Error(msg + fmt.Sprintf("%q\n%s\n", err, stack[:])) #logrus Legacy
27 | slog.Error(msg, err, stack)
28 | } else {
29 | // slog.Error(fmt.Sprintf(msg, args...) + fmt.Sprintf("%q\n%s\n", err, stack[:])) #logrus Legacy
30 | slog.Error(msg, err, stack)
31 | }
32 | // slog.Printf(msg+"\n%q\n%s\n",args, err, stack[:])
33 | return false
34 | }
35 | return true
36 | }
37 |
38 | // CheckErrorNoStackWithMessage check error with message and return true if error is nil and return false if error is not nil.
39 | func CheckErrorNoStackWithMessage(err error, msg string, args ...interface{}) bool {
40 | if err != nil {
41 | if len(args) == 0 {
42 | // slog.Error(msg + fmt.Sprintf("%q\n", err)) #logrus Legacy
43 | slog.Error(msg, err)
44 | } else {
45 | // slog.Error(fmt.Sprintf(msg, args...) + fmt.Sprintf("%q\n", err)) #logrus Legacy
46 | slog.Error(msg, err)
47 | }
48 | return false
49 | }
50 | return true
51 | }
52 |
--------------------------------------------------------------------------------
/util/log/logger.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "net/http"
5 | "os"
6 | "time"
7 |
8 | // "github.com/dorajistyle/goyangi/util/octokit"
9 | "github.com/lmittmann/tint"
10 | "github.com/spf13/viper"
11 | "golang.org/x/exp/slog"
12 | lumberjack "gopkg.in/natefinch/lumberjack.v2"
13 | )
14 |
15 | func LumberJackLogger(filePath string, maxSize int, maxBackups int, maxAge int) *lumberjack.Logger {
16 | return &lumberjack.Logger{
17 | Filename: filePath,
18 | MaxSize: maxSize, // megabytes
19 | MaxBackups: maxBackups,
20 | MaxAge: maxAge, //days
21 | }
22 | }
23 |
24 | func InitLogToStdoutDebug() {
25 |
26 | // var programLevel = new(slog.LevelVar)
27 | // programLevel.Set()
28 | // h := slog.HandlerOptions{Level: programLevel}.NewTextHandler(os.Stdout) # Legacy without tint
29 | h := tint.NewHandler(os.Stdout, &tint.Options{Level: slog.LevelDebug, TimeFormat: time.Kitchen})
30 | slog.SetDefault(slog.New(h))
31 |
32 | }
33 |
34 | func InitLogToStdout() {
35 | h := tint.NewHandler(os.Stdout, &tint.Options{Level: slog.LevelWarn, TimeFormat: time.DateTime})
36 | slog.SetDefault(slog.New(h))
37 | }
38 |
39 | func InitLogToFile() {
40 | out := LumberJackLogger(viper.GetString("log.error.filepath"), viper.GetInt("log.error.maxSize"), viper.GetInt("log.error.maxBackups"), viper.GetInt("log.error.maxAge"))
41 | var programLevel = new(slog.LevelVar)
42 | programLevel.Set(slog.LevelWarn)
43 | h := slog.NewJSONHandler(out, &slog.HandlerOptions{Level: programLevel})
44 | slog.SetDefault(slog.New(h))
45 | }
46 |
47 | // Init slog
48 | func Init(environment string) {
49 |
50 | switch environment {
51 | case "DEVELOPMENT":
52 | InitLogToStdoutDebug()
53 | case "TEST":
54 | InitLogToFile()
55 | case "PRODUCTION":
56 | InitLogToFile()
57 | }
58 | slog.Info("", "Environment", environment)
59 | }
60 |
61 | // Debug logs a message with debug log level.
62 | func Debug(msg string) {
63 | slog.Debug(msg)
64 | }
65 |
66 | // Debugf logs a formatted message with debug log level.
67 | func Debugf(msg string, args ...interface{}) {
68 | slog.Debug(msg, args...)
69 | }
70 |
71 | // Info logs a message with info log level.
72 | func Info(msg string) {
73 | slog.Info(msg)
74 | }
75 |
76 | // Infof logs a formatted message with info log level.
77 | func Infof(msg string, args ...interface{}) {
78 | slog.Info(msg, args...)
79 | }
80 |
81 | // Warn logs a message with warn log level.
82 | func Warn(msg string) {
83 | slog.Warn(msg)
84 | }
85 |
86 | // Warnf logs a formatted message with warn log level.
87 | func Warnf(msg string, args ...interface{}) {
88 | slog.Warn(msg, args...)
89 | }
90 |
91 | // Error logs a message with error log level.
92 | func Error(msg string, err error) {
93 | slog.Error(msg, err)
94 | }
95 |
96 | // Errorf logs a formatted message with error log level.
97 | func Errorf(msg string, err error, args ...any) {
98 | slog.Error(msg, err, args)
99 | }
100 |
101 | // log response body data for debugging
102 | func DebugResponse(response *http.Response) string {
103 | bodyBuffer := make([]byte, 5000)
104 | var str string
105 | count, err := response.Body.Read(bodyBuffer)
106 | for ; count > 0; count, err = response.Body.Read(bodyBuffer) {
107 | if err != nil {
108 | }
109 | str += string(bodyBuffer[:count])
110 | }
111 | slog.Debug("response data : %v", str)
112 | return str
113 | }
114 |
--------------------------------------------------------------------------------
/util/modelHelper/modelHelper.go:
--------------------------------------------------------------------------------
1 | package modelHelper
2 |
3 | import (
4 | "reflect"
5 |
6 | "github.com/dorajistyle/goyangi/util/log"
7 | )
8 |
9 | func IsZeroOfUnderlyingType(x interface{}) bool {
10 | return x == reflect.Zero(reflect.TypeOf(x)).Interface()
11 | }
12 |
13 | // AssignValue assign form values to model.
14 | func AssignValue(model interface{}, form interface{}) {
15 | modelIndirect := reflect.Indirect(reflect.ValueOf(model))
16 | formElem := reflect.ValueOf(form).Elem()
17 | typeOfTForm := formElem.Type()
18 | for i := 0; i < formElem.NumField(); i++ {
19 | modelField := modelIndirect.FieldByName(typeOfTForm.Field(i).Name)
20 | if modelField.IsValid() {
21 | formField := formElem.Field(i)
22 | modelField.Set(formField)
23 | } else {
24 | log.Warnf("modelField : %s - %s", typeOfTForm.Field(i).Name, modelField)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/util/modelHelper/modelHelper_suite_test.go:
--------------------------------------------------------------------------------
1 | package modelHelper_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestModelHelper(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "ModelHelper Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/modelHelper/modelHelper_test.go:
--------------------------------------------------------------------------------
1 | package modelHelper_test
2 |
3 | import (
4 | . "github.com/dorajistyle/goyangi/util/modelHelper"
5 |
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | type TestModel struct {
11 | ID int64
12 | Title string
13 | Order int
14 | }
15 | type TestForm struct {
16 | ID int64
17 | Title string
18 | Level int
19 | }
20 |
21 | var _ = Describe("ModelHelper", func() {
22 | var (
23 | model *TestModel
24 | form *TestForm
25 | )
26 |
27 | BeforeEach(func() {
28 | model = &TestModel{}
29 | form = &TestForm{ID: 4, Title: "goyangi, the beast."}
30 | })
31 |
32 | Describe("Check the interface", func() {
33 | Context("when the type is zero", func() {
34 | It("should equal to false", func() {
35 | Expect(IsZeroOfUnderlyingType(model)).To(Equal(false))
36 | })
37 | })
38 | })
39 | Describe("Assign values of form to model", func() {
40 | Context("when values assigned successfully", func() {
41 | BeforeEach(func() {
42 | AssignValue(model, form)
43 | })
44 | It("should have same ID", func() {
45 | Expect(model.ID).To(Equal(form.ID))
46 | })
47 |
48 | It("should have same title", func() {
49 | Expect(model.Title).To(Equal(form.Title))
50 | })
51 | })
52 | })
53 | })
54 |
--------------------------------------------------------------------------------
/util/oauth2/facebook/facebook.go:
--------------------------------------------------------------------------------
1 | package facebook
2 |
3 | import (
4 | "net/url"
5 |
6 | oauthHelper "github.com/dorajistyle/goyangi/util/oauth2"
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | const (
11 | ProviderId = 4
12 | Scheme = "https"
13 | Host = "graph.facebook.com"
14 | Opaque = "//graph.facebook.com/me"
15 | AuthURL = "https://www.facebook.com/dialog/oauth"
16 | TokenURL = "https://graph.facebook.com/oauth/access_token"
17 | )
18 |
19 | var RequestURL = &url.URL{
20 | Scheme: Scheme,
21 | Host: Host,
22 | Opaque: Opaque,
23 | }
24 |
25 | var Endpoint = oauth2.Endpoint{
26 | AuthURL: AuthURL,
27 | TokenURL: TokenURL,
28 | }
29 |
30 | var Config = Oauth2Config()
31 |
32 | func Oauth2Config() *oauth2.Config {
33 | id, secret, redirectURL := oauthHelper.GetProvider("facebook")
34 | return &oauth2.Config{
35 | ClientID: id,
36 | ClientSecret: secret,
37 | RedirectURL: redirectURL,
38 | Scopes: []string{
39 | "public_profile",
40 | "email",
41 | },
42 | Endpoint: Endpoint,
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/util/oauth2/github/github.go:
--------------------------------------------------------------------------------
1 | package github
2 |
3 | import (
4 | "net/url"
5 |
6 | oauthHelper "github.com/dorajistyle/goyangi/util/oauth2"
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | const (
11 | ProviderId = 2
12 | Scheme = "https"
13 | Host = "api.github.com"
14 | Opaque = "//api.github.com/user"
15 | AuthURL = "https://github.com/login/oauth/authorize"
16 | TokenURL = "https://github.com/login/oauth/access_token"
17 | )
18 |
19 | var RequestURL = &url.URL{
20 | Scheme: Scheme,
21 | Host: Host,
22 | Opaque: Opaque,
23 | }
24 |
25 | var Endpoint = oauth2.Endpoint{
26 | AuthURL: AuthURL,
27 | TokenURL: TokenURL,
28 | }
29 |
30 | var Config = Oauth2Config()
31 |
32 | func Oauth2Config() *oauth2.Config {
33 | id, secret, redirectURL := oauthHelper.GetProvider("github")
34 | return &oauth2.Config{
35 | ClientID: id,
36 | ClientSecret: secret,
37 | RedirectURL: redirectURL,
38 | Scopes: []string{
39 | "user",
40 | "user:email",
41 | },
42 | Endpoint: Endpoint,
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/util/oauth2/google/google.go:
--------------------------------------------------------------------------------
1 | package google
2 |
3 | import (
4 | "net/url"
5 |
6 | oauthHelper "github.com/dorajistyle/goyangi/util/oauth2"
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | const (
11 | ProviderId = 1
12 | Scheme = "https"
13 | Host = "www.googleapis.com"
14 | Opaque = "//www.googleapis.com/plus/v1/people/me"
15 | AuthURL = "https://accounts.google.com/o/oauth2/auth"
16 | TokenURL = "https://accounts.google.com/o/oauth2/token"
17 | )
18 |
19 | var RequestURL = &url.URL{
20 | Scheme: Scheme,
21 | Host: Host,
22 | Opaque: Opaque,
23 | }
24 |
25 | var Endpoint = oauth2.Endpoint{
26 | AuthURL: AuthURL,
27 | TokenURL: TokenURL,
28 | }
29 |
30 | var Config = Oauth2Config()
31 |
32 | func Oauth2Config() *oauth2.Config {
33 | id, secret, redirectURL := oauthHelper.GetProvider("google")
34 | return &oauth2.Config{
35 | ClientID: id,
36 | ClientSecret: secret,
37 | RedirectURL: redirectURL,
38 | Scopes: []string{
39 | "email",
40 | "profile",
41 | "https://www.googleapis.com/auth/plus.login",
42 | "https://www.googleapis.com/auth/plus.profile.emails.read",
43 | },
44 | Endpoint: Endpoint,
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/util/oauth2/helper.go:
--------------------------------------------------------------------------------
1 | // Helper functions of oauth2.
2 |
3 | package oauth2
4 |
5 | import (
6 | "net/url"
7 | "strings"
8 |
9 | "github.com/dorajistyle/goyangi/util/log"
10 | "github.com/spf13/viper"
11 | )
12 |
13 | func GetProvider(provider string) (id string, secret string, redirectURL string) {
14 | // Get provider id, secret, redirectURL from configuration.
15 | configRoot := []string{"oauth.", provider}
16 | configId := append(configRoot, ".id")
17 | configSecret := append(configRoot, ".secret")
18 | id = viper.GetString(strings.Join(configId, ""))
19 | secret = viper.GetString(strings.Join(configSecret, ""))
20 | redirectURL, err := url.JoinPath(viper.GetString("api.hostURL"), viper.GetString("api.url"), "oauth", provider, "redirect")
21 | if err != nil {
22 | log.Error("Redirect url generation failed.", err)
23 | }
24 | return id, secret, redirectURL
25 | }
26 |
--------------------------------------------------------------------------------
/util/oauth2/kakao/kakao.go:
--------------------------------------------------------------------------------
1 | package kakao
2 |
3 | import (
4 | "net/url"
5 |
6 | oauthHelper "github.com/dorajistyle/goyangi/util/oauth2"
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | const (
11 | ProviderId = 7
12 | Scheme = "https"
13 | Host = "kapi.kakao.com"
14 | Opaque = "//kapi.kakao.com/v1/user/me"
15 | AuthURL = "https://kauth.kakao.com/oauth/authorize"
16 | TokenURL = "https://kauth.kakao.com/oauth/token"
17 | )
18 |
19 | var RequestURL = &url.URL{
20 | Scheme: Scheme,
21 | Host: Host,
22 | Opaque: Opaque,
23 | }
24 |
25 | var Endpoint = oauth2.Endpoint{
26 | AuthURL: AuthURL,
27 | TokenURL: TokenURL,
28 | }
29 |
30 | var Config = Oauth2Config()
31 |
32 | func Oauth2Config() *oauth2.Config {
33 | id, secret, redirectURL := oauthHelper.GetProvider("kakao")
34 | return &oauth2.Config{
35 | ClientID: id,
36 | ClientSecret: secret,
37 | RedirectURL: redirectURL,
38 | Scopes: []string{
39 | "Basic_Profile",
40 | },
41 | Endpoint: Endpoint,
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/util/oauth2/linkedin/linkedin.go:
--------------------------------------------------------------------------------
1 | package linkedin
2 |
3 | import (
4 | "net/url"
5 |
6 | oauthHelper "github.com/dorajistyle/goyangi/util/oauth2"
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | const (
11 | ProviderId = 6
12 | Scheme = "https"
13 | Host = "api.linkedin.com"
14 | Opaque = "//api.linkedin.com/v1/people/~:(id,first-name,email-address,picture-url,public-profile-url)?format=json"
15 | AuthURL = "https://www.linkedin.com/uas/oauth2/authorization"
16 | TokenURL = "https://www.linkedin.com/uas/oauth2/accessToken"
17 | )
18 |
19 | var RequestURL = &url.URL{
20 | Scheme: Scheme,
21 | Host: Host,
22 | Opaque: Opaque,
23 | }
24 |
25 | var Endpoint = oauth2.Endpoint{
26 | AuthURL: AuthURL,
27 | TokenURL: TokenURL,
28 | }
29 |
30 | var Config = Oauth2Config()
31 |
32 | func Oauth2Config() *oauth2.Config {
33 | id, secret, redirectURL := oauthHelper.GetProvider("linkedin")
34 | return &oauth2.Config{
35 | ClientID: id,
36 | ClientSecret: secret,
37 | RedirectURL: redirectURL,
38 | Scopes: []string{
39 | "r_emailaddress",
40 | "r_basicprofile",
41 | "r_fullprofile",
42 | },
43 | Endpoint: Endpoint,
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/util/oauth2/naver/naver.go:
--------------------------------------------------------------------------------
1 | package naver
2 |
3 | import (
4 | "net/url"
5 |
6 | oauthHelper "github.com/dorajistyle/goyangi/util/oauth2"
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | const (
11 | ProviderId = 8
12 | Scheme = "https"
13 | Host = "apis.naver.com"
14 | Opaque = "//apis.naver.com/nidlogin/nid/getUserProfile.xml"
15 | AuthURL = "https://nid.naver.com/oauth2.0/authorize"
16 | TokenURL = "https://nid.naver.com//oauth2.0/token"
17 | )
18 |
19 | var RequestURL = &url.URL{
20 | Scheme: Scheme,
21 | Host: Host,
22 | Opaque: Opaque,
23 | }
24 |
25 | var Endpoint = oauth2.Endpoint{
26 | AuthURL: AuthURL,
27 | TokenURL: TokenURL,
28 | }
29 |
30 | var Config = Oauth2Config()
31 |
32 | func Oauth2Config() *oauth2.Config {
33 | id, secret, redirectURL := oauthHelper.GetProvider("naver")
34 | return &oauth2.Config{
35 | ClientID: id,
36 | ClientSecret: secret,
37 | RedirectURL: redirectURL,
38 | Scopes: []string{
39 | "user",
40 | "user:email",
41 | },
42 | Endpoint: Endpoint,
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/util/oauth2/oauth2.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 Google Inc. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // Package oauth2 contains Martini handlers to provide
16 | // user login via an OAuth 2.0 backend.
17 | package oauth2
18 |
19 | import (
20 | "net/http"
21 | "net/url"
22 |
23 | "github.com/dorajistyle/goyangi/util/log"
24 | "golang.org/x/oauth2"
25 | )
26 |
27 | type AuthResponse struct {
28 | State string `form:"state"`
29 | Code string `form:"code"`
30 | Authuser int `form:"authuser"`
31 | NumSessions int `form:"num_sessions"`
32 | prompt string `form:"prompt"`
33 | ImageName string `form:"imageName"`
34 | SessionState string `form:"session_state"`
35 | }
36 |
37 | // Your credentials should be obtained from the Google
38 | // Developer Console (https://console.developers.google.com).
39 | func OauthURL(conf *oauth2.Config) string {
40 | // Redirect user to Google's consent page to ask for permission
41 | // for the scopes specified above.
42 |
43 | url := conf.AuthCodeURL("state")
44 | log.Debugf("\nVisit the URL for the auth dialog: %v\n", url)
45 | return url
46 | }
47 |
48 | func OauthRequest(url *url.URL, conf *oauth2.Config, authResponse AuthResponse) (*http.Response, *oauth2.Token, error) {
49 | var res *http.Response
50 | var req *http.Request
51 | var token *oauth2.Token
52 | var err error
53 |
54 | log.Info("Oauth Redirect performed.")
55 | // Handle the exchange code to initiate a transport.
56 | log.Debugf("AuthResponse :%v\n", authResponse)
57 | token, err = conf.Exchange(oauth2.NoContext, authResponse.Code)
58 | // log.Debugf("Token AccessToken :%s\n", token.AccessToken)
59 | // log.Debugf("Token TokenType :%s\n", token.TokenType)
60 | // log.Debugf("Token RefreshToken :%s\n", token.RefreshToken)
61 | // log.Debugf("Token Expiry :%s\n", token.Expiry)
62 | if err != nil {
63 | return res, token, err
64 | }
65 | // if err == nil {
66 | client := conf.Client(oauth2.NoContext, token)
67 | log.Debugf("url : %s\n", url.String())
68 | req, err = http.NewRequest("GET", "url.String()", nil)
69 | if err != nil {
70 | return res, token, err
71 | }
72 | // if err == nil {
73 | req.URL = url
74 | res, err = client.Do(req)
75 | // if err != nil {
76 | // return res, token, err
77 | // }
78 | return res, token, nil
79 | // if err == nil {
80 | // return res, token, err
81 | // }
82 | // }
83 | // }
84 |
85 | // return nil, token, err
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/util/oauth2/twitter/twitter.go:
--------------------------------------------------------------------------------
1 | package twitter
2 |
3 | import (
4 | "net/url"
5 |
6 | oauthHelper "github.com/dorajistyle/goyangi/util/oauth2"
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | const (
11 | ProviderId = 5
12 | Scheme = "https"
13 | Host = "api.twitter.com"
14 | Opaque = "//api.twitter.com/1.1/account/verify_credentials.json"
15 | AuthURL = "https://api.twitter.com/oauth/authenticate"
16 | TokenURL = "https://api.twitter.com/oauth/access_token"
17 | )
18 |
19 | var RequestURL = &url.URL{
20 | Scheme: Scheme,
21 | Host: Host,
22 | Opaque: Opaque,
23 | }
24 |
25 | var Endpoint = oauth2.Endpoint{
26 | AuthURL: AuthURL,
27 | TokenURL: TokenURL,
28 | }
29 |
30 | var Config = Oauth2Config()
31 |
32 | func Oauth2Config() *oauth2.Config {
33 | id, secret, redirectURL := oauthHelper.GetProvider("twitter")
34 | return &oauth2.Config{
35 | ClientID: id,
36 | ClientSecret: secret,
37 | RedirectURL: redirectURL,
38 | // Scopes: []string{
39 | // ,
40 | // },
41 | Endpoint: Endpoint,
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/util/oauth2/yahoo/yahoo.go:
--------------------------------------------------------------------------------
1 | package yahoo
2 |
3 | import (
4 | "net/url"
5 |
6 | oauthHelper "github.com/dorajistyle/goyangi/util/oauth2"
7 | "golang.org/x/oauth2"
8 | )
9 |
10 | const (
11 | ProviderId = 3
12 | Scheme = "https"
13 | Host = "social.yahooapis.com"
14 | Opaque = "//social.yahooapis.com/v1/user"
15 | AuthURL = "https://api.login.yahoo.com/oauth2/request_auth"
16 | TokenURL = "https://api.login.yahoo.com/oauth2/get_token"
17 | )
18 |
19 | var RequestURL = &url.URL{
20 | Scheme: Scheme,
21 | Host: Host,
22 | Opaque: Opaque,
23 | }
24 |
25 | var Endpoint = oauth2.Endpoint{
26 | AuthURL: AuthURL,
27 | TokenURL: TokenURL,
28 | }
29 |
30 | var Config = Oauth2Config()
31 |
32 | func Oauth2Config() *oauth2.Config {
33 | id, secret, redirectURL := oauthHelper.GetProvider("yahoo")
34 | return &oauth2.Config{
35 | ClientID: id,
36 | ClientSecret: secret,
37 | RedirectURL: redirectURL,
38 | Scopes: []string{
39 | "Read (Shared) Yahoo Profiles",
40 | },
41 | Endpoint: Endpoint,
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/util/pagination/pagination.go:
--------------------------------------------------------------------------------
1 | package pagination
2 |
3 | func Paginate(currentPage int, perPage int, total int) (int, int, bool, bool) {
4 | var hasPrev, hasNext bool
5 | var offset int
6 | if currentPage == 0 {
7 | currentPage = 1
8 | }
9 | offset = (currentPage - 1) * perPage
10 | if currentPage > 1 {
11 | hasPrev = true
12 | }
13 | if total > (currentPage * perPage) {
14 | hasNext = true
15 | }
16 | return offset, currentPage, hasPrev, hasNext
17 | }
18 |
--------------------------------------------------------------------------------
/util/pagination/pagination_suite_test.go:
--------------------------------------------------------------------------------
1 | package pagination_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestPagination(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Pagination Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/pagination/pagination_test.go:
--------------------------------------------------------------------------------
1 | package pagination_test
2 |
3 | import (
4 | . "github.com/dorajistyle/goyangi/util/pagination"
5 | viper "github.com/dorajistyle/goyangi/util/viper"
6 |
7 | . "github.com/onsi/ginkgo"
8 | . "github.com/onsi/gomega"
9 | )
10 |
11 | func init() {
12 | viper.LoadConfig()
13 | }
14 |
15 | var _ = Describe("Pagination", func() {
16 | var (
17 | offset int
18 | currentPage int
19 | perPage int
20 | total int
21 | hasPrev bool
22 | hasNext bool
23 | )
24 |
25 | BeforeEach(func() {
26 | currentPage = 2
27 | perPage = 10
28 | total = 17
29 | })
30 |
31 | Describe("Paginate items", func() {
32 | Context("when ", func() {
33 | BeforeEach(func() {
34 | offset, currentPage, hasPrev, hasNext = Paginate(currentPage, perPage, total)
35 | })
36 |
37 | It("should be 5", func() {
38 | Expect(offset).To(Equal(10))
39 | })
40 |
41 | It("should be 2", func() {
42 | Expect(currentPage).To(Equal(2))
43 | })
44 |
45 | It("should be true", func() {
46 | Expect(hasPrev).To(Equal(true))
47 | })
48 |
49 | It("should be false", func() {
50 | Expect(hasNext).To(Equal(false))
51 | })
52 | })
53 | })
54 |
55 | })
56 |
--------------------------------------------------------------------------------
/util/random/random.go:
--------------------------------------------------------------------------------
1 | package random
2 |
3 | import (
4 | "math/rand"
5 | "time"
6 | )
7 |
8 | const (
9 | Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!#$%&*+=?@^_|-"
10 | )
11 |
12 | func GenerateRandomString(n int) string {
13 | letters := []rune(Letters)
14 | rand.Seed(time.Now().UTC().UnixNano())
15 | randomString := make([]rune, n)
16 | for i := range randomString {
17 | randomString[i] = letters[rand.Intn(len(letters))]
18 | }
19 | return string(randomString)
20 | }
21 |
--------------------------------------------------------------------------------
/util/random/random_suite_test.go:
--------------------------------------------------------------------------------
1 | package random_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestRandom(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Random Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/random/random_test.go:
--------------------------------------------------------------------------------
1 | package random_test
2 |
3 | import (
4 | . "github.com/dorajistyle/goyangi/util/random"
5 |
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | var _ = Describe("Random", func() {
11 | var (
12 | n int
13 | randomString string
14 | )
15 |
16 | BeforeEach(func() {
17 | randomString = GenerateRandomString(n)
18 | })
19 |
20 | Describe("Generate random string", func() {
21 | Context("when random string generated successfully", func() {
22 | It("should have length n", func() {
23 | Expect(len(randomString)).To(Equal(n))
24 | })
25 |
26 | })
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/util/redis/cache.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | import (
4 | "errors"
5 | "net/http"
6 |
7 | "github.com/dorajistyle/goyangi/util/log"
8 | "github.com/dorajistyle/goyangi/util/stringHelper"
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | // responseGenerator types take an *gin.Context and return a string, an int and an error value.
13 | type responseGenerator func(c *gin.Context) (string, int, error)
14 |
15 | // CacheResponse caches a response if redis server is available.
16 | func CacheResponse(c *gin.Context, keyPrefix string, keyBody string, resGenerator responseGenerator) (string, int, error) {
17 | var encrypted string
18 | var status int
19 | var resErr error
20 | var cacheStr string
21 | var cacheKey string
22 | var cacheErr error
23 | cacheErr = errors.New("It is a default error of cache")
24 | cacheKey = stringHelper.ConcatString(keyPrefix, keyBody)
25 | cacheStr, cacheErr = Get(cacheKey)
26 |
27 | if cacheErr != nil {
28 | if resGenerator == nil {
29 | log.Debug("resGenerator is nil")
30 | return encrypted, status, cacheErr
31 | }
32 | encrypted, status, resErr = resGenerator(c)
33 |
34 | log.Debugf("cacheKey : %s\n", cacheKey)
35 | appendErr := Append(cacheKey, encrypted)
36 | if appendErr != nil {
37 | log.Error("Cannot append cache", appendErr)
38 | }
39 |
40 | } else {
41 | log.Debugf("It has cached response. Key : %s\n", cacheKey)
42 | encrypted = cacheStr
43 | status = http.StatusOK
44 | }
45 | return encrypted, status, resErr
46 | }
47 |
--------------------------------------------------------------------------------
/util/redis/redis.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | import (
4 | // "github.com/dorajistyle/goyangi/util/log"
5 |
6 | "github.com/dorajistyle/goyangi/util/log"
7 | "github.com/rueian/rueidis"
8 | "github.com/spf13/viper"
9 | "golang.org/x/net/context"
10 | )
11 |
12 | func GetClient() rueidis.Client {
13 | redisAddr := viper.GetString("redis.addr") + ":" + viper.GetString("redis.port")
14 | options := rueidis.ClientOption{
15 | InitAddress: []string{redisAddr},
16 | }
17 | client, err := rueidis.NewClient(options)
18 | if err != nil {
19 | log.Error("Cannot get ruedis client.", err)
20 | }
21 | return client
22 | }
23 |
24 | func Append(key string, value string) error {
25 | client := GetClient()
26 | defer client.Close()
27 | cmd := client.B().Set().Key(key).Value(value).Build()
28 | res := client.Do(context.Background(), cmd)
29 | return res.Error()
30 | }
31 |
32 | func Get(key string) (value string, err error) {
33 | client := GetClient()
34 | defer client.Close()
35 | cmd := client.B().Get().Key(key).Build()
36 | res := client.Do(context.Background(), cmd)
37 | value, _ = res.ToString()
38 | return value, res.Error()
39 | }
40 |
41 | func Del(key string) error {
42 | client := GetClient()
43 | defer client.Close()
44 | cmd := client.B().Del().Key(key).Build()
45 | res := client.Do(context.Background(), cmd)
46 | return res.Error()
47 | }
48 |
--------------------------------------------------------------------------------
/util/redis/redis_suite_test.go:
--------------------------------------------------------------------------------
1 | package redis_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestRedis(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Redis Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/redis/redis_test.go:
--------------------------------------------------------------------------------
1 | package redis_test
2 |
3 | import (
4 | . "github.com/dorajistyle/goyangi/util/redis"
5 | viper "github.com/dorajistyle/goyangi/util/viper"
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | func init() {
11 | viper.LoadConfig()
12 | }
13 |
14 | var _ = Describe("Redis", func() {
15 | var (
16 | bestSDK string
17 | worstSDK string
18 | )
19 |
20 | BeforeEach(func() {
21 |
22 | bestSDK = "Goyangi SDK"
23 | worstSDK = "Goyak SDK"
24 | Append("bestSDKEver", bestSDK)
25 | Append("worstSDKEver", worstSDK)
26 | })
27 | Describe("get best sdk", func() {
28 |
29 | bestSDKEver, err := Get("bestSDKEver")
30 | // fmt.Println("bestSDKEver: %s", bestSDKEver)
31 | Context("when redis get a bestSDKEver successfully", func() {
32 | It("should equals with Goyangi SDK", func() {
33 | Expect(bestSDKEver).To(Equal(bestSDK))
34 | })
35 | It("should have no error", func() {
36 | Expect(err).To(BeNil())
37 | })
38 |
39 | })
40 | })
41 | Describe("get worst sdk", func() {
42 | worstSdkEver, err := Get("worstSDKEver")
43 | Context("when redis get a worstSDKEver successfully", func() {
44 | It("should equals with NowWorst SDK", func() {
45 | Expect(worstSdkEver).To(Equal(worstSDK))
46 | })
47 | It("should have no error", func() {
48 | Expect(err).To(BeNil())
49 | })
50 |
51 | })
52 | })
53 | Describe("get worst sdk after delete key", func() {
54 | delErr := Del("worstSDKEver")
55 | Context("when redis del a worstSDKEver successfully", func() {
56 | It("should have no error", func() {
57 | Expect(delErr).To(BeNil())
58 | })
59 | })
60 | worstSDKEver, err := Get("worstSDKEver")
61 | Context("when redis get a worstSDKEver successfully", func() {
62 | It("should equals with NowWorst SDK", func() {
63 | Expect(worstSDKEver).To(BeEmpty())
64 | })
65 | It("should have no error", func() {
66 | Expect(err).ToNot(BeNil())
67 | })
68 |
69 | })
70 | })
71 | })
72 |
--------------------------------------------------------------------------------
/util/retrieveHelper/app.go:
--------------------------------------------------------------------------------
1 | package retrieveHelper
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/dorajistyle/goyangi/db"
7 | "github.com/dorajistyle/goyangi/model"
8 | )
9 |
10 | func RetriveAppWithAppKey(appKey string) (model.App, error) {
11 | var app model.App
12 | if db.ORM.Where("key=?", appKey).First(&app).RecordNotFound() {
13 | return app, errors.New("App does not exist.")
14 | }
15 | return app, nil
16 | }
17 |
--------------------------------------------------------------------------------
/util/retrieveHelper/user.go:
--------------------------------------------------------------------------------
1 | package retrieveHelper
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/dorajistyle/goyangi/db"
7 | "github.com/dorajistyle/goyangi/model"
8 | )
9 |
10 | // RetriveUserWithAppIdAndUserName retrieve an user with AppId and user's name
11 | func RetriveUserWithAppIdAndUserName(appId int64, userName string) (model.User, error) {
12 | var user model.User
13 | if db.ORM.Where("app_id=? and name=?", appId, userName).First(&user).RecordNotFound() {
14 | return user, errors.New("User not found. (Check the AppId and the UserName)")
15 | }
16 | return user, nil
17 | }
18 |
--------------------------------------------------------------------------------
/util/stringHelper/stringHelper.go:
--------------------------------------------------------------------------------
1 | package stringHelper
2 |
3 | import "bytes"
4 |
5 | // Concat concatenates string to buffer.
6 | // According to 'Efficient String Concatenation in Go(http://herman.asia/efficient-string-concatenation-in-go)',
7 | // bytes.Buffer is best choice for heavy-duty case.
8 | // You should call buffer.String() to get a concatenated string after all concaternating finished.
9 | func Concat(buffer *bytes.Buffer, str string) {
10 | buffer.WriteString(str)
11 | }
12 |
13 | // ConcatExist concatenates string to string array.
14 | // According to 'Efficient String Concatenation in Go(http://herman.asia/efficient-string-concatenation-in-go)',
15 | // When str is already exist, it's faster than buffer concatenation.
16 | // You should call strings.Join(strs, "") to get a concatenated string after all concaternating finished.
17 | func ConcatExist(strs []string, str string) []string {
18 | return append(strs, str)
19 | }
20 |
21 | // ConcatString concatenates leadingStr and followingStr.
22 | func ConcatString(leadingStr string, followingStr string) string {
23 | var buffer *bytes.Buffer
24 | buffer = new(bytes.Buffer)
25 | buffer.WriteString(leadingStr)
26 | buffer.WriteString(followingStr)
27 | return buffer.String()
28 | }
--------------------------------------------------------------------------------
/util/stringHelper/stringHelper_suite_test.go:
--------------------------------------------------------------------------------
1 | package stringHelper_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestStringHelper(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "StringHelper Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/stringHelper/stringHelper_test.go:
--------------------------------------------------------------------------------
1 | package stringHelper_test
2 |
3 | import (
4 | "bytes"
5 | "strings"
6 |
7 | . "github.com/dorajistyle/goyangi/util/stringHelper"
8 | viper "github.com/dorajistyle/goyangi/util/viper"
9 |
10 | . "github.com/onsi/ginkgo"
11 | . "github.com/onsi/gomega"
12 | )
13 |
14 | func init() {
15 | viper.LoadConfig()
16 | }
17 |
18 | var _ = Describe("StringHelper", func() {
19 | var (
20 | testStr string
21 | strArr []string
22 | lengthOfTestStr int
23 | lengthOfStrArr int
24 | joinedStr string
25 | leadingStr string
26 | followingStr string
27 | concatedString string
28 | concatedLength int
29 | buffer *bytes.Buffer
30 | )
31 |
32 | BeforeEach(func() {
33 | testStr = "Goyangi is a SDK."
34 | leadingStr = "Tanguero y "
35 | followingStr = "Tanguera"
36 | })
37 |
38 | Describe("Concat string using Concat", func() {
39 | BeforeEach(func() {
40 | buffer = new(bytes.Buffer)
41 | Concat(buffer, testStr)
42 | })
43 | Context("when testStr concatenates successfully", func() {
44 | It("should have length concatedLength", func() {
45 | Expect(len(buffer.String())).To(Equal(len(testStr)))
46 | })
47 | })
48 | })
49 |
50 | Describe("Concat string using ConcatExist", func() {
51 | BeforeEach(func() {
52 | lengthOfStrArr = 1
53 | strArr = []string{}
54 | strArr = ConcatExist(strArr, testStr)
55 | lengthOfTestStr = len(testStr)
56 | joinedStr = strings.Join(strArr, "")
57 | })
58 | Context("when testStr appended to strArr successfully", func() {
59 | It("should have length lengthOfStrArr", func() {
60 | Expect(len(strArr)).To(Equal(lengthOfStrArr))
61 | })
62 | })
63 | Context("when strArr joined successfully", func() {
64 | It("should have length lengthOfTestStr", func() {
65 | Expect(len(joinedStr)).To(Equal(lengthOfTestStr))
66 | })
67 | })
68 | })
69 |
70 | Describe("Concat string using ConcatString", func() {
71 | BeforeEach(func() {
72 | concatedString = ConcatString(leadingStr, followingStr)
73 | concatedLength = len(concatedString)
74 | })
75 | Context("when leadingStr and followingStr concatenates successfully", func() {
76 | It("should have length concatedLength", func() {
77 | Expect(concatedLength).To(Equal(len(leadingStr) + len(followingStr)))
78 | })
79 | })
80 | })
81 |
82 | })
83 |
--------------------------------------------------------------------------------
/util/timeHelper/timeHelper.go:
--------------------------------------------------------------------------------
1 | package timeHelper
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/dorajistyle/goyangi/util/log"
7 | )
8 |
9 | func FewDaysLater(day int) time.Time {
10 | return FewDurationLater(time.Duration(day) * 24 * time.Hour)
11 | }
12 |
13 | func TwentyFourHoursLater() time.Time {
14 | return FewDurationLater(time.Duration(24) * time.Hour)
15 | }
16 |
17 | func SixHoursLater() time.Time {
18 | return FewDurationLater(time.Duration(6) * time.Hour)
19 | }
20 |
21 | func InTimeSpan(start, end, check time.Time) bool {
22 | log.Debugf("check after before: %s %t %t\n", check, check.After(start), check.Before(end))
23 | return check.After(start) && check.Before(end)
24 | }
25 |
26 | func InTimeSpanNow(start, end time.Time) bool {
27 | now := time.Now()
28 | return InTimeSpan(start, end, now)
29 | }
30 |
31 | func FewDurationLater(duration time.Duration) time.Time {
32 | // When Save time should considering UTC
33 | // baseTime := time.Now()
34 | // log.Debugf("basetime : %s", baseTime)
35 | fewDurationLater := time.Now().Add(duration)
36 | log.Debugf("time : %s", fewDurationLater)
37 | return fewDurationLater
38 | }
39 |
40 | func FewDurationLaterMillisecond(duration time.Duration) int64 {
41 | return FewDurationLater(duration).UnixNano() / int64(time.Millisecond)
42 | }
43 |
44 | func IsExpired(expirationTime time.Time) bool {
45 | // baseTime := time.Now()
46 | // log.Debugf("basetime : %s", baseTime)
47 | log.Debugf("expirationTime : %s", expirationTime)
48 | // elapsed := time.Since(expirationTime)
49 | // log.Debugf("elapsed : %s", elapsed)
50 | after := time.Now().After(expirationTime)
51 | log.Debugf("after : %t", after)
52 | return after
53 | }
54 |
--------------------------------------------------------------------------------
/util/timeHelper/timeHelper_suite_test.go:
--------------------------------------------------------------------------------
1 | package timeHelper_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestTimeHelper(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "TimeHelper Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/timeHelper/timeHelper_test.go:
--------------------------------------------------------------------------------
1 | package timeHelper_test
2 |
3 | import (
4 | "time"
5 |
6 | . "github.com/dorajistyle/goyangi/util/timeHelper"
7 | viper "github.com/dorajistyle/goyangi/util/viper"
8 | . "github.com/onsi/ginkgo"
9 | . "github.com/onsi/gomega"
10 | )
11 |
12 | func init() {
13 | viper.LoadConfig()
14 | }
15 |
16 | var _ = Describe("TimeHelper", func() {
17 | var (
18 | duration time.Duration
19 | now time.Time
20 | later time.Time
21 | isExpired bool
22 | )
23 |
24 | BeforeEach(func() {
25 | duration = time.Duration(6) * time.Hour
26 | now = time.Now()
27 | })
28 |
29 | Describe("Get the time that few duration later", func() {
30 |
31 | Context("when getting the time successfully", func() {
32 | BeforeEach(func() {
33 | later = FewDurationLater(duration)
34 | })
35 |
36 | It("should be 6 hours later.", func() {
37 | Expect(later.Hour()).To(Equal(now.Add(duration).Hour()))
38 | })
39 |
40 | })
41 | })
42 |
43 | Describe("Check the time is expired", func() {
44 |
45 | Context("when time expiration checked successfully", func() {
46 | BeforeEach(func() {
47 | isExpired = IsExpired(now)
48 | })
49 |
50 | It("should be expired.", func() {
51 | Expect(isExpired).To(Equal(true))
52 | })
53 |
54 | })
55 | })
56 |
57 | })
58 |
--------------------------------------------------------------------------------
/util/userHelper/user.go:
--------------------------------------------------------------------------------
1 | package userHelper
2 |
3 | import (
4 | "github.com/dorajistyle/goyangi/db"
5 | "github.com/dorajistyle/goyangi/model"
6 |
7 | // "github.com/gin-gonic/gin"
8 | "errors"
9 | "net/http"
10 |
11 | "github.com/dorajistyle/goyangi/util/log"
12 | "github.com/dorajistyle/goyangi/util/retrieveHelper"
13 | // "github.com/dorajistyle/goyangi/util/crypto"
14 | )
15 |
16 | // FindUserByUserName creates a user.
17 | func FindUserByUserName(appId int64, userName string) (model.User, int, error) {
18 | var user model.User
19 | var err error
20 | // token := c.Request.Header.Get("X-Auth-Token")
21 | user, err = retrieveHelper.RetriveUserWithAppIdAndUserName(appId, userName)
22 | if err != nil {
23 | return user, http.StatusUnauthorized, err
24 | }
25 | return user, http.StatusOK, nil
26 | }
27 |
28 | // FindOrCreateUser creates a user.
29 | func FindOrCreateUser(appId int64, userName string) (model.User, int, error) {
30 | var user model.User
31 | var err error
32 |
33 | // if len(token) > 0 {
34 | // log.Debug("header token exist.")
35 | // } else {
36 | // token, err = Token(c)
37 | // log.Debug("header token not exist.")
38 | // if err != nil {
39 | // return user, http.StatusUnauthorized, err
40 | // }
41 | // }
42 | log.Debugf("userName : %s\n", userName)
43 | // log.Debugf("Error : %s\n", err.Error())
44 | user, err = retrieveHelper.RetriveUserWithAppIdAndUserName(appId, userName)
45 | if err != nil {
46 | var user model.User
47 | // return user, http.StatusBadRequest, err
48 | user.Name = userName
49 | // user.Token = token
50 | user.AppId = uint(appId)
51 | log.Debugf("user %+v\n", user)
52 | if db.ORM.Create(&user).Error != nil {
53 | return user, http.StatusBadRequest, errors.New("User is not created.")
54 | }
55 | log.Debugf("retrived User %v\n", user)
56 | return user, http.StatusOK, nil
57 | }
58 | return user, http.StatusBadRequest, nil
59 | }
60 |
--------------------------------------------------------------------------------
/util/validation/validation.go:
--------------------------------------------------------------------------------
1 | package validation
2 |
3 | import (
4 | "regexp"
5 |
6 | "github.com/dorajistyle/goyangi/util/log"
7 | )
8 |
9 | const EMAIL_REGEX = `(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})`
10 |
11 | func EmailValidation(email string) bool {
12 | exp, err := regexp.Compile(EMAIL_REGEX)
13 | if regexpCompiled := log.CheckError(err); regexpCompiled {
14 | if exp.MatchString(email) {
15 | return true
16 | }
17 | }
18 | return false
19 | }
20 |
--------------------------------------------------------------------------------
/util/validation/validation_suite_test.go:
--------------------------------------------------------------------------------
1 | package validation_test
2 |
3 | import (
4 | . "github.com/onsi/ginkgo"
5 | . "github.com/onsi/gomega"
6 |
7 | "testing"
8 | )
9 |
10 | func TestValidation(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Validation Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/util/validation/validation_test.go:
--------------------------------------------------------------------------------
1 | package validation_test
2 |
3 | import (
4 | . "github.com/dorajistyle/goyangi/util/validation"
5 | viper "github.com/dorajistyle/goyangi/util/viper"
6 | . "github.com/onsi/ginkgo"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | func init() {
11 | viper.LoadConfig()
12 | }
13 |
14 | var _ = Describe("Validation", func() {
15 | var (
16 | emailValid string
17 | emailInvalid string
18 | isValid bool
19 | )
20 |
21 | BeforeEach(func() {
22 | emailValid = "test@goyangi.github.io"
23 | emailInvalid = "test#goyangi.github.io"
24 | })
25 |
26 | Describe("Check that the email address is valid or not", func() {
27 |
28 | Context("when the email checked successfully", func() {
29 | BeforeEach(func() {
30 | isValid = EmailValidation(emailValid)
31 | })
32 |
33 | It("should be valid.", func() {
34 | Expect(isValid).To(Equal(true))
35 | })
36 | })
37 |
38 | Context("when the email checked successfully", func() {
39 | BeforeEach(func() {
40 | isValid = EmailValidation(emailInvalid)
41 | })
42 |
43 | It("should be invalid.", func() {
44 | Expect(isValid).To(Equal(false))
45 | })
46 | })
47 | })
48 |
49 | })
50 |
--------------------------------------------------------------------------------
/util/viper/viper.go:
--------------------------------------------------------------------------------
1 | package viper
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/spf13/viper"
7 | )
8 |
9 | func LoadConfig() {
10 | viper.AddConfigPath("../../.")
11 | viper.SetConfigType("yaml")
12 |
13 | // switch os.Getenv("ENV") {
14 | // case "DEVELOPMENT":
15 | // viper.SetConfigName(".env.dev")
16 | // case "TEST":
17 | // viper.SetConfigName(".env.test")
18 | // case "PRODUCTION":
19 | // viper.SetConfigName(".env.prod")
20 | // }
21 |
22 | viper.SetConfigName("config.yml")
23 |
24 | viper.AutomaticEnv()
25 |
26 | if err := viper.ReadInConfig(); err != nil {
27 | log.Fatalln("Fatal error config file", err)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------