/ gibson.go
gibson.go
1 package main 2 3 import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "html/template" 8 "io" 9 "log" 10 "net/http" 11 "os" 12 "strconv" 13 "strings" 14 "time" 15 16 utils "gibson/internal" 17 18 "github.com/BurntSushi/toml" 19 "github.com/fsnotify/fsnotify" 20 "github.com/labstack/echo/v4" 21 "github.com/labstack/echo/v4/middleware" 22 ) 23 24 const Version = "0.7.4" 25 26 var ConfigFile string 27 var config utils.TomlConfig 28 var feedrss []utils.FeedRss 29 var rssChannel chan []utils.FeedRss 30 var pinnedposts []utils.Pinned 31 var links []utils.Link 32 33 type Template struct { 34 templates *template.Template 35 } 36 37 // https://stackoverflow.com/questions/72929883/golang-echo-labstack-how-to-call-a-function-method-in-a-template-view 38 func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { 39 40 // Add global methods if data is a map 41 if viewContext, isMap := data.(map[string]interface{}); isMap { 42 viewContext["reverse"] = c.Echo().Reverse 43 } 44 45 return t.templates.ExecuteTemplate(w, name, data) 46 } 47 48 func customHTTPErrorHandler(err error, c echo.Context) { 49 code := http.StatusInternalServerError 50 if he, ok := err.(*echo.HTTPError); ok { 51 code = he.Code 52 } 53 c.Logger().Error(err) 54 errorPage := fmt.Sprintf(config.Gibson.Directory+"templates/%d.html", code) 55 if err := c.File(errorPage); err != nil { 56 c.Logger().Error(err) 57 } 58 } 59 60 // https://stackoverflow.com/questions/30065203/golang-html-templating-range-limit 61 func mkslice(a []utils.BlogPost, start, end int) []utils.BlogPost { 62 return a[start:end] 63 } 64 65 func reloadMarkdown(c echo.Context) error { 66 var template Template 67 w := c.Response() 68 w.Header().Set("Content-Type", "text/event-stream") 69 w.Header().Set("Cache-Control", "no-cache") 70 w.Header().Set("Connection", "keep-alive") 71 72 // Create new watcher. 73 watcher, err := fsnotify.NewWatcher() 74 if err != nil { 75 log.Fatal(err) 76 } 77 defer watcher.Close() 78 79 // Add a path. 80 err = watcher.Add(config.Gibson.Directory + "markdown/posts") 81 if err != nil { 82 log.Fatal(err) 83 } 84 85 // Start listening for events. 86 for { 87 select { 88 case <-c.Request().Context().Done(): 89 //log.Printf("SSE client disconnected, ip: %v", c.RealIP()) 90 return nil 91 92 case event, ok := <-watcher.Events: 93 if !ok { 94 return nil 95 } 96 //log.Println("event:", event) 97 if event.Has(fsnotify.Write) { 98 //log.Println("modified file:", event.Name) 99 100 payload, err := utils.LoadMarkdownPost(config.Gibson.Directory + event.Name) 101 if err != nil { 102 fmt.Println(err) 103 return nil 104 } 105 var parse bytes.Buffer 106 parsedTemplate, _ := template.templates.ParseFiles(config.Gibson.Directory + "templates/content_reload.html") 107 parsedTemplate.Execute(&parse, payload) 108 109 event := utils.Event{ 110 Data: []byte(parse.Bytes()), 111 } 112 if err := event.MarshalTo(w); err != nil { 113 return err 114 } 115 w.Flush() 116 } 117 case err, ok := <-watcher.Errors: 118 if !ok { 119 return nil 120 } 121 log.Println("error:", err) 122 } 123 } 124 125 } 126 127 func httpServer() { 128 e := echo.New() 129 //e.Debug = true 130 e.Use(middleware.Logger()) 131 mc := utils.NewMediaController(config.S3) 132 e.GET("/media/:id", mc.ExtractFromS3) 133 134 s := &http.Server{ 135 Addr: "127.0.0.1:8079", 136 ReadTimeout: 60 * time.Minute, 137 WriteTimeout: 60 * time.Minute, 138 } 139 fmt.Println("HttpServer START") 140 e.Logger.Fatal(e.StartServer(s)) 141 } 142 143 func handleSSE(c echo.Context) error { 144 //log.Printf("SSE client connected, ip: %v", c.RealIP()) 145 146 w := c.Response() 147 w.Header().Set("Content-Type", "text/event-stream") 148 w.Header().Set("Cache-Control", "no-cache") 149 w.Header().Set("Connection", "keep-alive") 150 151 var template Template 152 var payload utils.Payload 153 154 for { 155 select { 156 case <-c.Request().Context().Done(): 157 //log.Printf("SSE client disconnected, ip: %v", c.RealIP()) 158 return nil 159 160 case feedrss := <-rssChannel: 161 payload.FeedRss = feedrss 162 var parse bytes.Buffer 163 parsedTemplate, _ := template.templates.ParseFiles(config.Gibson.Directory + "templates/rss_reload.html") 164 parsedTemplate.Execute(&parse, payload) 165 166 event := utils.Event{ 167 Data: []byte(parse.Bytes()), 168 } 169 if err := event.MarshalTo(w); err != nil { 170 return err 171 } 172 w.Flush() 173 } 174 } 175 } 176 177 // ServerHeader middleware adds a `AccessControlAllowOrigin` header to the response. 178 func ServerHeader(next echo.HandlerFunc) echo.HandlerFunc { 179 return func(c echo.Context) error { 180 c.Response().Header().Set(echo.HeaderAccessControlAllowOrigin, "*") 181 return next(c) 182 } 183 } 184 185 func loadMarkdown(e *echo.Echo) error { 186 187 // external data 188 fmt.Println("before external") 189 externalData, err := utils.LoadExternalData(config.Gibson.Directory + "markdown/external/") 190 if err != nil { 191 log.Fatal(err) 192 } 193 fmt.Println("after external") 194 // pages data 195 pagesData, err := utils.LoadData(config.Gibson.Directory + "markdown/pages/") 196 if err != nil { 197 log.Fatal(err) 198 } 199 200 // textes data 201 textesData, err := utils.LoadData(config.Gibson.Directory + "markdown/textes/") 202 if err != nil { 203 log.Fatal(err) 204 } 205 206 // posts data 207 postsData, err := utils.LoadData(config.Gibson.Directory + "markdown/posts/") 208 if err != nil { 209 log.Fatal(err) 210 } 211 212 // parse pinned articles 213 // 214 pinnedposts = nil 215 for _, article := range config.Pinned { 216 l_pinnedposts := utils.Pinned{Name: article.Name, URL: article.URL} 217 pinnedposts = append(pinnedposts, l_pinnedposts) 218 } 219 220 // parse links 221 // 222 links = nil 223 for _, link := range config.Link { 224 l_links := utils.Link{Name: link.Name, URL: link.URL} 225 links = append(links, l_links) 226 } 227 228 // generate RSS from markdown/posts/ 229 err = utils.RssGenerate(postsData.Categories[0].Pages, config) 230 if err != nil { 231 log.Fatal(err) 232 } 233 234 e.GET("/tags/:id", func(c echo.Context) error { 235 tagsData := utils.BuildData(c.Param("id"), postsData) 236 237 indexPath := config.Gibson.Directory + "markdown/posts/index.md" 238 indexContent, err := os.ReadFile(indexPath) 239 if err != nil { 240 c.Logger().Error(err) 241 return err 242 } 243 244 post, err := utils.ParseMarkdownFile(indexContent) 245 if err != nil { 246 c.Logger().Error(err) 247 return err 248 } 249 sidebarLinks := utils.CreateSidebarLinks(post.Headers) 250 251 var nbpages int 252 if tagsData.Categories[0].NbPosts > 4 { 253 nbpages = (tagsData.Categories[0].NbPosts / 5) + 1 254 tagsData.Categories[0].NBpages = 5 255 } else { 256 nbpages = tagsData.Categories[0].NbPosts 257 tagsData.Categories[0].NBpages = nbpages 258 } 259 260 var payload utils.Payload 261 payload.BlogTitle = config.Blog.Title 262 payload.BlogDesc = config.Blog.Desc 263 payload.BlogCopyright = config.Blog.Copyright 264 payload.Version = Version 265 payload.FeedRss = feedrss 266 payload.PinnedPosts = pinnedposts 267 payload.Links = links 268 payload.Title = post.Title 269 payload.Date = post.Date.Format(time.RFC1123) 270 payload.Content = post.Content 271 payload.ExternalData = externalData 272 payload.PagesData = pagesData 273 payload.PostsData = tagsData 274 payload.TextesData = textesData 275 payload.PostsData.Categories[0].CurrentPage = 1 276 payload.Headers = post.Headers 277 payload.SidebarLinks = sidebarLinks 278 payload.CurrentSlug = post.Slug 279 payload.MetaDescription = post.MetaDescription 280 payload.MetaPropertyTitle = post.MetaPropertyTitle 281 payload.MetaPropertyDescription = post.MetaPropertyDescription 282 payload.MetaOgURL = post.MetaOgURL 283 284 return c.Render(http.StatusOK, "tagsindex", payload) 285 }) 286 287 e.GET("/", func(c echo.Context) error { 288 indexPath := config.Gibson.Directory + "markdown/posts/index.md" 289 indexContent, err := os.ReadFile(indexPath) 290 if err != nil { 291 c.Logger().Error(err) 292 return err 293 } 294 295 post, err := utils.ParseMarkdownFile(indexContent) 296 if err != nil { 297 c.Logger().Error(err) 298 return err 299 } 300 301 sidebarLinks := utils.CreateSidebarLinks(post.Headers) 302 303 var nbpages int 304 if postsData.Categories[0].NbPosts > 4 { 305 nbpages = (postsData.Categories[0].NbPosts / 5) + 1 306 postsData.Categories[0].NBpages = 5 307 } else { 308 nbpages = postsData.Categories[0].NbPosts 309 postsData.Categories[0].NBpages = nbpages 310 } 311 312 var payload utils.Payload 313 payload.BlogTitle = config.Blog.Title 314 payload.BlogDesc = config.Blog.Desc 315 payload.BlogCopyright = config.Blog.Copyright 316 payload.Version = Version 317 payload.FeedRss = feedrss 318 payload.PinnedPosts = pinnedposts 319 payload.Links = links 320 payload.Title = post.Title 321 payload.Date = post.Date.Format(time.RFC1123) 322 payload.Content = post.Content 323 payload.ExternalData = externalData 324 payload.PagesData = pagesData 325 payload.PostsData = postsData 326 payload.TextesData = textesData 327 payload.PostsData.Categories[0].CurrentPage = 1 328 payload.Headers = post.Headers 329 payload.SidebarLinks = sidebarLinks 330 payload.CurrentSlug = post.Slug 331 payload.MetaDescription = post.MetaDescription 332 payload.MetaPropertyTitle = post.MetaPropertyTitle 333 payload.MetaPropertyDescription = post.MetaPropertyDescription 334 payload.MetaOgURL = post.MetaOgURL 335 336 return c.Render(http.StatusOK, "index", payload) 337 }) 338 339 e.GET("/external", func(c echo.Context) error { 340 indexPath := config.Gibson.Directory + "markdown/external/index.md" 341 indexContent, err := os.ReadFile(indexPath) 342 if err != nil { 343 c.Logger().Error(err) 344 return err 345 } 346 347 post, err := utils.ParseExternalMarkdownFile(indexContent) 348 if err != nil { 349 c.Logger().Error(err) 350 return err 351 } 352 sidebarLinks := utils.CreateSidebarLinks(post.Headers) 353 354 var payload utils.Payload 355 payload.BlogTitle = config.Blog.Title 356 payload.BlogDesc = config.Blog.Desc 357 payload.BlogCopyright = config.Blog.Copyright 358 payload.Version = Version 359 payload.FeedRss = feedrss 360 payload.PinnedPosts = pinnedposts 361 payload.Links = links 362 payload.Title = post.Title 363 payload.Date = post.Published.Format(time.RFC1123) 364 payload.Content = post.Content 365 payload.ExternalData = externalData 366 payload.PagesData = pagesData 367 payload.PostsData = postsData 368 payload.TextesData = textesData 369 payload.PostsData.Categories[0].CurrentPage = 1 370 payload.Headers = post.Headers 371 payload.SidebarLinks = sidebarLinks 372 payload.CurrentSlug = post.Slug 373 374 return c.Render(http.StatusOK, "external", payload) 375 }) 376 377 e.GET("/textes", func(c echo.Context) error { 378 indexPath := config.Gibson.Directory + "markdown/textes/index.md" 379 indexContent, err := os.ReadFile(indexPath) 380 if err != nil { 381 c.Logger().Error(err) 382 return err 383 } 384 385 post, err := utils.ParseMarkdownFile(indexContent) 386 if err != nil { 387 c.Logger().Error(err) 388 return err 389 } 390 sidebarLinks := utils.CreateSidebarLinks(post.Headers) 391 392 var nbpages int 393 if textesData.Categories[0].NbPosts > 4 { 394 nbpages = (textesData.Categories[0].NbPosts / 5) + 1 395 textesData.Categories[0].NBpages = 5 396 } else { 397 nbpages = textesData.Categories[0].NbPosts 398 textesData.Categories[0].NBpages = nbpages 399 } 400 401 var payload utils.Payload 402 payload.BlogTitle = config.Blog.Title 403 payload.BlogDesc = config.Blog.Desc 404 payload.BlogCopyright = config.Blog.Copyright 405 payload.Version = Version 406 payload.FeedRss = feedrss 407 payload.PinnedPosts = pinnedposts 408 payload.Links = links 409 payload.Title = post.Title 410 payload.Date = post.Date.Format(time.RFC1123) 411 payload.Content = post.Content 412 payload.ExternalData = externalData 413 payload.PagesData = pagesData 414 payload.PostsData = postsData 415 payload.TextesData = textesData 416 payload.PostsData.Categories[0].CurrentPage = 1 417 payload.Headers = post.Headers 418 payload.SidebarLinks = sidebarLinks 419 payload.CurrentSlug = post.Slug 420 payload.MetaDescription = post.MetaDescription 421 payload.MetaPropertyTitle = post.MetaPropertyTitle 422 payload.MetaPropertyDescription = post.MetaPropertyDescription 423 payload.MetaOgURL = post.MetaOgURL 424 425 return c.Render(http.StatusOK, "textes", payload) 426 }) 427 428 e.GET("/page/:id", func(c echo.Context) error { 429 indexPath := config.Gibson.Directory + "markdown/posts/index.md" 430 indexContent, err := os.ReadFile(indexPath) 431 if err != nil { 432 c.Logger().Error(err) 433 return err 434 } 435 436 post, err := utils.ParseMarkdownFile(indexContent) 437 if err != nil { 438 c.Logger().Error(err) 439 return err 440 } 441 sidebarLinks := utils.CreateSidebarLinks(post.Headers) 442 443 index, err := strconv.Atoi(c.Param("id")) 444 if err != nil { 445 c.Logger().Error(err) 446 return echo.NewHTTPError(http.StatusNotFound) 447 } 448 if index <= 0 { 449 index = 1 450 } 451 var nbpages int 452 if postsData.Categories[0].NbPosts > 4 { 453 nbpages = (postsData.Categories[0].NbPosts / 5) + 1 454 postsData.Categories[0].NBpages = 5 455 } else { 456 nbpages = postsData.Categories[0].NbPosts 457 postsData.Categories[0].NBpages = nbpages 458 } 459 if index > nbpages { 460 index = nbpages 461 } 462 463 // fmt.Println("nbpages : ", nbpages) 464 465 var payload utils.Payload 466 payload.BlogTitle = config.Blog.Title 467 payload.BlogDesc = config.Blog.Desc 468 payload.BlogCopyright = config.Blog.Copyright 469 payload.Version = Version 470 payload.FeedRss = feedrss 471 payload.PinnedPosts = pinnedposts 472 payload.Links = links 473 payload.Title = post.Title 474 payload.Date = post.Date.Format(time.RFC1123) 475 payload.Content = post.Content 476 payload.ExternalData = externalData 477 payload.PagesData = pagesData 478 payload.PostsData = postsData 479 payload.PostsData.Categories[0].CurrentPage = index 480 payload.TextesData = textesData 481 payload.Headers = post.Headers 482 payload.SidebarLinks = sidebarLinks 483 payload.CurrentSlug = post.Slug 484 payload.MetaDescription = post.MetaDescription 485 payload.MetaPropertyTitle = post.MetaPropertyTitle 486 payload.MetaPropertyDescription = post.MetaPropertyDescription 487 payload.MetaOgURL = post.MetaOgURL 488 489 return c.Render(http.StatusOK, "index", payload) 490 }) 491 492 e.GET("/posts", func(c echo.Context) error { 493 indexPath := config.Gibson.Directory + "markdown/posts/index.md" 494 indexContent, err := os.ReadFile(indexPath) 495 if err != nil { 496 c.Logger().Error(err) 497 return err 498 } 499 500 post, err := utils.ParseMarkdownFile(indexContent) 501 if err != nil { 502 c.Logger().Error(err) 503 return err 504 } 505 sidebarLinks := utils.CreateSidebarLinks(post.Headers) 506 507 var payload utils.Payload 508 payload.BlogTitle = config.Blog.Title 509 payload.BlogDesc = config.Blog.Desc 510 payload.BlogCopyright = config.Blog.Copyright 511 payload.Version = Version 512 payload.FeedRss = feedrss 513 payload.PinnedPosts = pinnedposts 514 payload.Links = links 515 payload.Title = post.Title 516 payload.Date = post.Date.Format(time.RFC1123) 517 payload.Content = post.Content 518 payload.ExternalData = externalData 519 payload.PagesData = pagesData 520 payload.PostsData = postsData 521 payload.TextesData = textesData 522 payload.Headers = post.Headers 523 payload.SidebarLinks = sidebarLinks 524 payload.CurrentSlug = post.Slug 525 payload.MetaDescription = post.MetaDescription 526 payload.MetaPropertyTitle = post.MetaPropertyTitle 527 payload.MetaPropertyDescription = post.MetaPropertyDescription 528 payload.MetaOgURL = post.MetaOgURL 529 530 return c.Render(http.StatusOK, "posts_index", payload) 531 }) 532 533 for _, external := range externalData.Categories[0].Pages { 534 localPost := external 535 if localPost.Slug != "" { 536 sidebarLinks := utils.CreateSidebarLinks(localPost.Headers) 537 slug := strings.ReplaceAll(localPost.Slug, "\"", "") 538 e.GET("/external/"+slug+"/", func(c echo.Context) error { 539 var payload utils.Payload 540 payload.BlogTitle = config.Blog.Title 541 payload.BlogDesc = config.Blog.Desc 542 payload.BlogCopyright = config.Blog.Copyright 543 payload.Version = Version 544 payload.FeedRss = feedrss 545 payload.PinnedPosts = pinnedposts 546 payload.Links = links 547 payload.Title = strings.ReplaceAll(localPost.Title, "\"", "") 548 payload.Date = strings.ReplaceAll(localPost.Published.Format(time.RFC1123), "\"", "") 549 payload.Content = localPost.Content 550 payload.ExternalData = externalData 551 payload.PagesData = pagesData 552 payload.TextesData = textesData 553 payload.Headers = localPost.Headers 554 payload.Description = strings.ReplaceAll(localPost.Description, "\"", "") 555 payload.SidebarLinks = sidebarLinks 556 payload.CurrentSlug = strings.ReplaceAll(localPost.Slug, "\"", "") 557 payload.Source = strings.ReplaceAll(localPost.Source, "\"", "") 558 payload.Author = strings.ReplaceAll(localPost.Author, "\"", "") 559 560 return c.Render(http.StatusOK, "layout_external_page", payload) 561 }) 562 } else { 563 log.Printf("Warning: Post titled '%s' has an empty slug and will not be accessible via a unique URL.\n", localPost.Title) 564 } 565 } 566 567 for _, page := range pagesData.Categories[0].Pages { 568 localPost := page 569 if localPost.Slug != "" { 570 sidebarLinks := utils.CreateSidebarLinks(localPost.Headers) 571 slug := strings.ReplaceAll(localPost.Slug, "\"", "") 572 e.GET("/page/"+slug+"/", func(c echo.Context) error { 573 var payload utils.Payload 574 payload.BlogTitle = config.Blog.Title 575 payload.BlogDesc = config.Blog.Desc 576 payload.BlogCopyright = config.Blog.Copyright 577 payload.Version = Version 578 payload.FeedRss = feedrss 579 payload.PinnedPosts = pinnedposts 580 payload.Links = links 581 payload.Title = strings.ReplaceAll(localPost.Title, "\"", "") 582 payload.Date = strings.ReplaceAll(localPost.Date.Format(time.RFC1123), "\"", "") 583 payload.Content = localPost.Content 584 payload.ExternalData = externalData 585 payload.PagesData = pagesData 586 payload.TextesData = textesData 587 payload.Headers = localPost.Headers 588 payload.Description = strings.ReplaceAll(localPost.Description, "\"", "") 589 payload.SidebarLinks = sidebarLinks 590 payload.CurrentSlug = strings.ReplaceAll(localPost.Slug, "\"", "") 591 payload.MetaDescription = strings.ReplaceAll(localPost.MetaDescription, "\"", "") 592 payload.MetaPropertyTitle = strings.ReplaceAll(localPost.MetaPropertyTitle, "\"", "") 593 payload.MetaPropertyDescription = strings.ReplaceAll(localPost.MetaPropertyDescription, "\"", "") 594 payload.MetaOgURL = strings.ReplaceAll(localPost.MetaOgURL, "\"", "") 595 596 return c.Render(http.StatusOK, "layout_page", payload) 597 }) 598 } else { 599 log.Printf("Warning: Post titled '%s' has an empty slug and will not be accessible via a unique URL.\n", localPost.Title) 600 } 601 } 602 603 for _, post := range postsData.Categories[0].Pages { 604 localPost := post 605 if localPost.Slug != "" { 606 sidebarLinks := utils.CreateSidebarLinks(localPost.Headers) 607 slug := strings.ReplaceAll(localPost.Slug, "\"", "") 608 e.GET("/"+slug+"/", func(c echo.Context) error { 609 var payload utils.Payload 610 payload.BlogTitle = config.Blog.Title 611 payload.BlogDesc = config.Blog.Desc 612 payload.BlogCopyright = config.Blog.Copyright 613 payload.Version = Version 614 payload.FeedRss = feedrss 615 payload.PinnedPosts = pinnedposts 616 payload.Links = links 617 payload.Title = strings.ReplaceAll(localPost.Title, "\"", "") 618 payload.Date = strings.ReplaceAll(localPost.Date.Format(time.RFC1123), "\"", "") 619 payload.Content = localPost.Content 620 payload.ExternalData = externalData 621 payload.PagesData = pagesData 622 payload.PostsData = postsData 623 payload.TextesData = textesData 624 payload.Headers = localPost.Headers 625 payload.Description = strings.ReplaceAll(localPost.Description, "\"", "") 626 payload.Tags = localPost.Tags 627 payload.SidebarLinks = sidebarLinks 628 payload.CurrentSlug = strings.ReplaceAll(localPost.Slug, "\"", "") 629 payload.MetaDescription = strings.ReplaceAll(localPost.MetaDescription, "\"", "") 630 payload.MetaPropertyTitle = strings.ReplaceAll(localPost.MetaPropertyTitle, "\"", "") 631 payload.MetaPropertyDescription = strings.ReplaceAll(localPost.MetaPropertyDescription, "\"", "") 632 payload.MetaOgURL = strings.ReplaceAll(localPost.MetaOgURL, "\"", "") 633 634 return c.Render(http.StatusOK, "layout", payload) 635 }) 636 } else { 637 log.Printf("Warning: Post titled '%s' has an empty slug and will not be accessible via a unique URL.\n", localPost.Title) 638 } 639 } 640 641 for _, texte := range textesData.Categories[0].Pages { 642 localPost := texte 643 if localPost.Slug != "" { 644 sidebarLinks := utils.CreateSidebarLinks(localPost.Headers) 645 slug := strings.ReplaceAll(localPost.Slug, "\"", "") 646 e.GET("/texte/"+slug+"/", func(c echo.Context) error { 647 var payload utils.Payload 648 payload.BlogTitle = config.Blog.Title 649 payload.BlogDesc = config.Blog.Desc 650 payload.BlogCopyright = config.Blog.Copyright 651 payload.Version = Version 652 payload.FeedRss = feedrss 653 payload.PinnedPosts = pinnedposts 654 payload.Links = links 655 payload.Title = strings.ReplaceAll(localPost.Title, "\"", "") 656 payload.Date = strings.ReplaceAll(localPost.Date.Format(time.RFC1123), "\"", "") 657 payload.Content = localPost.Content 658 payload.ExternalData = externalData 659 payload.PagesData = pagesData 660 payload.TextesData = textesData 661 payload.Headers = localPost.Headers 662 payload.Description = strings.ReplaceAll(localPost.Description, "\"", "") 663 payload.SidebarLinks = sidebarLinks 664 payload.CurrentSlug = strings.ReplaceAll(localPost.Slug, "\"", "") 665 payload.MetaDescription = strings.ReplaceAll(localPost.MetaDescription, "\"", "") 666 payload.MetaPropertyTitle = strings.ReplaceAll(localPost.MetaPropertyTitle, "\"", "") 667 payload.MetaPropertyDescription = strings.ReplaceAll(localPost.MetaPropertyDescription, "\"", "") 668 payload.MetaOgURL = strings.ReplaceAll(localPost.MetaOgURL, "\"", "") 669 670 return c.Render(http.StatusOK, "layout_texte", payload) 671 }) 672 } else { 673 log.Printf("Warning: Post titled '%s' has an empty slug and will not be accessible via a unique URL.\n", localPost.Title) 674 } 675 } 676 return nil 677 } 678 679 func main() { 680 flag.StringVar(&ConfigFile, "configFile", "/usr/share/gibson/gibson.toml", "path to config file") 681 showVersion := flag.Bool("version", false, "display the current version") 682 flag.Parse() 683 684 if *showVersion { 685 fmt.Println(Version) 686 os.Exit(0) 687 } 688 689 if _, err := toml.DecodeFile(ConfigFile, &config); err != nil { 690 fmt.Println(err) 691 return 692 } 693 694 // Launch Http server for S3 695 go httpServer() 696 697 e := echo.New() 698 //e.Debug = true 699 e.Use(middleware.Logger()) 700 e.Use(middleware.Recover()) 701 e.Use(ServerHeader) 702 703 e.Use(middleware.SecureWithConfig(middleware.SecureConfig{ 704 // ContentSecurityPolicy: "script-src 'unsafe-eval' 'unsafe-inline'", 705 // ContentSecurityPolicy: "script-src 'nonce-{RANDOM}' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' https: http:;", 706 XSSProtection: "1; mode=block", 707 ContentTypeNosniff: "nosniff", 708 XFrameOptions: "SAMEORIGIN", 709 HSTSMaxAge: 31536000, 710 HSTSExcludeSubdomains: false, 711 HSTSPreloadEnabled: true, 712 })) 713 714 feedrss = nil 715 rssChannel = make(chan []utils.FeedRss) 716 utils.GetRSS(rssChannel, config.RSS, &feedrss) 717 718 renderer := &Template{ 719 templates: template.Must(template.New("t").Funcs(template.FuncMap{ 720 "mkslice": mkslice, 721 "mul0": func(i int, o int) int { 722 return (i - 1) * o 723 }, 724 "mul1": func(i int, o int) int { 725 return i * o 726 }, 727 "inc": func(i int) int { 728 return i + 1 729 }, 730 "sub": func(i int) int { 731 o := i - 1 732 if o <= 0 { 733 o = 1 734 } 735 return o 736 }, 737 "sup": func(i int) bool { 738 if i > 4 { 739 return true 740 } else { 741 return false 742 } 743 }, 744 "dict": utils.Dict, 745 }).ParseGlob(config.Gibson.Directory + "templates/*.html")), 746 } 747 748 e.Renderer = renderer 749 e.Static("/static", config.Gibson.Directory+"static") 750 e.Static("/images", config.Gibson.Directory+"static/images") 751 e.Static("/.well-known", config.Gibson.Directory+"static/.well-known") 752 e.HTTPErrorHandler = customHTTPErrorHandler 753 754 e.GET("/sse", handleSSE) 755 e.GET("/reload", reloadMarkdown) 756 757 if len(config.S3.Endpoint) != 0 { 758 mc := utils.NewMediaController(config.S3) 759 e.GET("/s3/:id", mc.PlayFrontMediaFromHTTP) 760 } 761 e.GET("/index.xml", func(c echo.Context) error { 762 return c.File("/tmp/index.xml") 763 }) 764 765 if err := loadMarkdown(e); err != nil { 766 fmt.Println(err) 767 return 768 } 769 770 s := &http.Server{ 771 Addr: config.Gibson.IP + ":" + strconv.Itoa(config.Gibson.Port), 772 ReadTimeout: 60 * time.Minute, 773 WriteTimeout: 60 * time.Minute, 774 } 775 e.Logger.Fatal(e.StartServer(s)) 776 }