service.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. package main
  2. import (
  3. "bufio"
  4. "context"
  5. "crypto/md5"
  6. "encoding/csv"
  7. "encoding/json"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "net/http"
  12. "net/url"
  13. "os"
  14. "os/signal"
  15. "path/filepath"
  16. "strconv"
  17. "strings"
  18. "time"
  19. "github.com/robfig/cron/v3"
  20. api "github.com/willie68/schematic-service-go/api"
  21. "github.com/willie68/schematic-service-go/health"
  22. "github.com/willie68/schematic-service-go/internal/crypt"
  23. consulApi "github.com/hashicorp/consul/api"
  24. config "github.com/willie68/schematic-service-go/config"
  25. "github.com/willie68/schematic-service-go/dao"
  26. "github.com/willie68/schematic-service-go/logging"
  27. "github.com/willie68/schematic-service-go/model"
  28. "github.com/go-chi/chi"
  29. "github.com/go-chi/chi/middleware"
  30. "github.com/go-chi/render"
  31. flag "github.com/spf13/pflag"
  32. )
  33. /*
  34. apiVersion implementing api version for this service
  35. */
  36. const apiVersion = "1"
  37. const servicename = "gomicro"
  38. var port int
  39. var sslport int
  40. var system string
  41. var serviceURL string
  42. var registryURL string
  43. var importPath string
  44. var effectImportPath string
  45. var apikey string
  46. var ssl bool
  47. var configFile string
  48. var serviceConfig config.Config
  49. var consulAgent *consulApi.Agent
  50. var log logging.ServiceLogger
  51. var c cron.Cron
  52. func init() {
  53. // variables for parameter override
  54. ssl = false
  55. log.Info("init service")
  56. flag.IntVarP(&port, "port", "p", 0, "port of the http server.")
  57. flag.IntVarP(&sslport, "sslport", "t", 0, "port of the https server.")
  58. flag.StringVarP(&configFile, "config", "c", config.File, "this is the path and filename to the config file")
  59. flag.StringVarP(&serviceURL, "serviceURL", "u", "", "service url from outside")
  60. flag.StringVarP(&registryURL, "registryURL", "r", "", "registry url where to connect to consul")
  61. flag.StringVarP(&importPath, "import", "i", "", "import data from here")
  62. flag.StringVarP(&effectImportPath, "effect", "e", "", "effect import data from here")
  63. }
  64. func routes() *chi.Mux {
  65. //myHandler := api.NewSysAPIHandler(apikey)
  66. baseURL := fmt.Sprintf("/api/v%s", apiVersion)
  67. router := chi.NewRouter()
  68. router.Use(
  69. render.SetContentType(render.ContentTypeJSON),
  70. middleware.Logger,
  71. middleware.DefaultCompress,
  72. middleware.Recoverer,
  73. //myHandler.Handler,
  74. api.BasicAuth("schematic"),
  75. )
  76. router.Route("/", func(r chi.Router) {
  77. r.Mount(baseURL+"/config", api.ConfigRoutes())
  78. r.Mount(baseURL+"/files", api.FilesRoutes())
  79. r.Mount(baseURL+"/tags", api.TagsRoutes())
  80. r.Mount(baseURL+"/manufacturers", api.ManufacturersRoutes())
  81. r.Mount(baseURL+"/schematics", api.SchematicsRoutes())
  82. r.Mount(baseURL+"/users", api.UsersRoutes())
  83. r.Mount(baseURL+"/effects", api.EffectsRoutes())
  84. r.Mount(baseURL+"/effecttypes", api.EffectTypesRoutes())
  85. r.Mount("/health", health.Routes())
  86. })
  87. return router
  88. }
  89. func healthRoutes() *chi.Mux {
  90. router := chi.NewRouter()
  91. router.Use(
  92. render.SetContentType(render.ContentTypeJSON),
  93. middleware.Logger,
  94. middleware.DefaultCompress,
  95. middleware.Recoverer,
  96. )
  97. router.Route("/", func(r chi.Router) {
  98. r.Mount("/health", health.Routes())
  99. })
  100. return router
  101. }
  102. func main() {
  103. log.Info("starting server")
  104. flag.Parse()
  105. config.File = configFile
  106. if err := config.Load(); err != nil {
  107. log.Alertf("can't load config file: %s", err.Error())
  108. }
  109. serviceConfig = config.Get()
  110. initConfig()
  111. initGraylog()
  112. healthCheckConfig := health.CheckConfig(serviceConfig.HealthCheck)
  113. defer log.Close()
  114. gc := crypt.GenerateCertificate{
  115. Organization: "MCS Media Computer Spftware",
  116. Host: "127.0.0.1",
  117. ValidFor: 10 * 365 * 24 * time.Hour,
  118. IsCA: false,
  119. EcdsaCurve: "P256",
  120. Ed25519Key: true,
  121. }
  122. if serviceConfig.Sslport > 0 {
  123. ssl = true
  124. log.Info("ssl active")
  125. }
  126. storage := &dao.MongoDAO{}
  127. storage.InitDAO(config.Get().MongoDB)
  128. dao.Storage = storage
  129. if importPath != "" {
  130. go importData()
  131. }
  132. if effectImportPath != "" {
  133. go effectImportData()
  134. }
  135. health.InitHealthSystem(healthCheckConfig)
  136. apikey = getApikey()
  137. api.APIKey = apikey
  138. log.Infof("apikey: %s", apikey)
  139. log.Infof("ssl: %t", ssl)
  140. log.Infof("serviceURL: %s", serviceConfig.ServiceURL)
  141. if serviceConfig.RegistryURL != "" {
  142. log.Infof("registryURL: %s", serviceConfig.RegistryURL)
  143. }
  144. router := routes()
  145. walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
  146. log.Infof("%s %s", method, route)
  147. return nil
  148. }
  149. if err := chi.Walk(router, walkFunc); err != nil {
  150. log.Alertf("Logging err: %s", err.Error())
  151. }
  152. log.Info("Health routes")
  153. healthRouter := healthRoutes()
  154. if err := chi.Walk(healthRouter, walkFunc); err != nil {
  155. log.Alertf("Logging err: %s", err.Error())
  156. }
  157. var sslsrv *http.Server
  158. if ssl {
  159. tlsConfig, err := gc.GenerateTLSConfig()
  160. if err != nil {
  161. log.Alertf("logging err: %s", err.Error())
  162. }
  163. sslsrv = &http.Server{
  164. Addr: "0.0.0.0:" + strconv.Itoa(serviceConfig.Sslport),
  165. WriteTimeout: time.Second * 15,
  166. ReadTimeout: time.Second * 15,
  167. IdleTimeout: time.Second * 60,
  168. Handler: router,
  169. TLSConfig: tlsConfig,
  170. }
  171. go func() {
  172. log.Infof("starting https server on address: %s", sslsrv.Addr)
  173. if err := sslsrv.ListenAndServeTLS("", ""); err != nil {
  174. log.Alertf("error starting server: %s", err.Error())
  175. }
  176. }()
  177. }
  178. // own http server for the healthchecks
  179. srv := &http.Server{
  180. Addr: "0.0.0.0:" + strconv.Itoa(serviceConfig.Port),
  181. WriteTimeout: time.Second * 15,
  182. ReadTimeout: time.Second * 15,
  183. IdleTimeout: time.Second * 60,
  184. Handler: healthRouter,
  185. }
  186. go func() {
  187. log.Infof("starting http server on address: %s", srv.Addr)
  188. if err := srv.ListenAndServe(); err != nil {
  189. log.Alertf("error starting server: %s", err.Error())
  190. }
  191. }()
  192. if serviceConfig.RegistryURL != "" {
  193. initRegistry()
  194. }
  195. // starting cron jobs
  196. startCron()
  197. osc := make(chan os.Signal, 1)
  198. signal.Notify(osc, os.Interrupt)
  199. <-osc
  200. log.Info("waiting for clients")
  201. ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
  202. defer cancel()
  203. srv.Shutdown(ctx)
  204. if ssl {
  205. sslsrv.Shutdown(ctx)
  206. }
  207. c.Stop()
  208. log.Info("finished")
  209. os.Exit(0)
  210. }
  211. func initGraylog() {
  212. log.GelfURL = serviceConfig.Logging.Gelfurl
  213. log.GelfPort = serviceConfig.Logging.Gelfport
  214. log.InitGelf()
  215. }
  216. func initRegistry() {
  217. //register to consul, if configured
  218. consulConfig := consulApi.DefaultConfig()
  219. consulURL, err := url.Parse(serviceConfig.RegistryURL)
  220. consulConfig.Scheme = consulURL.Scheme
  221. consulConfig.Address = fmt.Sprintf("%s:%s", consulURL.Hostname(), consulURL.Port())
  222. consulClient, err := consulApi.NewClient(consulConfig)
  223. if err != nil {
  224. log.Alertf("can't connect to consul. %v", err)
  225. }
  226. consulAgent = consulClient.Agent()
  227. check := new(consulApi.AgentServiceCheck)
  228. check.HTTP = fmt.Sprintf("%s/health/health", serviceConfig.ServiceURL)
  229. check.Timeout = (time.Minute * 1).String()
  230. check.Interval = (time.Second * 30).String()
  231. check.TLSSkipVerify = true
  232. serviceDef := &consulApi.AgentServiceRegistration{
  233. Name: servicename,
  234. Check: check,
  235. }
  236. err = consulAgent.ServiceRegister(serviceDef)
  237. if err != nil {
  238. log.Alertf("can't register to consul. %s", err)
  239. time.Sleep(time.Second * 60)
  240. }
  241. }
  242. func initConfig() {
  243. if port > 0 {
  244. serviceConfig.Port = port
  245. }
  246. if sslport > 0 {
  247. serviceConfig.Sslport = sslport
  248. }
  249. if serviceURL != "" {
  250. serviceConfig.ServiceURL = serviceURL
  251. }
  252. }
  253. func getApikey() string {
  254. value := fmt.Sprintf("%s", servicename)
  255. apikey := fmt.Sprintf("%x", md5.Sum([]byte(value)))
  256. return strings.ToLower(apikey)
  257. }
  258. func startCron() {
  259. c = *cron.New()
  260. if serviceConfig.Backup.Period != "" {
  261. log.Info("starting cron with expression: " + serviceConfig.Backup.Period)
  262. c.AddFunc(serviceConfig.Backup.Period, func() { startBackup() })
  263. }
  264. c.Start()
  265. }
  266. func startBackup() {
  267. fmt.Println("Do Backup")
  268. if serviceConfig.Backup.Path != "" {
  269. dao.Storage.Backup(serviceConfig.Backup.Path)
  270. }
  271. }
  272. func importData() {
  273. count := 0
  274. dir := importPath
  275. err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
  276. if info != nil {
  277. if info.IsDir() {
  278. filepath := path + "/schematic.json"
  279. _, err := os.Stat(filepath)
  280. if !os.IsNotExist(err) {
  281. count++
  282. schematic := getSchematic(filepath)
  283. if schematic.Owner == "" {
  284. schematic.Owner = "w.klaas@gmx.de"
  285. }
  286. fileids := make(map[string]string)
  287. for filename, _ := range schematic.Files {
  288. file := path + "/" + filename
  289. f, err := os.Open(file)
  290. if err != nil {
  291. fmt.Printf("error: %s\n", err.Error())
  292. }
  293. defer f.Close()
  294. reader := bufio.NewReader(f)
  295. fileid, err := dao.GetStorage().AddFile(filename, reader)
  296. if err != nil {
  297. fmt.Printf("%v\n", err)
  298. } else {
  299. fmt.Printf("fileid: %s\n", fileid)
  300. fileids[filename] = fileid
  301. }
  302. }
  303. schematic.Files = fileids
  304. id, err := dao.GetStorage().CreateSchematic(schematic)
  305. if err != nil {
  306. fmt.Printf("%v\n", err)
  307. }
  308. fmt.Printf("%d: found %s: man: %s, model: %s\n", count, id, schematic.Manufacturer, schematic.Model)
  309. }
  310. }
  311. }
  312. return nil
  313. })
  314. if err != nil {
  315. fmt.Printf("%v\n", err)
  316. }
  317. }
  318. func effectImportData() {
  319. effectTypesImportData()
  320. effectModelsImportData()
  321. }
  322. func effectTypesImportData() {
  323. effectTypesFilesname := effectImportPath + "/WK_Schematic_Store$EffectType.csv"
  324. effectTypesReader, err := os.Open(effectTypesFilesname)
  325. if err != nil {
  326. fmt.Printf("%v\n", err)
  327. }
  328. defer effectTypesReader.Close()
  329. r := csv.NewReader(effectTypesReader)
  330. r.Comma = ';'
  331. r.Comment = '#'
  332. record, err := r.Read()
  333. fmt.Print(record)
  334. layout := "Mon, 02 Jan 2006 15:04:05 -0700" //"2006-01-02T15:04:05.000Z"
  335. for {
  336. record, err := r.Read()
  337. if err != nil {
  338. break
  339. }
  340. effectType := model.NewEffectType()
  341. effectType.ForeignID = record[1]
  342. effectType.TypeName = record[6]
  343. effectType.CreatedAt, err = time.Parse(layout, record[2])
  344. if err != nil {
  345. fmt.Printf("%v\n", err)
  346. break
  347. }
  348. effectType.LastModifiedAt, err = time.Parse(layout, record[3])
  349. if err != nil {
  350. fmt.Printf("%v\n", err)
  351. break
  352. }
  353. nlsString := record[4]
  354. nlsString = strings.TrimPrefix(nlsString, "[de->")
  355. nlsString = strings.TrimSuffix(nlsString, "]")
  356. effectType.Nls["de"] = nlsString
  357. typeImageString := record[5]
  358. typeImageString = strings.TrimPrefix(typeImageString, "images://")
  359. typeImageString = effectImportPath + "/" + typeImageString
  360. filename := filepath.Base(typeImageString)
  361. fileid, err := uploadFile(filename, typeImageString)
  362. if err != nil {
  363. fmt.Printf("%v\n", err)
  364. break
  365. }
  366. effectType.TypeImage = fileid
  367. dao.Storage.CreateEffectType(effectType)
  368. }
  369. if (err != nil) && (err != io.EOF) {
  370. fmt.Printf("%v\n", err)
  371. }
  372. fmt.Println("ready reading types")
  373. }
  374. func effectModelsImportData() {
  375. effectModelsFilesname := effectImportPath + "/WK_Schematic_Store$Effect.csv"
  376. effectModelsReader, err := os.Open(effectModelsFilesname)
  377. if err != nil {
  378. fmt.Printf("%v\n", err)
  379. }
  380. defer effectModelsReader.Close()
  381. r := csv.NewReader(effectModelsReader)
  382. r.Comma = ';'
  383. r.Comment = '#'
  384. record, err := r.Read()
  385. fmt.Print(record)
  386. layout := "Mon, 02 Jan 2006 15:04:05 -0700" //"2006-01-02T15:04:05.000Z"
  387. for {
  388. record, err := r.Read()
  389. if err != nil {
  390. break
  391. }
  392. effect := model.NewEffect()
  393. effect.ForeignID = record[1]
  394. effect.CreatedAt, err = time.Parse(layout, record[2])
  395. if err != nil {
  396. fmt.Printf("%v\n", err)
  397. break
  398. }
  399. effect.LastModifiedAt, err = time.Parse(layout, record[3])
  400. if err != nil {
  401. fmt.Printf("%v\n", err)
  402. break
  403. }
  404. effect.Comment = record[4]
  405. effect.Connector = record[5]
  406. effect.Current = record[6]
  407. effect.EffectType = record[7]
  408. imageString := record[8]
  409. imageString = strings.TrimPrefix(imageString, "images://")
  410. imageString = effectImportPath + "/" + imageString
  411. filename := filepath.Base(imageString)
  412. fileid, err := uploadFile(filename, imageString)
  413. if err != nil {
  414. fmt.Printf("%v\n", err)
  415. break
  416. }
  417. effect.Image = fileid
  418. effect.Model = record[9]
  419. effect.Manufacturer = record[10]
  420. effect.Voltage = record[12]
  421. dao.Storage.CreateEffect(effect)
  422. }
  423. if (err != nil) && (err != io.EOF) {
  424. fmt.Printf("%v\n", err)
  425. }
  426. fmt.Println("ready reading types")
  427. }
  428. func getSchematic(file string) model.Schematic {
  429. jsonFile, err := os.Open(file)
  430. // if we os.Open returns an error then handle it
  431. if err != nil {
  432. fmt.Printf("%v\n", err)
  433. }
  434. // defer the closing of our jsonFile so that we can parse it later on
  435. defer jsonFile.Close()
  436. byteValue, _ := ioutil.ReadAll(jsonFile)
  437. // fmt.Println(string(byteValue))
  438. var schematic model.Schematic
  439. err = json.Unmarshal(byteValue, &schematic)
  440. if err != nil {
  441. fmt.Printf("%v\n", err)
  442. }
  443. return schematic
  444. }
  445. func uploadFile(filename string, file string) (string, error) {
  446. f, err := os.Open(file)
  447. if err != nil {
  448. return "", err
  449. }
  450. defer f.Close()
  451. reader := bufio.NewReader(f)
  452. fileid, err := dao.GetStorage().AddFile(filename, reader)
  453. if err != nil {
  454. return "", err
  455. }
  456. return fileid, nil
  457. }