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,11 +78,16 @@ 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 )
84
+
69
85
return
70
86
}
71
- if err := postTemplate .ExecuteTemplate (w , "layout" , post ); err != nil {
87
+
88
+ err := postTemplate .ExecuteTemplate (w , "layout" , post )
89
+
90
+ if err != nil {
72
91
log .Println (err .Error ())
73
92
errorHandler (w , r , 500 )
74
93
}
@@ -77,9 +96,13 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
77
96
func errorHandler (w http.ResponseWriter , r * http.Request , status int ) {
78
97
log .Printf ("error %d %s %s\n " , status , r .RemoteAddr , r .URL .Path )
79
98
w .WriteHeader (status )
80
- if err := errorTemplate .ExecuteTemplate (w , "layout" , map [string ]interface {}{"Error" : http .StatusText (status ), "Status" : status }); err != nil {
99
+
100
+ err := errorTemplate .ExecuteTemplate (w , "layout" , map [string ]interface {}{"Error" : http .StatusText (status ), "Status" : status })
101
+
102
+ if err != nil {
81
103
log .Println (err .Error ())
82
104
http .Error (w , http .StatusText (500 ), 500 )
105
+
83
106
return
84
107
}
85
108
}
@@ -92,8 +115,84 @@ func noDirListing(h http.Handler) http.HandlerFunc {
92
115
return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
93
116
if strings .HasSuffix (r .URL .Path , "/" ) || r .URL .Path == "" {
94
117
http .NotFound (w , r )
118
+
95
119
return
96
120
}
121
+
97
122
h .ServeHTTP (w , r )
98
123
})
99
124
}
125
+
126
+ func readConfig (ConfigName string ) (conf * Config , err error ) {
127
+ var file []byte
128
+
129
+ file , err = ioutil .ReadFile (ConfigName )
130
+
131
+ if err != nil {
132
+ return nil , err
133
+ }
134
+
135
+ err = yaml .Unmarshal (file , conf )
136
+
137
+ if err != nil {
138
+ return nil , err
139
+ }
140
+
141
+ return conf , nil
142
+ }
143
+
144
+ type post struct {
145
+ Title string
146
+ Body template.HTML
147
+ ModTime int64
148
+ }
149
+
150
+ type postArray struct {
151
+ Items map [string ]post
152
+ sync.RWMutex
153
+ }
154
+
155
+ func newPostArray () * postArray {
156
+ p := postArray {}
157
+ p .Items = make (map [string ]post )
158
+
159
+ return & p
160
+ }
161
+
162
+ // Get Загружает markdown-файл и конвертирует его в HTML
163
+ // Возвращает объект типа Post
164
+ // Если путь не существует или является каталогом, то возвращаем ошибку
165
+ func (p * postArray ) Get (md string ) (post , int , error ) {
166
+ info , err := os .Stat (md )
167
+
168
+ if err != nil {
169
+ if os .IsNotExist (err ) {
170
+ // файл не существует
171
+ return post {}, 404 , err
172
+ }
173
+
174
+ return post {}, 500 , err
175
+ }
176
+
177
+ if info .IsDir () {
178
+ // не файл, а папка
179
+ return post {}, 404 , fmt .Errorf ("dir" )
180
+ }
181
+
182
+ val , ok := p .Items [md ]
183
+
184
+ if ! ok || (ok && val .ModTime != info .ModTime ().UnixNano ()) {
185
+ p .RLock ()
186
+ defer p .RUnlock ()
187
+ fileread , _ := ioutil .ReadFile (md )
188
+ lines := strings .Split (string (fileread ), "\n " )
189
+ title := string (lines [0 ])
190
+ body := strings .Join (lines [1 :], "\n " )
191
+ body = string (blackfriday .MarkdownCommon ([]byte (body )))
192
+ p .Items [md ] = post {title , template .HTML (body ), info .ModTime ().UnixNano ()}
193
+ }
194
+
195
+ post := p .Items [md ]
196
+
197
+ return post , 200 , nil
198
+ }
0 commit comments