Commit 63b2ae620176e76f604e31887ed0819b0b29e4da

Authored by Marko Tikvić
1 parent 087f8fb21c
Exists in master and in 1 other branch v2

renamed files

... ... @@ -0,0 +1,246 @@
  1 +package webutility
  2 +
  3 +import (
  4 + "crypto/rand"
  5 + "crypto/sha256"
  6 + "encoding/hex"
  7 + "errors"
  8 + "net/http"
  9 + "strings"
  10 + "time"
  11 +
  12 + "github.com/dgrijalva/jwt-go"
  13 +)
  14 +
  15 +const OneDay = time.Hour * 24
  16 +const OneWeek = OneDay * 7
  17 +const saltSize = 32
  18 +
  19 +var appName = "webutility"
  20 +var secret = "webutility"
  21 +
  22 +type Role struct {
  23 + Name string `json:"name"`
  24 + ID int64 `json:"id"`
  25 +}
  26 +
  27 +// TokenClaims are JWT token claims.
  28 +type TokenClaims struct {
  29 + // extending a struct
  30 + jwt.StandardClaims
  31 +
  32 + // custom claims
  33 + Token string `json:"access_token"`
  34 + TokenType string `json:"token_type"`
  35 + Username string `json:"username"`
  36 + Role string `json:"role"`
  37 + RoleID int64 `json:"role_id"`
  38 + ExpiresIn int64 `json:"expires_in"`
  39 +}
  40 +
  41 +func InitJWT(appName, secret string) {
  42 + appName = appName
  43 + secret = secret
  44 +}
  45 +
  46 +// ValidateCredentials hashes pass and salt and returns comparison result with resultHash
  47 +func ValidateCredentials(pass, salt, resultHash string) (bool, error) {
  48 + hash, _, err := CreateHash(pass, salt)
  49 + if err != nil {
  50 + return false, err
  51 + }
  52 + res := hash == resultHash
  53 + return res, nil
  54 +}
  55 +
  56 +// CreateHash hashes str using SHA256.
  57 +// If the presalt parameter is not provided CreateHash will generate new salt string.
  58 +// Returns hash and salt strings or an error if it fails.
  59 +func CreateHash(str, presalt string) (hash, salt string, err error) {
  60 + // chech if message is presalted
  61 + if presalt == "" {
  62 + salt, err = randomSalt()
  63 + if err != nil {
  64 + return "", "", err
  65 + }
  66 + } else {
  67 + salt = presalt
  68 + }
  69 +
  70 + // convert strings to raw byte slices
  71 + rawstr := []byte(str)
  72 + rawsalt, err := hex.DecodeString(salt)
  73 + if err != nil {
  74 + return "", "", err
  75 + }
  76 +
  77 + rawdata := make([]byte, len(rawstr)+len(rawsalt))
  78 + rawdata = append(rawdata, rawstr...)
  79 + rawdata = append(rawdata, rawsalt...)
  80 +
  81 + // hash message + salt
  82 + hasher := sha256.New()
  83 + hasher.Write(rawdata)
  84 + rawhash := hasher.Sum(nil)
  85 +
  86 + hash = hex.EncodeToString(rawhash)
  87 + return hash, salt, nil
  88 +}
  89 +
  90 +// CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims.
  91 +// It returns an error if it fails.
  92 +func CreateAuthToken(username string, role Role) (TokenClaims, error) {
  93 + t0 := (time.Now()).Unix()
  94 + t1 := (time.Now().Add(OneWeek)).Unix()
  95 + claims := TokenClaims{
  96 + TokenType: "Bearer",
  97 + Username: username,
  98 + Role: role.Name,
  99 + RoleID: role.ID,
  100 + ExpiresIn: t1 - t0,
  101 + }
  102 + // initialize jwt.StandardClaims fields (anonymous struct)
  103 + claims.IssuedAt = t0
  104 + claims.ExpiresAt = t1
  105 + claims.Issuer = appName
  106 +
  107 + jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
  108 + token, err := jwtToken.SignedString([]byte(secret))
  109 + if err != nil {
  110 + return TokenClaims{}, err
  111 + }
  112 + claims.Token = token
  113 + return claims, nil
  114 +}
  115 +
  116 +// RefreshAuthToken returns new JWT token with sprolongs JWT token's expiration date for one week.
  117 +// It returns new JWT token or an error if it fails.
  118 +func RefreshAuthToken(tok string) (TokenClaims, error) {
  119 + token, err := jwt.ParseWithClaims(tok, &TokenClaims{}, secretFunc)
  120 + if err != nil {
  121 + if validation, ok := err.(*jwt.ValidationError); ok {
  122 + // don't return error if token is expired
  123 + // just extend it
  124 + if !(validation.Errors&jwt.ValidationErrorExpired != 0) {
  125 + return TokenClaims{}, err
  126 + }
  127 + } else {
  128 + return TokenClaims{}, err
  129 + }
  130 + }
  131 +
  132 + // type assertion
  133 + claims, ok := token.Claims.(*TokenClaims)
  134 + if !ok {
  135 + return TokenClaims{}, errors.New("token is not valid")
  136 + }
  137 +
  138 + // extend token expiration date
  139 + return CreateAuthToken(claims.Username, Role{claims.Role, claims.RoleID})
  140 +}
  141 +
  142 +// RbacCheck returns true if user that made HTTP request is authorized to
  143 +// access the resource it is targeting.
  144 +// It exctracts user's role from the JWT token located in Authorization header of
  145 +// http.Request and then compares it with the list of supplied roles and returns
  146 +// true if there's a match, if "*" is provided or if the authRoles is nil.
  147 +// Otherwise it returns false.
  148 +func RbacCheck(req *http.Request, authRoles []string) bool {
  149 + if authRoles == nil {
  150 + return true
  151 + }
  152 +
  153 + // validate token and check expiration date
  154 + claims, err := GetTokenClaims(req)
  155 + if err != nil {
  156 + return false
  157 + }
  158 + // check if token has expired
  159 + if claims.ExpiresAt < (time.Now()).Unix() {
  160 + return false
  161 + }
  162 +
  163 + // check if role extracted from token matches
  164 + // any of the provided (allowed) ones
  165 + for _, r := range authRoles {
  166 + if claims.Role == r || r == "*" {
  167 + return true
  168 + }
  169 + }
  170 +
  171 + return false
  172 +}
  173 +
  174 +// AuthCheck returns token claims and boolean value based on user's rights to access resource specified in req.
  175 +// It exctracts user's role from the JWT token located in Authorization header of
  176 +// HTTP request and then compares it with the list of supplied (authorized);
  177 +// it returns true if there's a match, if "*" is provided or if the authRoles is nil.
  178 +func AuthCheck(req *http.Request, authRoles []string) (*TokenClaims, bool) {
  179 + if authRoles == nil {
  180 + return nil, true
  181 + }
  182 +
  183 + // validate token and check expiration date
  184 + claims, err := GetTokenClaims(req)
  185 + if err != nil {
  186 + return claims, false
  187 + }
  188 + // check if token has expired
  189 + if claims.ExpiresAt < (time.Now()).Unix() {
  190 + return claims, false
  191 + }
  192 +
  193 + // check if role extracted from token matches
  194 + // any of the provided (allowed) ones
  195 + for _, r := range authRoles {
  196 + if claims.Role == r || r == "*" {
  197 + return claims, true
  198 + }
  199 + }
  200 +
  201 + return claims, false
  202 +}
  203 +
  204 +// GetTokenClaims extracts JWT claims from Authorization header of the request.
  205 +// Returns token claims or an error.
  206 +func GetTokenClaims(req *http.Request) (*TokenClaims, error) {
  207 + // check for and strip 'Bearer' prefix
  208 + var tokstr string
  209 + authHead := req.Header.Get("Authorization")
  210 + if ok := strings.HasPrefix(authHead, "Bearer "); ok {
  211 + tokstr = strings.TrimPrefix(authHead, "Bearer ")
  212 + } else {
  213 + return &TokenClaims{}, errors.New("authorization header in incomplete")
  214 + }
  215 +
  216 + token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc)
  217 + if err != nil {
  218 + return &TokenClaims{}, err
  219 + }
  220 +
  221 + // type assertion
  222 + claims, ok := token.Claims.(*TokenClaims)
  223 + if !ok || !token.Valid {
  224 + return &TokenClaims{}, errors.New("token is not valid")
  225 + }
  226 +
  227 + return claims, nil
  228 +}
  229 +
  230 +// randomSalt returns a string of random characters of 'saltSize' length.
  231 +func randomSalt() (s string, err error) {
  232 + rawsalt := make([]byte, saltSize)
  233 +
  234 + _, err = rand.Read(rawsalt)
  235 + if err != nil {
  236 + return "", err
  237 + }
  238 +
  239 + s = hex.EncodeToString(rawsalt)
  240 + return s, nil
  241 +}
  242 +
  243 +// secretFunc returns byte slice of API secret keyword.
  244 +func secretFunc(token *jwt.Token) (interface{}, error) {
  245 + return []byte(secret), nil
  246 +}
... ...
auth_utility.go
... ... @@ -1,246 +0,0 @@
1   -package webutility
2   -
3   -import (
4   - "crypto/rand"
5   - "crypto/sha256"
6   - "encoding/hex"
7   - "errors"
8   - "net/http"
9   - "strings"
10   - "time"
11   -
12   - "github.com/dgrijalva/jwt-go"
13   -)
14   -
15   -const OneDay = time.Hour * 24
16   -const OneWeek = OneDay * 7
17   -const saltSize = 32
18   -
19   -var appName = "webutility"
20   -var secret = "webutility"
21   -
22   -type Role struct {
23   - Name string `json:"name"`
24   - ID int64 `json:"id"`
25   -}
26   -
27   -// TokenClaims are JWT token claims.
28   -type TokenClaims struct {
29   - // extending a struct
30   - jwt.StandardClaims
31   -
32   - // custom claims
33   - Token string `json:"access_token"`
34   - TokenType string `json:"token_type"`
35   - Username string `json:"username"`
36   - Role string `json:"role"`
37   - RoleID int64 `json:"role_id"`
38   - ExpiresIn int64 `json:"expires_in"`
39   -}
40   -
41   -func InitJWT(appName, secret string) {
42   - appName = appName
43   - secret = secret
44   -}
45   -
46   -// ValidateCredentials hashes pass and salt and returns comparison result with resultHash
47   -func ValidateCredentials(pass, salt, resultHash string) (bool, error) {
48   - hash, _, err := CreateHash(pass, salt)
49   - if err != nil {
50   - return false, err
51   - }
52   - res := hash == resultHash
53   - return res, nil
54   -}
55   -
56   -// CreateHash hashes str using SHA256.
57   -// If the presalt parameter is not provided CreateHash will generate new salt string.
58   -// Returns hash and salt strings or an error if it fails.
59   -func CreateHash(str, presalt string) (hash, salt string, err error) {
60   - // chech if message is presalted
61   - if presalt == "" {
62   - salt, err = randomSalt()
63   - if err != nil {
64   - return "", "", err
65   - }
66   - } else {
67   - salt = presalt
68   - }
69   -
70   - // convert strings to raw byte slices
71   - rawstr := []byte(str)
72   - rawsalt, err := hex.DecodeString(salt)
73   - if err != nil {
74   - return "", "", err
75   - }
76   -
77   - rawdata := make([]byte, len(rawstr)+len(rawsalt))
78   - rawdata = append(rawdata, rawstr...)
79   - rawdata = append(rawdata, rawsalt...)
80   -
81   - // hash message + salt
82   - hasher := sha256.New()
83   - hasher.Write(rawdata)
84   - rawhash := hasher.Sum(nil)
85   -
86   - hash = hex.EncodeToString(rawhash)
87   - return hash, salt, nil
88   -}
89   -
90   -// CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims.
91   -// It returns an error if it fails.
92   -func CreateAuthToken(username string, role Role) (TokenClaims, error) {
93   - t0 := (time.Now()).Unix()
94   - t1 := (time.Now().Add(OneWeek)).Unix()
95   - claims := TokenClaims{
96   - TokenType: "Bearer",
97   - Username: username,
98   - Role: role.Name,
99   - RoleID: role.ID,
100   - ExpiresIn: t1 - t0,
101   - }
102   - // initialize jwt.StandardClaims fields (anonymous struct)
103   - claims.IssuedAt = t0
104   - claims.ExpiresAt = t1
105   - claims.Issuer = appName
106   -
107   - jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
108   - token, err := jwtToken.SignedString([]byte(secret))
109   - if err != nil {
110   - return TokenClaims{}, err
111   - }
112   - claims.Token = token
113   - return claims, nil
114   -}
115   -
116   -// RefreshAuthToken returns new JWT token with sprolongs JWT token's expiration date for one week.
117   -// It returns new JWT token or an error if it fails.
118   -func RefreshAuthToken(tok string) (TokenClaims, error) {
119   - token, err := jwt.ParseWithClaims(tok, &TokenClaims{}, secretFunc)
120   - if err != nil {
121   - if validation, ok := err.(*jwt.ValidationError); ok {
122   - // don't return error if token is expired
123   - // just extend it
124   - if !(validation.Errors&jwt.ValidationErrorExpired != 0) {
125   - return TokenClaims{}, err
126   - }
127   - } else {
128   - return TokenClaims{}, err
129   - }
130   - }
131   -
132   - // type assertion
133   - claims, ok := token.Claims.(*TokenClaims)
134   - if !ok {
135   - return TokenClaims{}, errors.New("token is not valid")
136   - }
137   -
138   - // extend token expiration date
139   - return CreateAuthToken(claims.Username, Role{claims.Role, claims.RoleID})
140   -}
141   -
142   -// RbacCheck returns true if user that made HTTP request is authorized to
143   -// access the resource it is targeting.
144   -// It exctracts user's role from the JWT token located in Authorization header of
145   -// http.Request and then compares it with the list of supplied roles and returns
146   -// true if there's a match, if "*" is provided or if the authRoles is nil.
147   -// Otherwise it returns false.
148   -func RbacCheck(req *http.Request, authRoles []string) bool {
149   - if authRoles == nil {
150   - return true
151   - }
152   -
153   - // validate token and check expiration date
154   - claims, err := GetTokenClaims(req)
155   - if err != nil {
156   - return false
157   - }
158   - // check if token has expired
159   - if claims.ExpiresAt < (time.Now()).Unix() {
160   - return false
161   - }
162   -
163   - // check if role extracted from token matches
164   - // any of the provided (allowed) ones
165   - for _, r := range authRoles {
166   - if claims.Role == r || r == "*" {
167   - return true
168   - }
169   - }
170   -
171   - return false
172   -}
173   -
174   -// ProcessRBAC returns token claims and boolean value based on user's rights to access resource specified in req.
175   -// It exctracts user's role from the JWT token located in Authorization header of
176   -// HTTP request and then compares it with the list of supplied (authorized);
177   -// it returns true if there's a match, if "*" is provided or if the authRoles is nil.
178   -func ProcessRBAC(req *http.Request, authRoles []string) (*TokenClaims, bool) {
179   - if authRoles == nil {
180   - return nil, true
181   - }
182   -
183   - // validate token and check expiration date
184   - claims, err := GetTokenClaims(req)
185   - if err != nil {
186   - return claims, false
187   - }
188   - // check if token has expired
189   - if claims.ExpiresAt < (time.Now()).Unix() {
190   - return claims, false
191   - }
192   -
193   - // check if role extracted from token matches
194   - // any of the provided (allowed) ones
195   - for _, r := range authRoles {
196   - if claims.Role == r || r == "*" {
197   - return claims, true
198   - }
199   - }
200   -
201   - return claims, false
202   -}
203   -
204   -// GetTokenClaims extracts JWT claims from Authorization header of the request.
205   -// Returns token claims or an error.
206   -func GetTokenClaims(req *http.Request) (*TokenClaims, error) {
207   - // check for and strip 'Bearer' prefix
208   - var tokstr string
209   - authHead := req.Header.Get("Authorization")
210   - if ok := strings.HasPrefix(authHead, "Bearer "); ok {
211   - tokstr = strings.TrimPrefix(authHead, "Bearer ")
212   - } else {
213   - return &TokenClaims{}, errors.New("authorization header in incomplete")
214   - }
215   -
216   - token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc)
217   - if err != nil {
218   - return &TokenClaims{}, err
219   - }
220   -
221   - // type assertion
222   - claims, ok := token.Claims.(*TokenClaims)
223   - if !ok || !token.Valid {
224   - return &TokenClaims{}, errors.New("token is not valid")
225   - }
226   -
227   - return claims, nil
228   -}
229   -
230   -// randomSalt returns a string of random characters of 'saltSize' length.
231   -func randomSalt() (s string, err error) {
232   - rawsalt := make([]byte, saltSize)
233   -
234   - _, err = rand.Read(rawsalt)
235   - if err != nil {
236   - return "", err
237   - }
238   -
239   - s = hex.EncodeToString(rawsalt)
240   - return s, nil
241   -}
242   -
243   -// secretFunc returns byte slice of API secret keyword.
244   -func secretFunc(token *jwt.Token) (interface{}, error) {
245   - return []byte(secret), nil
246   -}
... ... @@ -0,0 +1,50 @@
  1 +package webutility
  2 +
  3 +import (
  4 + "fmt"
  5 + "time"
  6 +)
  7 +
  8 +// UnixToDate converts given Unix time to local time in format and returns result:
  9 +// YYYY-MM-DD hh:mm:ss +zzzz UTC
  10 +func UnixToDate(unix int64) time.Time {
  11 + return time.Unix(unix, 0)
  12 +}
  13 +
  14 +// DateToUnix converts given date in Unix timestamp.
  15 +func DateToUnix(date interface{}) int64 {
  16 + if date != nil {
  17 + t, ok := date.(time.Time)
  18 + if !ok {
  19 + return 0
  20 + }
  21 + return t.Unix()
  22 +
  23 + }
  24 + return 0
  25 +}
  26 +
  27 +// EqualQuotes encapsulates given string in SQL 'equal' statement and returns result.
  28 +// Example: "hello" -> " = 'hello'"
  29 +func EqualQuotes(stmt string) string {
  30 + if stmt != "" {
  31 + stmt = fmt.Sprintf(" = '%s'", stmt)
  32 + }
  33 + return stmt
  34 +}
  35 +
  36 +func EqualString(stmt string) string {
  37 + if stmt != "" {
  38 + stmt = fmt.Sprintf(" = %s", stmt)
  39 + }
  40 + return stmt
  41 +}
  42 +
  43 +// LikeQuotes encapsulates given string in SQL 'like' statement and returns result.
  44 +// Example: "hello" -> " LIKE UPPER('%hello%')"
  45 +func LikeQuotes(stmt string) string {
  46 + if stmt != "" {
  47 + stmt = fmt.Sprintf(" LIKE UPPER('%s%s%s')", "%", stmt, "%")
  48 + }
  49 + return stmt
  50 +}
... ...
format_utility.go
... ... @@ -1,50 +0,0 @@
1   -package webutility
2   -
3   -import (
4   - "fmt"
5   - "time"
6   -)
7   -
8   -// UnixToDate converts given Unix time to local time in format and returns result:
9   -// YYYY-MM-DD hh:mm:ss +zzzz UTC
10   -func UnixToDate(unix int64) time.Time {
11   - return time.Unix(unix, 0)
12   -}
13   -
14   -// DateToUnix converts given date in Unix timestamp.
15   -func DateToUnix(date interface{}) int64 {
16   - if date != nil {
17   - t, ok := date.(time.Time)
18   - if !ok {
19   - return 0
20   - }
21   - return t.Unix()
22   -
23   - }
24   - return 0
25   -}
26   -
27   -// EqualQuotes encapsulates given string in SQL 'equal' statement and returns result.
28   -// Example: "hello" -> " = 'hello'"
29   -func EqualQuotes(stmt string) string {
30   - if stmt != "" {
31   - stmt = fmt.Sprintf(" = '%s'", stmt)
32   - }
33   - return stmt
34   -}
35   -
36   -func EqualString(stmt string) string {
37   - if stmt != "" {
38   - stmt = fmt.Sprintf(" = %s", stmt)
39   - }
40   - return stmt
41   -}
42   -
43   -// LikeQuotes encapsulates given string in SQL 'like' statement and returns result.
44   -// Example: "hello" -> " LIKE UPPER('%hello%')"
45   -func LikeQuotes(stmt string) string {
46   - if stmt != "" {
47   - stmt = fmt.Sprintf(" LIKE UPPER('%s%s%s')", "%", stmt, "%")
48   - }
49   - return stmt
50   -}
... ... @@ -0,0 +1,165 @@
  1 +package webutility
  2 +
  3 +import (
  4 + "encoding/json"
  5 + "net/http"
  6 +)
  7 +
  8 +type webError struct {
  9 + Request string `json:"request"`
  10 + Error string `json:"error"`
  11 +}
  12 +
  13 +// NotFoundHandler writes HTTP error 404 to w.
  14 +func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
  15 + SetDefaultHeaders(w)
  16 + if req.Method == "OPTIONS" {
  17 + return
  18 + }
  19 + NotFound(w, req, "Not found")
  20 +}
  21 +
  22 +// SetDefaultHeaders set's default headers for an HTTP response.
  23 +func SetDefaultHeaders(w http.ResponseWriter) {
  24 + w.Header().Set("Access-Control-Allow-Origin", "*")
  25 + w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
  26 + w.Header().Set("Access-Control-Allow-Headers", `Accept, Content-Type,
  27 + Content-Length, Accept-Encoding, X-CSRF-Token, Authorization`)
  28 + w.Header().Set("Content-Type", "application/json; charset=utf-8")
  29 +}
  30 +
  31 +func ReqLocale(req *http.Request, dflt string) string {
  32 + loc := req.FormValue("locale")
  33 + if loc == "" {
  34 + return dflt
  35 + }
  36 + return loc
  37 +}
  38 +
  39 +// 2xx
  40 +func Success(w http.ResponseWriter, payload *Payload, code int) {
  41 + w.WriteHeader(code)
  42 + if payload != nil {
  43 + json.NewEncoder(w).Encode(*payload)
  44 + }
  45 +}
  46 +
  47 +// 200
  48 +func OK(w http.ResponseWriter, payload *Payload) {
  49 + Success(w, payload, http.StatusOK)
  50 +}
  51 +
  52 +// 201
  53 +func Created(w http.ResponseWriter, payload *Payload) {
  54 + Success(w, payload, http.StatusCreated)
  55 +}
  56 +
  57 +// 4xx; 5xx
  58 +func Error(w http.ResponseWriter, r *http.Request, code int, err string) {
  59 + werr := webError{Error: err, Request: r.Method + " " + r.RequestURI}
  60 + w.WriteHeader(code)
  61 + json.NewEncoder(w).Encode(werr)
  62 +}
  63 +
  64 +// 400
  65 +func BadRequest(w http.ResponseWriter, r *http.Request, err string) {
  66 + Error(w, r, http.StatusBadRequest, err)
  67 +}
  68 +
  69 +// 404
  70 +func NotFound(w http.ResponseWriter, r *http.Request, err string) {
  71 + Error(w, r, http.StatusNotFound, err)
  72 +}
  73 +
  74 +// 401
  75 +func Unauthorized(w http.ResponseWriter, r *http.Request, err string) {
  76 + Error(w, r, http.StatusUnauthorized, err)
  77 +}
  78 +
  79 +// 403
  80 +func Forbidden(w http.ResponseWriter, r *http.Request, err string) {
  81 + Error(w, r, http.StatusForbidden, err)
  82 +}
  83 +
  84 +// 403
  85 +func Conflict(w http.ResponseWriter, r *http.Request, err string) {
  86 + Error(w, r, http.StatusConflict, err)
  87 +}
  88 +
  89 +// 500
  90 +func InternalServerError(w http.ResponseWriter, r *http.Request, err string) {
  91 + Error(w, r, http.StatusInternalServerError, err)
  92 +}
  93 +
  94 +///
  95 +/// Old API
  96 +///
  97 +
  98 +const (
  99 + templateHttpErr500_EN = "An internal server error has occurred."
  100 + templateHttpErr500_RS = "Došlo je do greške na serveru."
  101 + templateHttpErr400_EN = "Bad request."
  102 + templateHttpErr400_RS = "Neispravan zahtev."
  103 + templateHttpErr404_EN = "Resource not found."
  104 + templateHttpErr404_RS = "Resurs nije pronadjen."
  105 + templateHttpErr401_EN = "Unauthorized request."
  106 + templateHttpErr401_RS = "Neautorizovan zahtev."
  107 +)
  108 +
  109 +type httpError struct {
  110 + Error []HttpErrorDesc `json:"error"`
  111 + Request string `json:"request"`
  112 +}
  113 +
  114 +type HttpErrorDesc struct {
  115 + Lang string `json:"lang"`
  116 + Desc string `json:"description"`
  117 +}
  118 +
  119 +// DeliverPayload encodes payload as JSON to w.
  120 +func DeliverPayload(w http.ResponseWriter, payload Payload) {
  121 + // Don't write status OK in the headers here. Leave it up for the caller.
  122 + // E.g. Status 201.
  123 + json.NewEncoder(w).Encode(payload)
  124 + payload.Data = nil
  125 +}
  126 +
  127 +// ErrorResponse writes HTTP error to w.
  128 +func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) {
  129 + err := httpError{desc, r.Method + " " + r.RequestURI}
  130 + w.WriteHeader(code)
  131 + json.NewEncoder(w).Encode(err)
  132 +}
  133 +
  134 +// NotFoundResponse writes HTTP error 404 to w.
  135 +func NotFoundResponse(w http.ResponseWriter, req *http.Request) {
  136 + ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{
  137 + {"en", templateHttpErr404_EN},
  138 + {"rs", templateHttpErr404_RS},
  139 + })
  140 +}
  141 +
  142 +// BadRequestResponse writes HTTP error 400 to w.
  143 +func BadRequestResponse(w http.ResponseWriter, req *http.Request) {
  144 + ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{
  145 + {"en", templateHttpErr400_EN},
  146 + {"rs", templateHttpErr400_RS},
  147 + })
  148 +}
  149 +
  150 +// InternalSeverErrorResponse writes HTTP error 500 to w.
  151 +func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) {
  152 + ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{
  153 + {"en", templateHttpErr500_EN},
  154 + {"rs", templateHttpErr500_RS},
  155 + })
  156 +}
  157 +
  158 +// UnauthorizedError writes HTTP error 401 to w.
  159 +func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) {
  160 + w.Header().Set("WWW-Authenticate", "Bearer")
  161 + ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{
  162 + {"en", templateHttpErr401_EN},
  163 + {"rs", templateHttpErr401_RS},
  164 + })
  165 +}
... ...
http_utility.go
... ... @@ -1,165 +0,0 @@
1   -package webutility
2   -
3   -import (
4   - "encoding/json"
5   - "net/http"
6   -)
7   -
8   -type webError struct {
9   - Request string `json:"request"`
10   - Error string `json:"error"`
11   -}
12   -
13   -// NotFoundHandler writes HTTP error 404 to w.
14   -func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
15   - SetDefaultHeaders(w)
16   - if req.Method == "OPTIONS" {
17   - return
18   - }
19   - NotFound(w, req, "Not found")
20   -}
21   -
22   -// SetDefaultHeaders set's default headers for an HTTP response.
23   -func SetDefaultHeaders(w http.ResponseWriter) {
24   - w.Header().Set("Access-Control-Allow-Origin", "*")
25   - w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
26   - w.Header().Set("Access-Control-Allow-Headers", `Accept, Content-Type,
27   - Content-Length, Accept-Encoding, X-CSRF-Token, Authorization`)
28   - w.Header().Set("Content-Type", "application/json; charset=utf-8")
29   -}
30   -
31   -func ReqLocale(req *http.Request, dflt string) string {
32   - loc := req.FormValue("locale")
33   - if loc == "" {
34   - return dflt
35   - }
36   - return loc
37   -}
38   -
39   -// 2xx
40   -func Success(w http.ResponseWriter, payload *Payload, code int) {
41   - w.WriteHeader(code)
42   - if payload != nil {
43   - json.NewEncoder(w).Encode(*payload)
44   - }
45   -}
46   -
47   -// 200
48   -func OK(w http.ResponseWriter, payload *Payload) {
49   - Success(w, payload, http.StatusOK)
50   -}
51   -
52   -// 201
53   -func Created(w http.ResponseWriter, payload *Payload) {
54   - Success(w, payload, http.StatusCreated)
55   -}
56   -
57   -// 4xx; 5xx
58   -func Error(w http.ResponseWriter, r *http.Request, code int, err string) {
59   - werr := webError{Error: err, Request: r.Method + " " + r.RequestURI}
60   - w.WriteHeader(code)
61   - json.NewEncoder(w).Encode(werr)
62   -}
63   -
64   -// 400
65   -func BadRequest(w http.ResponseWriter, r *http.Request, err string) {
66   - Error(w, r, http.StatusBadRequest, err)
67   -}
68   -
69   -// 404
70   -func NotFound(w http.ResponseWriter, r *http.Request, err string) {
71   - Error(w, r, http.StatusNotFound, err)
72   -}
73   -
74   -// 401
75   -func Unauthorized(w http.ResponseWriter, r *http.Request, err string) {
76   - Error(w, r, http.StatusUnauthorized, err)
77   -}
78   -
79   -// 403
80   -func Forbidden(w http.ResponseWriter, r *http.Request, err string) {
81   - Error(w, r, http.StatusForbidden, err)
82   -}
83   -
84   -// 403
85   -func Conflict(w http.ResponseWriter, r *http.Request, err string) {
86   - Error(w, r, http.StatusConflict, err)
87   -}
88   -
89   -// 500
90   -func InternalServerError(w http.ResponseWriter, r *http.Request, err string) {
91   - Error(w, r, http.StatusInternalServerError, err)
92   -}
93   -
94   -///
95   -/// Old API
96   -///
97   -
98   -const (
99   - templateHttpErr500_EN = "An internal server error has occurred."
100   - templateHttpErr500_RS = "Došlo je do greške na serveru."
101   - templateHttpErr400_EN = "Bad request."
102   - templateHttpErr400_RS = "Neispravan zahtev."
103   - templateHttpErr404_EN = "Resource not found."
104   - templateHttpErr404_RS = "Resurs nije pronadjen."
105   - templateHttpErr401_EN = "Unauthorized request."
106   - templateHttpErr401_RS = "Neautorizovan zahtev."
107   -)
108   -
109   -type httpError struct {
110   - Error []HttpErrorDesc `json:"error"`
111   - Request string `json:"request"`
112   -}
113   -
114   -type HttpErrorDesc struct {
115   - Lang string `json:"lang"`
116   - Desc string `json:"description"`
117   -}
118   -
119   -// DeliverPayload encodes payload as JSON to w.
120   -func DeliverPayload(w http.ResponseWriter, payload Payload) {
121   - // Don't write status OK in the headers here. Leave it up for the caller.
122   - // E.g. Status 201.
123   - json.NewEncoder(w).Encode(payload)
124   - payload.Data = nil
125   -}
126   -
127   -// ErrorResponse writes HTTP error to w.
128   -func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) {
129   - err := httpError{desc, r.Method + " " + r.RequestURI}
130   - w.WriteHeader(code)
131   - json.NewEncoder(w).Encode(err)
132   -}
133   -
134   -// NotFoundResponse writes HTTP error 404 to w.
135   -func NotFoundResponse(w http.ResponseWriter, req *http.Request) {
136   - ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{
137   - {"en", templateHttpErr404_EN},
138   - {"rs", templateHttpErr404_RS},
139   - })
140   -}
141   -
142   -// BadRequestResponse writes HTTP error 400 to w.
143   -func BadRequestResponse(w http.ResponseWriter, req *http.Request) {
144   - ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{
145   - {"en", templateHttpErr400_EN},
146   - {"rs", templateHttpErr400_RS},
147   - })
148   -}
149   -
150   -// InternalSeverErrorResponse writes HTTP error 500 to w.
151   -func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) {
152   - ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{
153   - {"en", templateHttpErr500_EN},
154   - {"rs", templateHttpErr500_RS},
155   - })
156   -}
157   -
158   -// UnauthorizedError writes HTTP error 401 to w.
159   -func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) {
160   - w.Header().Set("WWW-Authenticate", "Bearer")
161   - ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{
162   - {"en", templateHttpErr401_EN},
163   - {"rs", templateHttpErr401_RS},
164   - })
165   -}
... ... @@ -0,0 +1,339 @@
  1 +package webutility
  2 +
  3 +import (
  4 + "database/sql"
  5 + "encoding/json"
  6 + "errors"
  7 + "fmt"
  8 + "io"
  9 + "net/http"
  10 + "sync"
  11 + "time"
  12 +
  13 + "git.to-net.rs/marko.tikvic/gologger"
  14 +)
  15 +
  16 +var (
  17 + mu = &sync.Mutex{}
  18 + metadata = make(map[string]Payload)
  19 + updateQue = make(map[string][]byte)
  20 +
  21 + metadataDB *sql.DB
  22 + activeProject string
  23 +
  24 + inited bool
  25 + driver string
  26 +)
  27 +
  28 +var logger *gologger.Logger
  29 +
  30 +func init() {
  31 + var err error
  32 + logger, err = gologger.New("webutility", gologger.MaxLogSize100KB)
  33 + if err != nil {
  34 + fmt.Printf("webutility: %s\n", err.Error())
  35 + }
  36 +}
  37 +
  38 +type LangMap map[string]map[string]string
  39 +
  40 +type Field struct {
  41 + Parameter string `json:"param"`
  42 + Type string `json:"type"`
  43 + Visible bool `json:"visible"`
  44 + Editable bool `json:"editable"`
  45 +}
  46 +
  47 +type CorrelationField struct {
  48 + Result string `json:"result"`
  49 + Elements []string `json:"elements"`
  50 + Type string `json:"type"`
  51 +}
  52 +
  53 +type Translation struct {
  54 + Language string `json:"language"`
  55 + FieldsLabels map[string]string `json:"fieldsLabels"`
  56 +}
  57 +
  58 +type Payload struct {
  59 + Method string `json:"method"`
  60 + Params map[string]string `json:"params"`
  61 + Lang []Translation `json:"lang"`
  62 + Fields []Field `json:"fields"`
  63 + Correlations []CorrelationField `json:"correlationFields"`
  64 + IdField string `json:"idField"`
  65 +
  66 + // Data holds JSON payload.
  67 + // It can't be used for itteration.
  68 + Data interface{} `json:"data"`
  69 +}
  70 +
  71 +// NewPayload returs a payload sceleton for entity described with etype.
  72 +func NewPayload(r *http.Request, etype string) Payload {
  73 + pload := metadata[etype]
  74 + pload.Method = r.Method + " " + r.RequestURI
  75 + return pload
  76 +}
  77 +
  78 +// DecodeJSON decodes JSON data from r to v.
  79 +// Returns an error if it fails.
  80 +func DecodeJSON(r io.Reader, v interface{}) error {
  81 + return json.NewDecoder(r).Decode(v)
  82 +}
  83 +
  84 +// InitPayloadsMetadata loads all payloads' information into 'metadata' variable.
  85 +func InitPayloadsMetadata(drv string, db *sql.DB, project string) error {
  86 + if drv != "ora" && drv != "mysql" {
  87 + return errors.New("driver not supported")
  88 + }
  89 + driver = drv
  90 + metadataDB = db
  91 + activeProject = project
  92 +
  93 + mu.Lock()
  94 + defer mu.Unlock()
  95 + err := initMetadata(project)
  96 + if err != nil {
  97 + return err
  98 + }
  99 + inited = true
  100 +
  101 + return nil
  102 +}
  103 +
  104 +func EnableHotloading(interval int) {
  105 + if interval > 0 {
  106 + go hotload(interval)
  107 + }
  108 +}
  109 +
  110 +func GetMetadataForAllEntities() map[string]Payload {
  111 + return metadata
  112 +}
  113 +
  114 +func GetMetadataForEntity(t string) (Payload, bool) {
  115 + p, ok := metadata[t]
  116 + return p, ok
  117 +}
  118 +
  119 +func QueEntityModelUpdate(entityType string, v interface{}) {
  120 + updateQue[entityType], _ = json.Marshal(v)
  121 +}
  122 +
  123 +func UpdateEntityModels(command string) (total, upd, add int, err error) {
  124 + if command != "force" && command != "missing" {
  125 + return total, 0, 0, errors.New("webutility: unknown command: " + command)
  126 + }
  127 +
  128 + if !inited {
  129 + return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.")
  130 + }
  131 +
  132 + total = len(updateQue)
  133 +
  134 + toUpdate := make([]string, 0)
  135 + toAdd := make([]string, 0)
  136 +
  137 + for k, _ := range updateQue {
  138 + if _, exists := metadata[k]; exists {
  139 + if command == "force" {
  140 + toUpdate = append(toUpdate, k)
  141 + }
  142 + } else {
  143 + toAdd = append(toAdd, k)
  144 + }
  145 + }
  146 +
  147 + var uStmt *sql.Stmt
  148 + if driver == "ora" {
  149 + uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2")
  150 + if err != nil {
  151 + return
  152 + }
  153 + } else if driver == "mysql" {
  154 + uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?")
  155 + if err != nil {
  156 + return
  157 + }
  158 + }
  159 + for _, k := range toUpdate {
  160 + //fmt.Printf("Updating: %s\n", k)
  161 + //fmt.Printf("New model: %s\n", updateQue[k])
  162 + _, err = uStmt.Exec(string(updateQue[k]), k)
  163 + if err != nil {
  164 + logger.Log("webutility: %v\n", err)
  165 + return
  166 + }
  167 + upd++
  168 + }
  169 +
  170 + blankPayload, _ := json.Marshal(Payload{})
  171 + var iStmt *sql.Stmt
  172 + if driver == "ora" {
  173 + iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)")
  174 + if err != nil {
  175 + return
  176 + }
  177 + } else if driver == "mysql" {
  178 + iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)")
  179 + if err != nil {
  180 + return
  181 + }
  182 + }
  183 + for _, k := range toAdd {
  184 + _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k]))
  185 + if err != nil {
  186 + logger.Log("webutility: %v\n", err)
  187 + return
  188 + }
  189 + metadata[k] = Payload{}
  190 + add++
  191 + }
  192 +
  193 + return total, upd, add, nil
  194 +}
  195 +
  196 +func initMetadata(project string) error {
  197 + rows, err := metadataDB.Query(`select
  198 + entity_type,
  199 + metadata
  200 + from entities
  201 + where projekat = ` + fmt.Sprintf("'%s'", project))
  202 + if err != nil {
  203 + return err
  204 + }
  205 + defer rows.Close()
  206 +
  207 + count := 0
  208 + success := 0
  209 + if len(metadata) > 0 {
  210 + metadata = nil
  211 + }
  212 + metadata = make(map[string]Payload)
  213 + for rows.Next() {
  214 + var name, load string
  215 + rows.Scan(&name, &load)
  216 +
  217 + p := Payload{}
  218 + err := json.Unmarshal([]byte(load), &p)
  219 + if err != nil {
  220 + logger.Log("webutility: couldn't init: '%s' metadata: %s:\n%s\n", name, err.Error(), load)
  221 + } else {
  222 + success++
  223 + metadata[name] = p
  224 + }
  225 + count++
  226 + }
  227 + perc := float32(success) / float32(count) * 100.0
  228 + logger.Log("webutility: loaded %d/%d (%.1f%%) entities\n", success, count, perc)
  229 +
  230 + return nil
  231 +}
  232 +
  233 +func hotload(n int) {
  234 + entityScan := make(map[string]int64)
  235 + firstCheck := true
  236 + for {
  237 + time.Sleep(time.Duration(n) * time.Second)
  238 + rows, err := metadataDB.Query(`select
  239 + ora_rowscn,
  240 + entity_type
  241 + from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject))
  242 + if err != nil {
  243 + logger.Log("webutility: hotload failed: %v\n", err)
  244 + time.Sleep(time.Duration(n) * time.Second)
  245 + continue
  246 + }
  247 +
  248 + var toRefresh []string
  249 + for rows.Next() {
  250 + var scanID int64
  251 + var entity string
  252 + rows.Scan(&scanID, &entity)
  253 + oldID, ok := entityScan[entity]
  254 + if !ok || oldID != scanID {
  255 + entityScan[entity] = scanID
  256 + toRefresh = append(toRefresh, entity)
  257 + }
  258 + }
  259 + rows.Close()
  260 +
  261 + if rows.Err() != nil {
  262 + logger.Log("webutility: hotload rset error: %v\n", rows.Err())
  263 + time.Sleep(time.Duration(n) * time.Second)
  264 + continue
  265 + }
  266 +
  267 + if len(toRefresh) > 0 && !firstCheck {
  268 + mu.Lock()
  269 + refreshMetadata(toRefresh)
  270 + mu.Unlock()
  271 + }
  272 + if firstCheck {
  273 + firstCheck = false
  274 + }
  275 + }
  276 +}
  277 +
  278 +func refreshMetadata(entities []string) {
  279 + for _, e := range entities {
  280 + fmt.Printf("refreshing %s\n", e)
  281 + rows, err := metadataDB.Query(`select
  282 + metadata
  283 + from entities
  284 + where projekat = ` + fmt.Sprintf("'%s'", activeProject) +
  285 + ` and entity_type = ` + fmt.Sprintf("'%s'", e))
  286 +
  287 + if err != nil {
  288 + logger.Log("webutility: refresh: prep: %v\n", err)
  289 + rows.Close()
  290 + continue
  291 + }
  292 +
  293 + for rows.Next() {
  294 + var load string
  295 + rows.Scan(&load)
  296 + p := Payload{}
  297 + err := json.Unmarshal([]byte(load), &p)
  298 + if err != nil {
  299 + logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load)
  300 + } else {
  301 + metadata[e] = p
  302 + }
  303 + }
  304 + rows.Close()
  305 + }
  306 +}
  307 +
  308 +/*
  309 +func ModifyMetadataForEntity(entityType string, p *Payload) error {
  310 + md, err := json.Marshal(*p)
  311 + if err != nil {
  312 + return err
  313 + }
  314 +
  315 + mu.Lock()
  316 + defer mu.Unlock()
  317 + _, err = metadataDB.PrepAndExe(`update entities set
  318 + metadata = :1
  319 + where projekat = :2
  320 + and entity_type = :3`,
  321 + string(md),
  322 + activeProject,
  323 + entityType)
  324 + if err != nil {
  325 + return err
  326 + }
  327 + return nil
  328 +}
  329 +
  330 +func DeleteEntityModel(entityType string) error {
  331 + _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType)
  332 + if err == nil {
  333 + mu.Lock()
  334 + delete(metadata, entityType)
  335 + mu.Unlock()
  336 + }
  337 + return err
  338 +}
  339 +*/
... ...
json_utility.go
... ... @@ -1,339 +0,0 @@
1   -package webutility
2   -
3   -import (
4   - "database/sql"
5   - "encoding/json"
6   - "errors"
7   - "fmt"
8   - "io"
9   - "net/http"
10   - "sync"
11   - "time"
12   -
13   - "git.to-net.rs/marko.tikvic/gologger"
14   -)
15   -
16   -var (
17   - mu = &sync.Mutex{}
18   - metadata = make(map[string]Payload)
19   - updateQue = make(map[string][]byte)
20   -
21   - metadataDB *sql.DB
22   - activeProject string
23   -
24   - inited bool
25   - driver string
26   -)
27   -
28   -var logger *gologger.Logger
29   -
30   -func init() {
31   - var err error
32   - logger, err = gologger.New("webutility", gologger.MaxLogSize100KB)
33   - if err != nil {
34   - fmt.Printf("webutility: %s\n", err.Error())
35   - }
36   -}
37   -
38   -type LangMap map[string]map[string]string
39   -
40   -type Field struct {
41   - Parameter string `json:"param"`
42   - Type string `json:"type"`
43   - Visible bool `json:"visible"`
44   - Editable bool `json:"editable"`
45   -}
46   -
47   -type CorrelationField struct {
48   - Result string `json:"result"`
49   - Elements []string `json:"elements"`
50   - Type string `json:"type"`
51   -}
52   -
53   -type Translation struct {
54   - Language string `json:"language"`
55   - FieldsLabels map[string]string `json:"fieldsLabels"`
56   -}
57   -
58   -type Payload struct {
59   - Method string `json:"method"`
60   - Params map[string]string `json:"params"`
61   - Lang []Translation `json:"lang"`
62   - Fields []Field `json:"fields"`
63   - Correlations []CorrelationField `json:"correlationFields"`
64   - IdField string `json:"idField"`
65   -
66   - // Data holds JSON payload.
67   - // It can't be used for itteration.
68   - Data interface{} `json:"data"`
69   -}
70   -
71   -// NewPayload returs a payload sceleton for entity described with etype.
72   -func NewPayload(r *http.Request, etype string) Payload {
73   - pload := metadata[etype]
74   - pload.Method = r.Method + " " + r.RequestURI
75   - return pload
76   -}
77   -
78   -// DecodeJSON decodes JSON data from r to v.
79   -// Returns an error if it fails.
80   -func DecodeJSON(r io.Reader, v interface{}) error {
81   - return json.NewDecoder(r).Decode(v)
82   -}
83   -
84   -// InitPayloadsMetadata loads all payloads' information into 'metadata' variable.
85   -func InitPayloadsMetadata(drv string, db *sql.DB, project string) error {
86   - if drv != "ora" && drv != "mysql" {
87   - return errors.New("driver not supported")
88   - }
89   - driver = drv
90   - metadataDB = db
91   - activeProject = project
92   -
93   - mu.Lock()
94   - defer mu.Unlock()
95   - err := initMetadata(project)
96   - if err != nil {
97   - return err
98   - }
99   - inited = true
100   -
101   - return nil
102   -}
103   -
104   -func EnableHotloading(interval int) {
105   - if interval > 0 {
106   - go hotload(interval)
107   - }
108   -}
109   -
110   -func GetMetadataForAllEntities() map[string]Payload {
111   - return metadata
112   -}
113   -
114   -func GetMetadataForEntity(t string) (Payload, bool) {
115   - p, ok := metadata[t]
116   - return p, ok
117   -}
118   -
119   -func QueEntityModelUpdate(entityType string, v interface{}) {
120   - updateQue[entityType], _ = json.Marshal(v)
121   -}
122   -
123   -func initMetadata(project string) error {
124   - rows, err := metadataDB.Query(`select
125   - entity_type,
126   - metadata
127   - from entities
128   - where projekat = ` + fmt.Sprintf("'%s'", project))
129   - if err != nil {
130   - return err
131   - }
132   - defer rows.Close()
133   -
134   - count := 0
135   - success := 0
136   - if len(metadata) > 0 {
137   - metadata = nil
138   - }
139   - metadata = make(map[string]Payload)
140   - for rows.Next() {
141   - var name, load string
142   - rows.Scan(&name, &load)
143   -
144   - p := Payload{}
145   - err := json.Unmarshal([]byte(load), &p)
146   - if err != nil {
147   - logger.Log("webutility: couldn't init: '%s' metadata: %s\n%s\n", name, err.Error(), load)
148   - } else {
149   - success++
150   - metadata[name] = p
151   - }
152   - count++
153   - }
154   - perc := float32(success/count) * 100.0
155   - logger.Log("webutility: loaded %d/%d (%.1f%%) entities\n", success, count, perc)
156   -
157   - return nil
158   -}
159   -
160   -func hotload(n int) {
161   - entityScan := make(map[string]int64)
162   - firstCheck := true
163   - for {
164   - time.Sleep(time.Duration(n) * time.Second)
165   - rows, err := metadataDB.Query(`select
166   - ora_rowscn,
167   - entity_type
168   - from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject))
169   - if err != nil {
170   - logger.Log("webutility: hotload failed: %v\n", err)
171   - time.Sleep(time.Duration(n) * time.Second)
172   - continue
173   - }
174   -
175   - var toRefresh []string
176   - for rows.Next() {
177   - var scanID int64
178   - var entity string
179   - rows.Scan(&scanID, &entity)
180   - oldID, ok := entityScan[entity]
181   - if !ok || oldID != scanID {
182   - entityScan[entity] = scanID
183   - toRefresh = append(toRefresh, entity)
184   - }
185   - }
186   - rows.Close()
187   -
188   - if rows.Err() != nil {
189   - logger.Log("webutility: hotload rset error: %v\n", rows.Err())
190   - time.Sleep(time.Duration(n) * time.Second)
191   - continue
192   - }
193   -
194   - if len(toRefresh) > 0 && !firstCheck {
195   - mu.Lock()
196   - refreshMetadata(toRefresh)
197   - mu.Unlock()
198   - }
199   - if firstCheck {
200   - firstCheck = false
201   - }
202   - }
203   -}
204   -
205   -func refreshMetadata(entities []string) {
206   - for _, e := range entities {
207   - fmt.Printf("refreshing %s\n", e)
208   - rows, err := metadataDB.Query(`select
209   - metadata
210   - from entities
211   - where projekat = ` + fmt.Sprintf("'%s'", activeProject) +
212   - ` and entity_type = ` + fmt.Sprintf("'%s'", e))
213   -
214   - if err != nil {
215   - logger.Log("webutility: refresh: prep: %v\n", err)
216   - rows.Close()
217   - continue
218   - }
219   -
220   - for rows.Next() {
221   - var load string
222   - rows.Scan(&load)
223   - p := Payload{}
224   - err := json.Unmarshal([]byte(load), &p)
225   - if err != nil {
226   - logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load)
227   - } else {
228   - metadata[e] = p
229   - }
230   - }
231   - rows.Close()
232   - }
233   -}
234   -
235   -func UpdateEntityModels(command string) (total, upd, add int, err error) {
236   - if command != "force" && command != "missing" {
237   - return total, 0, 0, errors.New("webutility: unknown command: " + command)
238   - }
239   -
240   - if !inited {
241   - return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.")
242   - }
243   -
244   - total = len(updateQue)
245   -
246   - toUpdate := make([]string, 0)
247   - toAdd := make([]string, 0)
248   -
249   - for k, _ := range updateQue {
250   - if _, exists := metadata[k]; exists {
251   - if command == "force" {
252   - toUpdate = append(toUpdate, k)
253   - }
254   - } else {
255   - toAdd = append(toAdd, k)
256   - }
257   - }
258   -
259   - var uStmt *sql.Stmt
260   - if driver == "ora" {
261   - uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2")
262   - if err != nil {
263   - return
264   - }
265   - } else if driver == "mysql" {
266   - uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?")
267   - if err != nil {
268   - return
269   - }
270   - }
271   - for _, k := range toUpdate {
272   - //fmt.Printf("Updating: %s\n", k)
273   - //fmt.Printf("New model: %s\n", updateQue[k])
274   - _, err = uStmt.Exec(string(updateQue[k]), k)
275   - if err != nil {
276   - logger.Log("webutility: %v\n", err)
277   - return
278   - }
279   - upd++
280   - }
281   -
282   - blankPayload, _ := json.Marshal(Payload{})
283   - var iStmt *sql.Stmt
284   - if driver == "ora" {
285   - iStmt, err = metadataDB.Prepare("INSERT INTO ENTITIES(PROJEKAT, METADATA, ENTITY_TYPE, ENTITY_MODEL) VALUES(:1, :2, :3, :4)")
286   - if err != nil {
287   - return
288   - }
289   - } else if driver == "mysql" {
290   - iStmt, err = metadataDB.Prepare("INSERT INTO ENTITIES(PROJEKAT, METADATA, ENTITY_TYPE, ENTITY_MODEL) VALUES(?, ?, ?, ?)")
291   - if err != nil {
292   - return
293   - }
294   - }
295   - for _, k := range toAdd {
296   - _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k]))
297   - if err != nil {
298   - logger.Log("webutility: %v\n", err)
299   - return
300   - }
301   - metadata[k] = Payload{}
302   - add++
303   - }
304   -
305   - return total, upd, add, nil
306   -}
307   -
308   -/*
309   -func ModifyMetadataForEntity(entityType string, p *Payload) error {
310   - md, err := json.Marshal(*p)
311   - if err != nil {
312   - return err
313   - }
314   -
315   - mu.Lock()
316   - defer mu.Unlock()
317   - _, err = metadataDB.PrepAndExe(`update entities set
318   - metadata = :1
319   - where projekat = :2
320   - and entity_type = :3`,
321   - string(md),
322   - activeProject,
323   - entityType)
324   - if err != nil {
325   - return err
326   - }
327   - return nil
328   -}
329   -
330   -func DeleteEntityModel(entityType string) error {
331   - _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType)
332   - if err == nil {
333   - mu.Lock()
334   - delete(metadata, entityType)
335   - mu.Unlock()
336   - }
337   - return err
338   -}
339   -*/
locale_utility.go
... ... @@ -1,66 +0,0 @@
1   -package webutility
2   -
3   -import (
4   - "encoding/json"
5   - "errors"
6   - "io/ioutil"
7   -)
8   -
9   -type Dictionary struct {
10   - locales map[string]map[string]string
11   - supported []string
12   - defaultLocale string
13   -}
14   -
15   -func NewDictionary() Dictionary {
16   - return Dictionary{
17   - locales: map[string]map[string]string{},
18   - }
19   -}
20   -
21   -func (d *Dictionary) AddLocale(loc, filePath string) error {
22   - file, err := ioutil.ReadFile(filePath)
23   - if err != nil {
24   - return err
25   - }
26   -
27   - var data interface{}
28   - err = json.Unmarshal(file, &data)
29   - if err != nil {
30   - return err
31   - }
32   -
33   - l := map[string]string{}
34   - for k, v := range data.(map[string]interface{}) {
35   - l[k] = v.(string)
36   - }
37   - d.locales[loc] = l
38   - d.supported = append(d.supported, loc)
39   -
40   - return nil
41   -}
42   -
43   -func (d *Dictionary) Translate(loc, key string) string {
44   - return d.locales[loc][key]
45   -}
46   -
47   -func (d *Dictionary) HasLocale(loc string) bool {
48   - for _, v := range d.supported {
49   - if v == loc {
50   - return true
51   - }
52   - }
53   - return false
54   -}
55   -
56   -func (d *Dictionary) SetDefaultLocale(loc string) error {
57   - if !d.HasLocale(loc) {
58   - return errors.New("dictionary does not contain translations for " + loc)
59   - }
60   - d.defaultLocale = loc
61   - return nil
62   -}
63   -
64   -func (d *Dictionary) GetDefaultLocale() string {
65   - return d.defaultLocale
66   -}
... ... @@ -0,0 +1,66 @@
  1 +package webutility
  2 +
  3 +import (
  4 + "encoding/json"
  5 + "errors"
  6 + "io/ioutil"
  7 +)
  8 +
  9 +type Dictionary struct {
  10 + locales map[string]map[string]string
  11 + supported []string
  12 + defaultLocale string
  13 +}
  14 +
  15 +func NewDictionary() Dictionary {
  16 + return Dictionary{
  17 + locales: map[string]map[string]string{},
  18 + }
  19 +}
  20 +
  21 +func (d *Dictionary) AddLocale(loc, filePath string) error {
  22 + file, err := ioutil.ReadFile(filePath)
  23 + if err != nil {
  24 + return err
  25 + }
  26 +
  27 + var data interface{}
  28 + err = json.Unmarshal(file, &data)
  29 + if err != nil {
  30 + return err
  31 + }
  32 +
  33 + l := map[string]string{}
  34 + for k, v := range data.(map[string]interface{}) {
  35 + l[k] = v.(string)
  36 + }
  37 + d.locales[loc] = l
  38 + d.supported = append(d.supported, loc)
  39 +
  40 + return nil
  41 +}
  42 +
  43 +func (d *Dictionary) Translate(loc, key string) string {
  44 + return d.locales[loc][key]
  45 +}
  46 +
  47 +func (d *Dictionary) HasLocale(loc string) bool {
  48 + for _, v := range d.supported {
  49 + if v == loc {
  50 + return true
  51 + }
  52 + }
  53 + return false
  54 +}
  55 +
  56 +func (d *Dictionary) SetDefaultLocale(loc string) error {
  57 + if !d.HasLocale(loc) {
  58 + return errors.New("dictionary does not contain translations for " + loc)
  59 + }
  60 + d.defaultLocale = loc
  61 + return nil
  62 +}
  63 +
  64 +func (d *Dictionary) GetDefaultLocale() string {
  65 + return d.defaultLocale
  66 +}
... ...