service.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. package main
  2. import (
  3. "context"
  4. "crypto/md5"
  5. "encoding/json"
  6. "fmt"
  7. "io/ioutil"
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "os/signal"
  12. "path/filepath"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "gopkg.in/yaml.v3"
  17. api "wkla.no-ip.biz/gogs/Willie/MsgService/MessageService/api"
  18. "wkla.no-ip.biz/gogs/Willie/MsgService/MessageService/dao"
  19. "wkla.no-ip.biz/gogs/Willie/MsgService/MessageService/health"
  20. "wkla.no-ip.biz/gogs/Willie/MsgService/MessageService/model"
  21. "wkla.no-ip.biz/gogs/Willie/MsgService/MessageService/worker"
  22. "wkla.no-ip.biz/gogs/Willie/MsgService/MessageService/internal/crypt"
  23. consulApi "github.com/hashicorp/consul/api"
  24. config "wkla.no-ip.biz/gogs/Willie/MsgService/MessageService/config"
  25. "wkla.no-ip.biz/gogs/Willie/MsgService/MessageService/logging"
  26. "github.com/go-chi/chi"
  27. "github.com/go-chi/chi/middleware"
  28. "github.com/go-chi/render"
  29. flag "github.com/spf13/pflag"
  30. )
  31. //apiVersion implementing api version for this service
  32. const apiVersion = "1"
  33. const servicename = "autorest-srv"
  34. var port int
  35. var sslport int
  36. var system string
  37. var serviceURL string
  38. var registryURL string
  39. var apikey string
  40. var ssl bool
  41. var configFile string
  42. var serviceConfig config.Config
  43. var consulAgent *consulApi.Agent
  44. var log logging.ServiceLogger
  45. func init() {
  46. // variables for parameter override
  47. ssl = false
  48. log.Info("init service")
  49. flag.IntVarP(&port, "port", "p", 0, "port of the http server.")
  50. flag.IntVarP(&sslport, "sslport", "t", 0, "port of the https server.")
  51. flag.StringVarP(&system, "systemid", "s", "", "this is the systemid of this service. Used for the apikey generation")
  52. flag.StringVarP(&configFile, "config", "c", config.File, "this is the path and filename to the config file")
  53. flag.StringVarP(&serviceURL, "serviceURL", "u", "", "service url from outside")
  54. flag.StringVarP(&registryURL, "registryURL", "r", "", "registry url where to connect to consul")
  55. }
  56. func cors(next http.Handler) http.Handler {
  57. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  58. w.Header().Set("Access-Control-Allow-Origin", "*")
  59. w.Header().Set("Access-Control-Allow-Methods", "*")
  60. w.Header().Set("Access-Control-Allow-Headers", "*")
  61. w.Header().Set("Access-Control-Allow-Credentials", "true")
  62. log.Infof("Should set headers")
  63. if r.Method == "OPTIONS" {
  64. log.Infof("Should return for OPTIONS")
  65. return
  66. }
  67. next.ServeHTTP(w, r)
  68. })
  69. }
  70. func routes() *chi.Mux {
  71. // sysApiHandler := api.NewSysAPIHandler(serviceConfig.SystemID, apikey)
  72. baseURL := fmt.Sprintf("/api/v%s", apiVersion)
  73. router := chi.NewRouter()
  74. router.Use(
  75. cors,
  76. render.SetContentType(render.ContentTypeJSON),
  77. middleware.Logger,
  78. middleware.Compress(5),
  79. middleware.Recoverer,
  80. //sysApiHandler.Handler,
  81. )
  82. router.Route("/", func(r chi.Router) {
  83. r.With(api.BasicAuth(servicename)).Mount(baseURL+"/models", api.ModelRoutes())
  84. r.With(api.BasicAuth(servicename)).Mount(baseURL+"/files", api.FilesRoutes())
  85. r.With(api.BasicAuth(servicename)).Mount(baseURL+"/users", api.UsersRoutes())
  86. r.With(api.BasicAuth(servicename)).Mount(baseURL+"/"+api.AdminPrefix, api.AdminRoutes())
  87. r.Mount("/health", health.Routes())
  88. })
  89. staticDir := serviceConfig.WebRoot
  90. if staticDir != "" {
  91. workDir, _ := os.Getwd()
  92. staticFiles, err := filepath.Abs(filepath.Join(workDir, staticDir))
  93. if err != nil {
  94. log.Alertf("can't serve static files: %s", err.Error())
  95. panic(1)
  96. }
  97. filesDir := http.Dir(staticFiles)
  98. FileServer(router, "/files", filesDir)
  99. }
  100. return router
  101. }
  102. // GetPublicInfoHandler getting server info
  103. func GetPublicInfoHandler(response http.ResponseWriter, request *http.Request) {
  104. log.Infof("GET: path: %s", request.URL.Path)
  105. jsonObject := make([]string, 0)
  106. jsonObject = append(jsonObject, "pub hund")
  107. jsonObject = append(jsonObject, "pub katze")
  108. jsonObject = append(jsonObject, "pub maus")
  109. render.JSON(response, request, jsonObject)
  110. }
  111. // GetPrivateInfoHandler getting server info
  112. func GetPrivateInfoHandler(response http.ResponseWriter, request *http.Request) {
  113. log.Infof("GET: path: %s", request.URL.Path)
  114. jsonObject := make([]string, 0)
  115. jsonObject = append(jsonObject, "prv hund")
  116. jsonObject = append(jsonObject, "prv katze")
  117. jsonObject = append(jsonObject, "prv maus")
  118. render.JSON(response, request, jsonObject)
  119. }
  120. func healthRoutes() *chi.Mux {
  121. router := chi.NewRouter()
  122. router.Use(
  123. cors,
  124. render.SetContentType(render.ContentTypeJSON),
  125. middleware.Logger,
  126. middleware.Compress(5),
  127. middleware.Recoverer,
  128. )
  129. router.Route("/", func(r chi.Router) {
  130. r.Mount("/health", health.Routes())
  131. })
  132. return router
  133. }
  134. func main() {
  135. log.Info("starting server")
  136. _, err := crypt.GenerateRandomBytes(20)
  137. if err != nil {
  138. log.Alertf("can't generate secure salts: %s", err.Error())
  139. panic(1)
  140. }
  141. flag.Parse()
  142. config.File = configFile
  143. if err := config.Load(); err != nil {
  144. log.Alertf("can't load config file: %s", err.Error())
  145. }
  146. serviceConfig = config.Get()
  147. initConfig()
  148. initGraylog()
  149. healthCheckConfig := health.CheckConfig(serviceConfig.HealthCheck)
  150. backgroundTasksConfig := worker.BackgroundConfig(serviceConfig.BackgroundTasks)
  151. defer log.Close()
  152. if serviceConfig.SystemID == "" {
  153. log.Fatal("system id not given, can't start! Please use config file or -s parameter")
  154. }
  155. gc := crypt.GenerateCertificate{
  156. Organization: "MCS Media Computer Spftware",
  157. Host: "127.0.0.1",
  158. ValidFor: 10 * 365 * 24 * time.Hour,
  159. IsCA: false,
  160. EcdsaCurve: "P256",
  161. Ed25519Key: true,
  162. }
  163. if serviceConfig.Sslport > 0 {
  164. ssl = true
  165. log.Info("ssl active")
  166. }
  167. storage := &dao.MongoDAO{}
  168. storage.InitDAO(config.Get().MongoDB)
  169. dao.SetStorage(storage)
  170. // initialise the identity managment system
  171. idm := dao.NewIDM()
  172. idm.InitIDM()
  173. dao.SetIDM(idm)
  174. health.InitHealthSystem(healthCheckConfig)
  175. worker.InitBackgroundTasks(backgroundTasksConfig)
  176. apikey = getApikey()
  177. log.Infof("systemid: %s", serviceConfig.SystemID)
  178. log.Infof("apikey: %s", apikey)
  179. log.Infof("ssl: %t", ssl)
  180. log.Infof("serviceURL: %s", serviceConfig.ServiceURL)
  181. if serviceConfig.RegistryURL != "" {
  182. log.Infof("registryURL: %s", serviceConfig.RegistryURL)
  183. }
  184. router := routes()
  185. walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
  186. log.Infof("%s %s", method, route)
  187. return nil
  188. }
  189. if err := chi.Walk(router, walkFunc); err != nil {
  190. log.Alertf("Logging err: %s", err.Error())
  191. }
  192. log.Info("Health routes")
  193. healthRouter := healthRoutes()
  194. if err := chi.Walk(healthRouter, walkFunc); err != nil {
  195. log.Alertf("Logging err: %s", err.Error())
  196. }
  197. initAutoRest()
  198. var sslsrv *http.Server
  199. var srv *http.Server
  200. if ssl {
  201. tlsConfig, err := gc.GenerateTLSConfig()
  202. if err != nil {
  203. log.Alertf("logging err: %s", err.Error())
  204. }
  205. sslsrv = &http.Server{
  206. Addr: "0.0.0.0:" + strconv.Itoa(serviceConfig.Sslport),
  207. WriteTimeout: time.Second * 15,
  208. ReadTimeout: time.Second * 15,
  209. IdleTimeout: time.Second * 60,
  210. Handler: router,
  211. TLSConfig: tlsConfig,
  212. }
  213. go func() {
  214. log.Infof("starting https server on address: %s", sslsrv.Addr)
  215. if err := sslsrv.ListenAndServeTLS("", ""); err != nil {
  216. log.Alertf("error starting server: %s", err.Error())
  217. }
  218. }()
  219. srv = &http.Server{
  220. Addr: "0.0.0.0:" + strconv.Itoa(serviceConfig.Port),
  221. WriteTimeout: time.Second * 15,
  222. ReadTimeout: time.Second * 15,
  223. IdleTimeout: time.Second * 60,
  224. Handler: healthRouter,
  225. }
  226. go func() {
  227. log.Infof("starting http server on address: %s", srv.Addr)
  228. if err := srv.ListenAndServe(); err != nil {
  229. log.Alertf("error starting server: %s", err.Error())
  230. }
  231. }()
  232. } else {
  233. // own http server for the healthchecks
  234. srv = &http.Server{
  235. Addr: "0.0.0.0:" + strconv.Itoa(serviceConfig.Port),
  236. WriteTimeout: time.Second * 15,
  237. ReadTimeout: time.Second * 15,
  238. IdleTimeout: time.Second * 60,
  239. Handler: router,
  240. }
  241. go func() {
  242. log.Infof("starting http server on address: %s", srv.Addr)
  243. if err := srv.ListenAndServe(); err != nil {
  244. log.Alertf("error starting server: %s", err.Error())
  245. }
  246. }()
  247. }
  248. if serviceConfig.RegistryURL != "" {
  249. initRegistry()
  250. }
  251. //go importData("E:/temp/backup/schematic/dev")
  252. //go generateTempData()
  253. osc := make(chan os.Signal, 1)
  254. signal.Notify(osc, os.Interrupt)
  255. <-osc
  256. log.Info("waiting for clients")
  257. ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
  258. defer cancel()
  259. srv.Shutdown(ctx)
  260. if ssl {
  261. sslsrv.Shutdown(ctx)
  262. }
  263. log.Info("finished")
  264. os.Exit(0)
  265. }
  266. func initGraylog() {
  267. log.GelfURL = serviceConfig.Logging.Gelfurl
  268. log.GelfPort = serviceConfig.Logging.Gelfport
  269. log.SystemID = serviceConfig.SystemID
  270. log.InitGelf()
  271. }
  272. func initRegistry() {
  273. //register to consul, if configured
  274. consulConfig := consulApi.DefaultConfig()
  275. consulURL, err := url.Parse(serviceConfig.RegistryURL)
  276. consulConfig.Scheme = consulURL.Scheme
  277. consulConfig.Address = fmt.Sprintf("%s:%s", consulURL.Hostname(), consulURL.Port())
  278. consulClient, err := consulApi.NewClient(consulConfig)
  279. if err != nil {
  280. log.Alertf("can't connect to consul. %v", err)
  281. }
  282. consulAgent = consulClient.Agent()
  283. check := new(consulApi.AgentServiceCheck)
  284. check.HTTP = fmt.Sprintf("%s/health/health", serviceConfig.ServiceURL)
  285. check.Timeout = (time.Minute * 1).String()
  286. check.Interval = (time.Second * 30).String()
  287. check.TLSSkipVerify = true
  288. serviceDef := &consulApi.AgentServiceRegistration{
  289. Name: servicename,
  290. Check: check,
  291. }
  292. err = consulAgent.ServiceRegister(serviceDef)
  293. if err != nil {
  294. log.Alertf("can't register to consul. %s", err)
  295. time.Sleep(time.Second * 60)
  296. }
  297. }
  298. func initConfig() {
  299. if port > 0 {
  300. serviceConfig.Port = port
  301. }
  302. if sslport > 0 {
  303. serviceConfig.Sslport = sslport
  304. }
  305. if system != "" {
  306. serviceConfig.SystemID = system
  307. }
  308. if serviceURL != "" {
  309. serviceConfig.ServiceURL = serviceURL
  310. }
  311. }
  312. func getApikey() string {
  313. value := fmt.Sprintf("%s_%s", servicename, serviceConfig.SystemID)
  314. apikey := fmt.Sprintf("%x", md5.Sum([]byte(value)))
  315. return strings.ToLower(apikey)
  316. }
  317. func initAutoRest() {
  318. backendPath := serviceConfig.BackendPath
  319. var files []string
  320. err := filepath.Walk(backendPath, func(path string, info os.FileInfo, err error) error {
  321. if !info.IsDir() {
  322. if strings.HasSuffix(info.Name(), ".yaml") {
  323. files = append(files, path)
  324. }
  325. }
  326. return nil
  327. })
  328. if err != nil {
  329. fmt.Println(err)
  330. }
  331. storage := dao.GetStorage()
  332. route := model.Route{
  333. Backend: "_system",
  334. Model: "backends",
  335. Apikey: getApikey(),
  336. SystemID: serviceConfig.SystemID,
  337. }
  338. worker.BackendStorageRoute = route
  339. // importing the files, if needed, to the database
  340. for _, value := range files {
  341. data, err := ioutil.ReadFile(value)
  342. bemodel := model.Backend{}
  343. bemodel.DataSources = make([]model.DataSource, 0)
  344. bemodel.Rules = make([]model.Rule, 0)
  345. err = yaml.Unmarshal(data, &bemodel)
  346. if err != nil {
  347. log.Alertf("%v", err)
  348. break
  349. }
  350. query := fmt.Sprintf("{\"backendname\": \"%s\"}", bemodel.Backendname)
  351. count, _, err := storage.QueryModel(route, query, 0, 10)
  352. if err != nil {
  353. log.Alertf("%v", err)
  354. break
  355. }
  356. if count == 0 {
  357. id, err := worker.StoreBackend(bemodel)
  358. if err != nil {
  359. log.Alertf("%v", err)
  360. break
  361. }
  362. log.Infof("model created : %s", id)
  363. }
  364. }
  365. //now, getting all backends from the database and register them
  366. query := ""
  367. count, backends, err := storage.QueryModel(route, query, 0, 10)
  368. if err != nil {
  369. log.Alertf("%v", err)
  370. return
  371. }
  372. if count > 0 {
  373. for _, dbmodel := range backends {
  374. jsonString, err := json.Marshal(dbmodel)
  375. if err != nil {
  376. log.Alertf("%v", err)
  377. break
  378. }
  379. bemodel := model.Backend{}
  380. bemodel.DataSources = make([]model.DataSource, 0)
  381. bemodel.Rules = make([]model.Rule, 0)
  382. err = json.Unmarshal(jsonString, &bemodel)
  383. if err != nil {
  384. log.Alertf("%v", err)
  385. break
  386. }
  387. err = registerBackend(bemodel)
  388. if err != nil {
  389. log.Alertf("%v", err)
  390. break
  391. }
  392. }
  393. }
  394. }
  395. func registerBackend(bemodel model.Backend) error {
  396. bemodel, err := worker.PrepareBackend(bemodel)
  397. if err != nil {
  398. return fmt.Errorf("validating backend %s, getting error: %v", bemodel.Backendname, err)
  399. }
  400. err = worker.ValidateBackend(bemodel)
  401. if err != nil {
  402. return fmt.Errorf("validating backend %s, getting error: %v", bemodel.Backendname, err)
  403. }
  404. err = worker.RegisterBackend(bemodel)
  405. if err != nil {
  406. return fmt.Errorf("error registering backend %s. %v", bemodel.Backendname, err)
  407. }
  408. backendName := model.BackendList.Add(bemodel)
  409. log.Infof("registering backend %s successfully.", backendName)
  410. return nil
  411. }
  412. // FileServer conveniently sets up a http.FileServer handler to serve
  413. // static files from a http.FileSystem.
  414. func FileServer(r chi.Router, path string, root http.FileSystem) {
  415. if strings.ContainsAny(path, "{}*") {
  416. panic("FileServer does not permit any URL parameters.")
  417. }
  418. if path != "/" && path[len(path)-1] != '/' {
  419. r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
  420. path += "/"
  421. }
  422. path += "*"
  423. r.Get(path, func(w http.ResponseWriter, r *http.Request) {
  424. rctx := chi.RouteContext(r.Context())
  425. pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
  426. fs := http.StripPrefix(pathPrefix, http.FileServer(root))
  427. fs.ServeHTTP(w, r)
  428. })
  429. }