1
1
package main
2
2
3
3
import (
4
+ "fmt"
4
5
"html/template"
6
+ "io/ioutil"
5
7
"log"
6
8
"net/http"
9
+ "os"
7
10
"path"
8
11
"strings"
12
+ "sync"
9
13
10
14
"github.com/bmizerany/pat"
15
+ "github.com/russross/blackfriday"
16
+ "gopkg.in/yaml.v2"
11
17
)
12
18
13
19
const (
14
20
configFileName = "mdserver.yaml"
15
21
)
16
22
23
+ // Config - структура для считывания конфигурационного файла
24
+ type Config struct {
25
+ Listen string `yaml:"listen"`
26
+ }
27
+
17
28
var (
18
29
// компилируем шаблоны, если не удалось, то выходим
19
30
postTemplate = template .Must (template .ParseFiles (path .Join ("templates" , "layout.html" ), path .Join ("templates" , "post.html" )))
23
34
24
35
func main () {
25
36
cfg , err := readConfig (configFileName )
37
+
26
38
if err != nil {
27
39
log .Fatalln (err )
28
40
}
41
+
29
42
// для отдачи сервером статичных файлов из папки public/static
30
43
fs := noDirListing (http .FileServer (http .Dir ("./public/static" )))
31
44
http .Handle ("/static/" , http .StripPrefix ("/static/" , fs ))
@@ -54,6 +67,7 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
54
67
p := path .Join ("posts" , page )
55
68
56
69
var postMD string
70
+
57
71
if page != "" {
58
72
// если page не пусто, то считаем, что запрашивается файл
59
73
// получим posts/p1.md
@@ -64,10 +78,12 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
64
78
}
65
79
66
80
post , status , err := posts .Get (postMD )
81
+
67
82
if err != nil {
68
83
errorHandler (w , r , status )
69
84
return
70
85
}
86
+
71
87
if err := postTemplate .ExecuteTemplate (w , "layout" , post ); err != nil {
72
88
log .Println (err .Error ())
73
89
errorHandler (w , r , 500 )
@@ -97,3 +113,69 @@ func noDirListing(h http.Handler) http.HandlerFunc {
97
113
h .ServeHTTP (w , r )
98
114
})
99
115
}
116
+
117
+ func readConfig (ConfigName string ) (conf * Config , err error ) {
118
+ var file []byte
119
+
120
+ file , err = ioutil .ReadFile (ConfigName )
121
+
122
+ if err != nil {
123
+ return nil , err
124
+ }
125
+
126
+ err = yaml .Unmarshal (file , conf )
127
+
128
+ if err != nil {
129
+ return nil , err
130
+ }
131
+
132
+ return conf , nil
133
+ }
134
+
135
+ type post struct {
136
+ Title string
137
+ Body template.HTML
138
+ ModTime int64
139
+ }
140
+
141
+ type postArray struct {
142
+ Items map [string ]post
143
+ sync.RWMutex
144
+ }
145
+
146
+ func newPostArray () * postArray {
147
+ p := postArray {}
148
+ p .Items = make (map [string ]post )
149
+ return & p
150
+ }
151
+
152
+ // Get Загружает markdown-файл и конвертирует его в HTML
153
+ // Возвращает объект типа Post
154
+ // Если путь не существует или является каталогом, то возвращаем ошибку
155
+ func (p * postArray ) Get (md string ) (post , int , error ) {
156
+ info , err := os .Stat (md )
157
+ if err != nil {
158
+ if os .IsNotExist (err ) {
159
+ // файл не существует
160
+ return post {}, 404 , err
161
+ }
162
+ return post {}, 500 , err
163
+ }
164
+ if info .IsDir () {
165
+ // не файл, а папка
166
+ return post {}, 404 , fmt .Errorf ("dir" )
167
+ }
168
+ val , ok := p .Items [md ]
169
+ if ! ok || (ok && val .ModTime != info .ModTime ().UnixNano ()) {
170
+ p .RLock ()
171
+ defer p .RUnlock ()
172
+ fileread , _ := ioutil .ReadFile (md )
173
+ lines := strings .Split (string (fileread ), "\n " )
174
+ title := string (lines [0 ])
175
+ body := strings .Join (lines [1 :], "\n " )
176
+ body = string (blackfriday .MarkdownCommon ([]byte (body )))
177
+ p .Items [md ] = post {title , template .HTML (body ), info .ModTime ().UnixNano ()}
178
+ }
179
+ post := p .Items [md ]
180
+ return post , 200 , nil
181
+ }
0 commit comments