[TASK] add redirect to other auth microservice + add worker to clean cached values from other microservice
This commit is contained in:
		
							parent
							
								
									21859eec82
								
							
						
					
					
						commit
						38f51b29a9
					
				|  | @ -35,7 +35,9 @@ func main() { | ||||||
| 		log.Log.Panic(err) | 		log.Log.Panic(err) | ||||||
| 	} | 	} | ||||||
| 	grw := models.NewGoodReleaseWorker(config.GoodRelease) | 	grw := models.NewGoodReleaseWorker(config.GoodRelease) | ||||||
|  | 	cw := models.NewCacheWorker(config.CacheClean) | ||||||
| 	go grw.Start() | 	go grw.Start() | ||||||
|  | 	go cw.Start() | ||||||
| 	// Startwebsrver
 | 	// Startwebsrver
 | ||||||
| 	router := goji.NewMux() | 	router := goji.NewMux() | ||||||
| 	web.BindAPI(router) | 	web.BindAPI(router) | ||||||
|  | @ -53,6 +55,7 @@ func main() { | ||||||
| 	// Stop services
 | 	// Stop services
 | ||||||
| 	srv.Close() | 	srv.Close() | ||||||
| 	grw.Close() | 	grw.Close() | ||||||
|  | 	cw.Close() | ||||||
| 	database.Close() | 	database.Close() | ||||||
| 
 | 
 | ||||||
| 	log.Log.Info("received", sig) | 	log.Log.Info("received", sig) | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| webserver_bind = ":8080" | webserver_bind = ":8080" | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| good_availablity_template = "contrib/good_availablity.svg" | good_availablity_template = "contrib/good_availablity.svg" | ||||||
| 
 | 
 | ||||||
| [database] | [database] | ||||||
|  | @ -10,5 +11,9 @@ connection = "file::memory:?mode=memory&cache=shared" | ||||||
| # read_connection = "" | # read_connection = "" | ||||||
| 
 | 
 | ||||||
| [good_release] | [good_release] | ||||||
| timer = "5m" | every = "5m" | ||||||
|  | after = "30m" | ||||||
|  | 
 | ||||||
|  | [cache_clean] | ||||||
|  | every = "5m" | ||||||
| after = "30m" | after = "30m" | ||||||
|  |  | ||||||
|  | @ -0,0 +1,64 @@ | ||||||
|  | package models | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/genofire/hs_master-kss-monolith/lib/log" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // TODO DRAFT for a rest request to a other microservice
 | ||||||
|  | const PermissionURL = "https://google.com/?q=%sa%d" | ||||||
|  | 
 | ||||||
|  | type Permission int | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	PermissionCreateGood = 1 | ||||||
|  | 	PermissionDeleteGood = 2 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type permissionMicroServiceCache struct { | ||||||
|  | 	LastCheck   time.Time | ||||||
|  | 	session     string | ||||||
|  | 	permissions map[Permission]boolMicroServiceCache | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *permissionMicroServiceCache) HasPermission(p Permission) (bool, error) { | ||||||
|  | 	c.LastCheck = time.Now() | ||||||
|  | 	if cache, ok := c.permissions[p]; ok { | ||||||
|  | 		// cache for 5min
 | ||||||
|  | 		before := time.Now().Add(-time.Minute * 5) | ||||||
|  | 		if !cache.LastCheck.Before(before) { | ||||||
|  | 			return cache.Value, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	url := fmt.Sprintf(PermissionURL, c.session, p) | ||||||
|  | 	log.Log.WithField("url", url).Info("has permission?") | ||||||
|  | 	res, err := http.Get(url) | ||||||
|  | 
 | ||||||
|  | 	c.permissions[p] = boolMicroServiceCache{ | ||||||
|  | 		LastCheck: c.LastCheck, | ||||||
|  | 		Value:     (res.StatusCode == http.StatusOK), | ||||||
|  | 	} | ||||||
|  | 	return c.permissions[p].Value, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var permissionCache map[string]*permissionMicroServiceCache | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	permissionCache = make(map[string]*permissionMicroServiceCache) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func HasPermission(session string, p Permission) (bool, error) { | ||||||
|  | 	_, ok := permissionCache[session] | ||||||
|  | 	if !ok { | ||||||
|  | 		permissionCache[session] = &permissionMicroServiceCache{ | ||||||
|  | 			LastCheck:   time.Now(), | ||||||
|  | 			session:     session, | ||||||
|  | 			permissions: make(map[Permission]boolMicroServiceCache), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return permissionCache[session].HasPermission(p) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,15 @@ | ||||||
|  | package models | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestAuth(t *testing.T) { | ||||||
|  | 	assert := assert.New(t) | ||||||
|  | 
 | ||||||
|  | 	perm, err := HasPermission("session", PermissionCreateGood) | ||||||
|  | 	assert.NoError(err) | ||||||
|  | 	assert.True(perm) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,25 @@ | ||||||
|  | package models | ||||||
|  | 
 | ||||||
|  | import "time" | ||||||
|  | 
 | ||||||
|  | type CacheWorkerConfig struct { | ||||||
|  | 	Every Duration | ||||||
|  | 	After Duration | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewCacheWorker(config CacheWorkerConfig) (w *Worker) { | ||||||
|  | 	return NewWorker(config.Every.Duration, func() { | ||||||
|  | 		// Cache if product exists
 | ||||||
|  | 		for index, cache := range productExistCache { | ||||||
|  | 			if cache.LastCheck.After(time.Now().Add(-config.After.Duration)) { | ||||||
|  | 				delete(productExistCache, index) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// Cache for permissions
 | ||||||
|  | 		for index, cache := range permissionCache { | ||||||
|  | 			if cache.LastCheck.After(time.Now().Add(-config.After.Duration)) { | ||||||
|  | 				delete(permissionCache, index) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | @ -11,13 +11,11 @@ import ( | ||||||
| 
 | 
 | ||||||
| //Config the config File of this daemon
 | //Config the config File of this daemon
 | ||||||
| type Config struct { | type Config struct { | ||||||
| 	WebserverBind           string          `toml:"webserver_bind"` | 	WebserverBind           string            `toml:"webserver_bind"` | ||||||
| 	Database                database.Config `toml:"database"` | 	Database                database.Config   `toml:"database"` | ||||||
| 	GoodAvailablityTemplate string          `toml:"good_availablity_template"` | 	GoodAvailablityTemplate string            `toml:"good_availablity_template"` | ||||||
| 	GoodRelease             struct { | 	GoodRelease             GoodReleaseConfig `toml:"good_release"` | ||||||
| 		After Duration `toml:"after"` | 	CacheClean              CacheWorkerConfig `toml:"cache_clean"` | ||||||
| 		Timer Duration `toml:"timer"` |  | ||||||
| 	} `toml:"good_release"` |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ReadConfigFile reads a config model from path of a yml file
 | // ReadConfigFile reads a config model from path of a yml file
 | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ type Good struct { | ||||||
| 	RecievedAt *time.Time `sql:"default:current_timestamp"` | 	RecievedAt *time.Time `sql:"default:current_timestamp"` | ||||||
| 	// Make it temporary unusable
 | 	// Make it temporary unusable
 | ||||||
| 	LockedAt     *time.Time | 	LockedAt     *time.Time | ||||||
| 	LockedSecret string | 	LockedSecret string `json:"-"` | ||||||
| 	// Make it unusable
 | 	// Make it unusable
 | ||||||
| 	DeletedAt *time.Time | 	DeletedAt *time.Time | ||||||
| 	Sended    bool | 	Sended    bool | ||||||
|  |  | ||||||
|  | @ -4,45 +4,17 @@ import ( | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/genofire/hs_master-kss-monolith/lib/database" | 	"github.com/genofire/hs_master-kss-monolith/lib/database" | ||||||
| 	"github.com/genofire/hs_master-kss-monolith/lib/log" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type GoodReleaseConfig struct { | type GoodReleaseConfig struct { | ||||||
| 	After Duration `toml:"after"` | 	After Duration `toml:"after"` | ||||||
| 	Timer Duration `toml:"timer"` | 	Every Duration `toml:"every"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type GoodReleaseWorker struct { | func NewGoodReleaseWorker(grc GoodReleaseConfig) *Worker { | ||||||
| 	unlockTimer time.Duration | 	return NewWorker(grc.Every.Duration, func() { | ||||||
| 	unlockAfter time.Duration | 		goodRelease(grc.After.Duration) | ||||||
| 	quit        chan struct{} | 	}) | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func NewGoodReleaseWorker(grc GoodReleaseConfig) (rw *GoodReleaseWorker) { |  | ||||||
| 	rw = &GoodReleaseWorker{ |  | ||||||
| 		unlockTimer: grc.Timer.Duration, |  | ||||||
| 		unlockAfter: grc.After.Duration, |  | ||||||
| 		quit:        make(chan struct{}), |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (rw *GoodReleaseWorker) Start() { |  | ||||||
| 	ticker := time.NewTicker(rw.unlockTimer) |  | ||||||
| 	for { |  | ||||||
| 		select { |  | ||||||
| 		case <-ticker.C: |  | ||||||
| 			count := goodRelease(rw.unlockAfter) |  | ||||||
| 			log.Log.WithField("count", count).Info("goods released") |  | ||||||
| 		case <-rw.quit: |  | ||||||
| 			ticker.Stop() |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (rw *GoodReleaseWorker) Close() { |  | ||||||
| 	close(rw.quit) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func goodRelease(unlockAfter time.Duration) int64 { | func goodRelease(unlockAfter time.Duration) int64 { | ||||||
|  |  | ||||||
|  | @ -32,7 +32,7 @@ func TestGoodRelease(t *testing.T) { | ||||||
| 	assert.Equal(int64(1), count, "unlock after timeout") | 	assert.Equal(int64(1), count, "unlock after timeout") | ||||||
| 
 | 
 | ||||||
| 	grw := NewGoodReleaseWorker(GoodReleaseConfig{ | 	grw := NewGoodReleaseWorker(GoodReleaseConfig{ | ||||||
| 		Timer: Duration{Duration: time.Duration(3) * time.Millisecond}, | 		Every: Duration{Duration: time.Duration(3) * time.Millisecond}, | ||||||
| 		After: Duration{Duration: time.Duration(5) * time.Millisecond}, | 		After: Duration{Duration: time.Duration(5) * time.Millisecond}, | ||||||
| 	}) | 	}) | ||||||
| 	go grw.Start() | 	go grw.Start() | ||||||
|  |  | ||||||
|  | @ -0,0 +1,34 @@ | ||||||
|  | package models | ||||||
|  | 
 | ||||||
|  | import "time" | ||||||
|  | 
 | ||||||
|  | type Worker struct { | ||||||
|  | 	every time.Duration | ||||||
|  | 	run   func() | ||||||
|  | 	quit  chan struct{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewWorker(every time.Duration, f func()) (w *Worker) { | ||||||
|  | 	w = &Worker{ | ||||||
|  | 		every: every, | ||||||
|  | 		run:   f, | ||||||
|  | 		quit:  make(chan struct{}), | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (w *Worker) Start() { | ||||||
|  | 	ticker := time.NewTicker(w.every) | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case <-ticker.C: | ||||||
|  | 			w.run() | ||||||
|  | 		case <-w.quit: | ||||||
|  | 			ticker.Stop() | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | func (w *Worker) Close() { | ||||||
|  | 	close(w.quit) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,103 @@ | ||||||
|  | mode: count | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/cache_worker.go:10.59,11.49 1 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/cache_worker.go:11.49,13.47 1 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/cache_worker.go:19.3,19.45 1 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/cache_worker.go:13.47,14.69 1 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/cache_worker.go:14.69,16.5 1 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/cache_worker.go:19.45,20.69 1 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/cache_worker.go:20.69,22.5 1 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/worker.go:11.59,18.2 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/worker.go:20.26,22.6 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/worker.go:22.6,23.10 1 6 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/worker.go:24.19,25.11 1 5 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/worker.go:26.17,28.10 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/worker.go:32.26,34.2 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good_release.go:14.58,15.46 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good_release.go:15.46,17.3 1 5 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good_release.go:20.51,23.2 2 7 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/product_cache.go:21.13,23.2 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/product_cache.go:25.42,26.46 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/product_cache.go:34.2,42.43 5 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/product_cache.go:26.46,29.38 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/product_cache.go:29.38,31.4 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/auth.go:27.81,29.39 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/auth.go:37.2,45.36 5 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/auth.go:29.39,32.38 2 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/auth.go:32.38,34.4 1 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/auth.go:50.13,52.2 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/auth.go:54.64,56.9 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/auth.go:63.2,63.50 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/auth.go:56.9,62.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/config.go:22.42,25.16 3 3 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/config.go:29.2,29.53 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/config.go:33.2,33.15 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/config.go:25.16,27.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/config.go:29.53,31.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:19.67,21.30 2 20 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:28.2,28.19 1 19 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:32.2,34.16 3 18 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:38.2,38.14 1 17 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:55.2,55.12 1 16 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:22.14,23.32 1 19 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:24.10,25.63 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:28.19,30.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:34.16,36.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:39.11,40.50 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:41.11,42.50 1 6 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:43.11,44.48 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:45.11,46.53 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:47.11,48.57 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:49.11,50.59 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/duration.go:51.10,52.63 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good.go:28.54,30.2 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good.go:32.36,36.2 3 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good.go:37.30,39.2 1 4 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good.go:40.44,41.30 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good.go:46.2,46.35 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good.go:41.30,45.3 3 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/models/good.go:49.13,51.2 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/log/main.go:14.13,18.2 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/log/main.go:21.42,23.18 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/log/main.go:26.2,30.4 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/log/main.go:23.18,25.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:25.33,29.16 4 4 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:32.2,37.36 6 3 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:51.2,52.8 2 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:29.16,31.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:37.36,40.17 3 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:43.3,47.60 5 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:40.17,42.4 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:48.3,50.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:55.14,58.36 3 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:61.2,61.12 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:58.36,60.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/database/main.go:64.30,66.2 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/http/main.go:10.56,11.56 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/http/main.go:15.2,16.8 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/http/main.go:11.56,14.3 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/http/main.go:20.53,22.16 2 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/http/main.go:26.2,27.13 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/lib/http/main.go:22.16,25.3 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/main.go:8.32,12.2 3 3 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/status.go:12.53,31.2 8 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:16.56,19.16 3 3 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:24.2,27.30 4 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:32.2,33.18 2 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:19.16,23.3 3 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:27.30,31.3 3 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:36.91,39.16 3 4 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:44.2,47.16 4 3 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:51.2,51.9 1 3 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:56.2,58.24 3 3 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:39.16,43.3 3 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:47.16,50.3 2 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:51.9,55.3 3 0 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:60.65,62.15 2 4 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:65.2,66.38 2 3 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:72.2,72.18 1 3 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:62.15,64.3 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:67.26,68.22 1 2 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good.go:69.10,70.34 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good_temp.go:13.38,15.2 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good_temp.go:17.56,19.2 1 1 | ||||||
|  | github.com/genofire/hs_master-kss-monolith/http/good_temp.go:21.62,37.2 10 1 | ||||||
		Reference in New Issue