service.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  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 Cors(next http.Handler) http.Handler {
  65. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  66. w.Header().Set("Access-Control-Allow-Origin", "*")
  67. w.Header().Set("Access-Control-Allow-Methods", "*")
  68. w.Header().Set("Access-Control-Allow-Headers", "*")
  69. w.Header().Set("Access-Control-Allow-Credentials", "true")
  70. log.Infof("Should set headers")
  71. if r.Method == "OPTIONS" {
  72. log.Infof("Should return for OPTIONS")
  73. return
  74. }
  75. next.ServeHTTP(w, r)
  76. })
  77. }
  78. func routes() *chi.Mux {
  79. //myHandler := api.NewSysAPIHandler(apikey)
  80. baseURL := fmt.Sprintf("/api/v%s", apiVersion)
  81. router := chi.NewRouter()
  82. router.Use(
  83. Cors,
  84. /* cors.Handler(cors.Options{
  85. // AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
  86. AllowedOrigins: []string{"*"},
  87. // AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
  88. AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
  89. AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
  90. ExposedHeaders: []string{"Link"},
  91. AllowCredentials: true,
  92. MaxAge: 300, // Maximum value not ignored by any of major browsers
  93. }),
  94. */
  95. render.SetContentType(render.ContentTypeJSON),
  96. middleware.Logger,
  97. middleware.DefaultCompress,
  98. middleware.Recoverer,
  99. //myHandler.Handler,
  100. api.BasicAuth("schematic"),
  101. )
  102. router.Route("/", func(r chi.Router) {
  103. r.Mount(baseURL+"/config", api.ConfigRoutes())
  104. r.Mount(baseURL+"/files", api.FilesRoutes())
  105. r.Mount(baseURL+"/tags", api.TagsRoutes())
  106. r.Mount(baseURL+"/manufacturers", api.ManufacturersRoutes())
  107. r.Mount(baseURL+"/schematics", api.SchematicsRoutes())
  108. r.Mount(baseURL+"/users", api.UsersRoutes())
  109. r.Mount(baseURL+"/effects", api.EffectsRoutes())
  110. r.Mount(baseURL+"/effecttypes", api.EffectTypesRoutes())
  111. r.Mount("/health", health.Routes())
  112. })
  113. return router
  114. }
  115. func healthRoutes() *chi.Mux {
  116. router := chi.NewRouter()
  117. router.Use(
  118. render.SetContentType(render.ContentTypeJSON),
  119. middleware.Logger,
  120. middleware.DefaultCompress,
  121. middleware.Recoverer,
  122. )
  123. router.Route("/", func(r chi.Router) {
  124. r.Mount("/health", health.Routes())
  125. })
  126. return router
  127. }
  128. func main() {
  129. log.Info("starting server")
  130. flag.Parse()
  131. config.File = configFile
  132. if err := config.Load(); err != nil {
  133. log.Alertf("can't load config file: %s", err.Error())
  134. }
  135. serviceConfig = config.Get()
  136. initConfig()
  137. initGraylog()
  138. healthCheckConfig := health.CheckConfig(serviceConfig.HealthCheck)
  139. defer log.Close()
  140. gc := crypt.GenerateCertificate{
  141. Organization: "MCS Media Computer Software",
  142. Host: "127.0.0.1",
  143. ValidFor: 10 * 365 * 24 * time.Hour,
  144. IsCA: false,
  145. EcdsaCurve: "P256",
  146. Ed25519Key: true,
  147. }
  148. if serviceConfig.Sslport > 0 {
  149. ssl = true
  150. log.Info("ssl active")
  151. }
  152. storage := &dao.MongoDAO{}
  153. storage.InitDAO(config.Get().MongoDB)
  154. dao.Storage = storage
  155. if importPath != "" {
  156. go importData()
  157. }
  158. if effectImportPath != "" {
  159. go effectImportData()
  160. }
  161. health.InitHealthSystem(healthCheckConfig)
  162. apikey = getApikey()
  163. api.APIKey = apikey
  164. log.Infof("apikey: %s", apikey)
  165. log.Infof("ssl: %t", ssl)
  166. log.Infof("serviceURL: %s", serviceConfig.ServiceURL)
  167. if serviceConfig.RegistryURL != "" {
  168. log.Infof("registryURL: %s", serviceConfig.RegistryURL)
  169. }
  170. router := routes()
  171. walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
  172. log.Infof("%s %s", method, route)
  173. return nil
  174. }
  175. if err := chi.Walk(router, walkFunc); err != nil {
  176. log.Alertf("Logging err: %s", err.Error())
  177. }
  178. log.Info("Health routes")
  179. healthRouter := healthRoutes()
  180. if err := chi.Walk(healthRouter, walkFunc); err != nil {
  181. log.Alertf("Logging err: %s", err.Error())
  182. }
  183. var sslsrv *http.Server
  184. var srv *http.Server
  185. if ssl {
  186. tlsConfig, err := gc.GenerateTLSConfig()
  187. if err != nil {
  188. log.Alertf("logging err: %s", err.Error())
  189. }
  190. sslsrv = &http.Server{
  191. Addr: "0.0.0.0:" + strconv.Itoa(serviceConfig.Sslport),
  192. WriteTimeout: time.Second * 15,
  193. ReadTimeout: time.Second * 15,
  194. IdleTimeout: time.Second * 60,
  195. Handler: router,
  196. TLSConfig: tlsConfig,
  197. }
  198. go func() {
  199. log.Infof("starting https server on address: %s", sslsrv.Addr)
  200. if err := sslsrv.ListenAndServeTLS("", ""); err != nil {
  201. log.Alertf("error starting server: %s", err.Error())
  202. }
  203. }()
  204. srv = &http.Server{
  205. Addr: "0.0.0.0:" + strconv.Itoa(serviceConfig.Port),
  206. WriteTimeout: time.Second * 15,
  207. ReadTimeout: time.Second * 15,
  208. IdleTimeout: time.Second * 60,
  209. Handler: healthRouter,
  210. }
  211. go func() {
  212. log.Infof("starting http server on address: %s", srv.Addr)
  213. if err := srv.ListenAndServe(); err != nil {
  214. log.Alertf("error starting server: %s", err.Error())
  215. }
  216. }()
  217. } else {
  218. // own http server for the healthchecks
  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: router,
  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. }
  233. if serviceConfig.RegistryURL != "" {
  234. initRegistry()
  235. }
  236. // starting cron jobs
  237. startCron()
  238. osc := make(chan os.Signal, 1)
  239. signal.Notify(osc, os.Interrupt)
  240. <-osc
  241. log.Info("waiting for clients")
  242. ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
  243. defer cancel()
  244. srv.Shutdown(ctx)
  245. if ssl {
  246. sslsrv.Shutdown(ctx)
  247. }
  248. c.Stop()
  249. log.Info("finished")
  250. os.Exit(0)
  251. }
  252. func initGraylog() {
  253. log.GelfURL = serviceConfig.Logging.Gelfurl
  254. log.GelfPort = serviceConfig.Logging.Gelfport
  255. log.InitGelf()
  256. }
  257. func initRegistry() {
  258. //register to consul, if configured
  259. consulConfig := consulApi.DefaultConfig()
  260. consulURL, err := url.Parse(serviceConfig.RegistryURL)
  261. consulConfig.Scheme = consulURL.Scheme
  262. consulConfig.Address = fmt.Sprintf("%s:%s", consulURL.Hostname(), consulURL.Port())
  263. consulClient, err := consulApi.NewClient(consulConfig)
  264. if err != nil {
  265. log.Alertf("can't connect to consul. %v", err)
  266. }
  267. consulAgent = consulClient.Agent()
  268. check := new(consulApi.AgentServiceCheck)
  269. check.HTTP = fmt.Sprintf("%s/health/health", serviceConfig.ServiceURL)
  270. check.Timeout = (time.Minute * 1).String()
  271. check.Interval = (time.Second * 30).String()
  272. check.TLSSkipVerify = true
  273. serviceDef := &consulApi.AgentServiceRegistration{
  274. Name: servicename,
  275. Check: check,
  276. }
  277. err = consulAgent.ServiceRegister(serviceDef)
  278. if err != nil {
  279. log.Alertf("can't register to consul. %s", err)
  280. time.Sleep(time.Second * 60)
  281. }
  282. }
  283. func initConfig() {
  284. if port > 0 {
  285. serviceConfig.Port = port
  286. }
  287. if sslport > 0 {
  288. serviceConfig.Sslport = sslport
  289. }
  290. if serviceURL != "" {
  291. serviceConfig.ServiceURL = serviceURL
  292. }
  293. }
  294. func getApikey() string {
  295. value := fmt.Sprintf("%s", servicename)
  296. apikey := fmt.Sprintf("%x", md5.Sum([]byte(value)))
  297. return strings.ToLower(apikey)
  298. }
  299. func startCron() {
  300. c = *cron.New()
  301. if serviceConfig.Backup.Period != "" {
  302. log.Info("starting cron with expression: " + serviceConfig.Backup.Period)
  303. c.AddFunc(serviceConfig.Backup.Period, func() { startBackup() })
  304. }
  305. c.Start()
  306. }
  307. func startBackup() {
  308. fmt.Println("Do Backup")
  309. if serviceConfig.Backup.Path != "" {
  310. dao.Storage.Backup(serviceConfig.Backup.Path)
  311. }
  312. }
  313. func importData() {
  314. count := 0
  315. dir := importPath
  316. err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
  317. if info != nil {
  318. if info.IsDir() {
  319. filepath := path + "/schematic.json"
  320. _, err := os.Stat(filepath)
  321. if !os.IsNotExist(err) {
  322. count++
  323. schematic := getSchematic(filepath)
  324. if schematic.Owner == "" {
  325. schematic.Owner = "w.klaas@gmx.de"
  326. }
  327. fileids := make(map[string]string)
  328. for filename, _ := range schematic.Files {
  329. file := path + "/" + filename
  330. f, err := os.Open(file)
  331. if err != nil {
  332. fmt.Printf("error: %s\n", err.Error())
  333. }
  334. defer f.Close()
  335. reader := bufio.NewReader(f)
  336. fileid, err := dao.GetStorage().AddFile(filename, reader)
  337. if err != nil {
  338. fmt.Printf("%v\n", err)
  339. } else {
  340. fmt.Printf("fileid: %s\n", fileid)
  341. fileids[filename] = fileid
  342. }
  343. }
  344. schematic.Files = fileids
  345. id, err := dao.GetStorage().CreateSchematic(schematic)
  346. if err != nil {
  347. fmt.Printf("%v\n", err)
  348. }
  349. fmt.Printf("%d: found %s: man: %s, model: %s\n", count, id, schematic.Manufacturer, schematic.Model)
  350. }
  351. }
  352. }
  353. return nil
  354. })
  355. if err != nil {
  356. fmt.Printf("%v\n", err)
  357. }
  358. }
  359. func effectImportData() {
  360. effectTypesImportData()
  361. effectModelsImportData()
  362. }
  363. func effectTypesImportData() {
  364. effectTypesFilesname := effectImportPath + "/WK_Schematic_Store$EffectType.csv"
  365. effectTypesReader, err := os.Open(effectTypesFilesname)
  366. if err != nil {
  367. fmt.Printf("%v\n", err)
  368. }
  369. defer effectTypesReader.Close()
  370. r := csv.NewReader(effectTypesReader)
  371. r.Comma = ';'
  372. r.Comment = '#'
  373. record, err := r.Read()
  374. fmt.Print(record)
  375. layout := "Mon, 02 Jan 2006 15:04:05 -0700" //"2006-01-02T15:04:05.000Z"
  376. for {
  377. record, err := r.Read()
  378. if err != nil {
  379. break
  380. }
  381. effectType := model.NewEffectType()
  382. effectType.ForeignID = record[1]
  383. effectType.TypeName = record[6]
  384. effectType.CreatedAt, err = time.Parse(layout, record[2])
  385. if err != nil {
  386. fmt.Printf("%v\n", err)
  387. break
  388. }
  389. effectType.LastModifiedAt, err = time.Parse(layout, record[3])
  390. if err != nil {
  391. fmt.Printf("%v\n", err)
  392. break
  393. }
  394. nlsString := record[4]
  395. nlsString = strings.TrimPrefix(nlsString, "[de->")
  396. nlsString = strings.TrimSuffix(nlsString, "]")
  397. effectType.Nls["de"] = nlsString
  398. typeImageString := record[5]
  399. typeImageString = strings.TrimPrefix(typeImageString, "images://")
  400. typeImageString = effectImportPath + "/" + typeImageString
  401. filename := filepath.Base(typeImageString)
  402. fileid, err := uploadFile(filename, typeImageString)
  403. if err != nil {
  404. fmt.Printf("%v\n", err)
  405. break
  406. }
  407. effectType.TypeImage = fileid
  408. dao.Storage.CreateEffectType(effectType)
  409. }
  410. if (err != nil) && (err != io.EOF) {
  411. fmt.Printf("%v\n", err)
  412. }
  413. fmt.Println("ready reading types")
  414. }
  415. func effectModelsImportData() {
  416. effectModelsFilesname := effectImportPath + "/WK_Schematic_Store$Effect.csv"
  417. effectModelsReader, err := os.Open(effectModelsFilesname)
  418. if err != nil {
  419. fmt.Printf("%v\n", err)
  420. }
  421. defer effectModelsReader.Close()
  422. r := csv.NewReader(effectModelsReader)
  423. r.Comma = ';'
  424. r.Comment = '#'
  425. record, err := r.Read()
  426. fmt.Print(record)
  427. layout := "Mon, 02 Jan 2006 15:04:05 -0700" //"2006-01-02T15:04:05.000Z"
  428. for {
  429. record, err := r.Read()
  430. if err != nil {
  431. break
  432. }
  433. effect := model.NewEffect()
  434. effect.ForeignID = record[1]
  435. effect.CreatedAt, err = time.Parse(layout, record[2])
  436. if err != nil {
  437. fmt.Printf("%v\n", err)
  438. break
  439. }
  440. effect.LastModifiedAt, err = time.Parse(layout, record[3])
  441. if err != nil {
  442. fmt.Printf("%v\n", err)
  443. break
  444. }
  445. effect.Comment = record[4]
  446. effect.Connector = record[5]
  447. effect.Current = record[6]
  448. effect.EffectType = record[7]
  449. imageString := record[8]
  450. imageString = strings.TrimPrefix(imageString, "images://")
  451. imageString = effectImportPath + "/" + imageString
  452. filename := filepath.Base(imageString)
  453. fileid, err := uploadFile(filename, imageString)
  454. if err != nil {
  455. fmt.Printf("%v\n", err)
  456. break
  457. }
  458. effect.Image = fileid
  459. effect.Model = record[9]
  460. effect.Manufacturer = record[10]
  461. effect.Voltage = record[12]
  462. dao.Storage.CreateEffect(effect)
  463. }
  464. if (err != nil) && (err != io.EOF) {
  465. fmt.Printf("%v\n", err)
  466. }
  467. fmt.Println("ready reading types")
  468. }
  469. func getSchematic(file string) model.Schematic {
  470. jsonFile, err := os.Open(file)
  471. // if we os.Open returns an error then handle it
  472. if err != nil {
  473. fmt.Printf("%v\n", err)
  474. }
  475. // defer the closing of our jsonFile so that we can parse it later on
  476. defer jsonFile.Close()
  477. byteValue, _ := ioutil.ReadAll(jsonFile)
  478. // fmt.Println(string(byteValue))
  479. var schematic model.Schematic
  480. err = json.Unmarshal(byteValue, &schematic)
  481. if err != nil {
  482. fmt.Printf("%v\n", err)
  483. }
  484. return schematic
  485. }
  486. func uploadFile(filename string, file string) (string, error) {
  487. f, err := os.Open(file)
  488. if err != nil {
  489. return "", err
  490. }
  491. defer f.Close()
  492. reader := bufio.NewReader(f)
  493. fileid, err := dao.GetStorage().AddFile(filename, reader)
  494. if err != nil {
  495. return "", err
  496. }
  497. return fileid, nil
  498. }