service.go 13 KB

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