Запись не найдена
6 |
├── .gitignore
├── .travis.yml
├── BASE.sql
├── README.md
├── blog.go
├── config-sample.json
├── kayden.go
├── models
└── Config.go
├── public
├── css
│ └── style.css
├── favicon.ico
└── img
│ └── 404.jpg
├── uploads
└── 01.jpg
├── utils.go
└── views
├── 404.html
├── all.html
├── draft_edit.html
├── draft_new.html
├── drafts.html
├── edit.html
├── footer.html
├── header.html
├── index.html
├── login.html
├── menu.html
├── new.html
├── posts.html
├── rss.html
├── rss_header.html
└── upload.html
/.gitignore:
--------------------------------------------------------------------------------
1 | TODO
2 | config.json
3 | .htaccess
4 | install.go
5 | nohup.out
6 | liamka.me
7 | pkg
8 | src
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - 1.1
4 | - 1.2
5 | - 1.3
6 | - release
7 | - tip
8 |
9 | install:
10 | - go get github.com/go-sql-driver/mysql
11 |
12 | script:
13 | - go build -v ./...
--------------------------------------------------------------------------------
/BASE.sql:
--------------------------------------------------------------------------------
1 | # ************************************************************
2 | # SQL dump for Kayden
3 | # ************************************************************
4 |
5 | CREATE TABLE `kayden_blog_posts` (
6 | `id` int(11) NOT NULL AUTO_INCREMENT,
7 | `title` varchar(1000) NOT NULL,
8 | `body` varchar(5000) NOT NULL,
9 | `tags` varchar(1000) NOT NULL DEFAULT '',
10 | `time` int(100) NOT NULL,
11 | PRIMARY KEY (`id`)
12 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
13 |
14 | INSERT INTO `kayden_blog_posts` (`id`, `title`, `body`, `tags`, `time`)
15 | VALUES (1,'Hello golang!','This is test message','hello, world, ',1397480139);
16 |
17 |
18 |
19 | CREATE TABLE `kayden_blog_drafts` (
20 | `id` int(11) NOT NULL AUTO_INCREMENT,
21 | `title` varchar(1000) NOT NULL,
22 | `body` varchar(5000) NOT NULL,
23 | `tags` varchar(1000) NOT NULL DEFAULT '',
24 | `time` int(100) NOT NULL,
25 | PRIMARY KEY (`id`)
26 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
27 |
28 | INSERT INTO `kayden_blog_drafts` (`id`, `title`, `body`, `tags`, `time`)
29 | VALUES (1,'Test draft','Example draft','draft, tags, ',1397480139);
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Keywords: Golang, go, Blog, Engine, MySQL
2 |
3 |
4 |
5 | ### Kayden - blog engine on go(golang)
6 |
7 | Simple blog engine on go. Nothing else.
8 |
9 | [Demo here](https://liamka.me)
10 |
11 | ### Features
12 | - Fully customizable
13 | - Tags (search by tag)
14 | - Markdown
15 | - Drafts
16 | - RSS
17 | - Simple ;)
18 |
19 | ### Install
20 |
21 | 1) Rename config-sample.json to config.json and change params
22 |
23 | 2) Upload base BASE.sql
24 |
25 | 3) Install dependencies:
26 |
27 | go get github.com/go-sql-driver/mysql
28 |
29 | go get github.com/russross/blackfriday
30 |
31 | go get github.com/liamka/Superior
32 |
33 | 3) Build it and run!
34 |
35 | 4) Go to http://your-website/kayden and login
36 |
37 | ### License
38 |
39 | Kayden(blog engine) is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
40 |
41 | Author: [](https://liamka.me) Kirill Kotikov
42 |
43 | 
44 |
--------------------------------------------------------------------------------
/blog.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 | "text/template"
6 | _ "github.com/go-sql-driver/mysql"
7 | "github.com/liamka/Superior"
8 | "database/sql"
9 | "./models"
10 | )
11 |
12 | var (
13 | db *sql.DB
14 | config models.Config
15 | v models.Vars
16 | t = template.Must(template.ParseGlob("views/*"))
17 | )
18 |
19 | type post struct {
20 | Id int
21 | Title string
22 | Body string
23 | Tags string
24 | Time string
25 | }
26 |
27 | ////////////////////////////////
28 | // Index post
29 | ////////////////////////////////
30 | func index(w http.ResponseWriter, r *http.Request) {
31 | // Query
32 | rows, _ := db.Query("SELECT * FROM kayden_blog_posts ORDER BY id DESC LIMIT 10")
33 | defer rows.Close()
34 | posts := []post{}
35 | for rows.Next() {
36 | p := post{}
37 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
38 | v.Title = config.Title
39 | p.Body = ConvertMarkdownToHtml(p.Body)
40 | p.Time = timeX(p.Time)
41 | p.Tags = tagsX(p.Tags,"true")
42 | posts = append(posts, p)
43 | }
44 | t.ExecuteTemplate(w, "header", v)
45 | t.ExecuteTemplate(w, "index", posts)
46 | }
47 |
48 | ////////////////////////////////
49 | // Single post
50 | ////////////////////////////////
51 | func single(w http.ResponseWriter, r *http.Request) {
52 | // Get single
53 | id := r.URL.Path[len("/note/"):]
54 | stmt, _ := db.Prepare("SELECT * FROM kayden_blog_posts where id = ? LIMIT 1")
55 | rows, _ := stmt.Query(id)
56 | defer rows.Close()
57 | posts := []post{}
58 | for rows.Next() {
59 | p := post{}
60 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
61 | v.Title = config.Title + ": " + p.Title
62 | p.Body = ConvertMarkdownToHtml(p.Body)
63 | p.Time = timeX(p.Time)
64 | p.Tags = tagsX(p.Tags,"true")
65 | posts = append(posts, p)
66 | }
67 | // 404
68 | if len(posts) == 0 {
69 | http.Redirect(w, r, "/404", 302)
70 | }
71 | t.ExecuteTemplate(w, "header", v)
72 | t.ExecuteTemplate(w, "index", posts)
73 | }
74 | ////////////////////////////////
75 | // All posts
76 | ////////////////////////////////
77 | func allPosts(w http.ResponseWriter, r *http.Request) {
78 | // Query
79 | rows, _ := db.Query("SELECT * FROM kayden_blog_posts ORDER BY id DESC LIMIT 100000")
80 | defer rows.Close()
81 | posts := []post{}
82 | for rows.Next() {
83 | p := post{}
84 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
85 | p.Body = ConvertMarkdownToHtml(p.Body)
86 | p.Time = timeX(p.Time)
87 | p.Tags = tagsX(p.Tags,"false")
88 | posts = append(posts, p)
89 | }
90 | t.ExecuteTemplate(w, "header", v)
91 | t.ExecuteTemplate(w, "all", posts)
92 | }
93 | ////////////////////////////////
94 | // Tags
95 | ////////////////////////////////
96 | func tagPosts(w http.ResponseWriter, r *http.Request) {
97 | // Get tag
98 | tag := r.URL.Path[len("/tag/"):]
99 | stmt, _ := db.Prepare("SELECT * FROM kayden_blog_posts where tags LIKE ? ORDER BY id DESC LIMIT 1000000")
100 | rows, _ := stmt.Query("%"+tag+"%")
101 | defer rows.Close()
102 | posts := []post{}
103 | for rows.Next() {
104 | p := post{}
105 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
106 | p.Body = ConvertMarkdownToHtml(p.Body)
107 | p.Time = timeX(p.Time)
108 | p.Tags = tagsX(p.Tags,"true")
109 | posts = append(posts, p)
110 | }
111 | t.ExecuteTemplate(w, "header", v)
112 | t.ExecuteTemplate(w, "index", posts)
113 | }
114 |
115 | ////////////////////////////////
116 | // RSS
117 | ////////////////////////////////
118 | func rssPosts(w http.ResponseWriter, r *http.Request) {
119 | w.Header().Set("Content-Type", "application/xml")
120 | // Query
121 | rows, _ := db.Query("SELECT * FROM kayden_blog_posts ORDER BY id DESC LIMIT 10")
122 | defer rows.Close()
123 |
124 | type rssPost struct {
125 | Id int
126 | Title string
127 | Body string
128 | Tags string
129 | Time string
130 | URI string
131 | }
132 |
133 | posts := []rssPost{}
134 | k := 0
135 | var lastUpdate string
136 | for rows.Next() {
137 | p := rssPost{}
138 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
139 | if k == 0 {
140 | lastUpdate = timeRFC(p.Time)
141 | }
142 | p.Body = ConvertMarkdownToHtml(p.Body)
143 | p.Time = timeRFC(p.Time)
144 | p.Tags = tagsX(p.Tags,"false")
145 | p.URI = config.URI
146 | posts = append(posts, p)
147 | k++
148 | }
149 |
150 | type Rss struct {
151 | Title string
152 | Subtitle string
153 | URI string
154 | Description string
155 | LastBuild string
156 | }
157 |
158 | rss := Rss{
159 | Title: config.Title,
160 | URI: config.URI,
161 | Description: config.Description,
162 | LastBuild: lastUpdate,
163 | }
164 |
165 | t.ExecuteTemplate(w, "rss_header", rss)
166 | t.ExecuteTemplate(w, "rss", posts)
167 | }
168 |
169 | ////////////////////////////////
170 | // 404
171 | ////////////////////////////////
172 | func notfound(w http.ResponseWriter, r *http.Request) {
173 | t.ExecuteTemplate(w, "header", v)
174 | t.ExecuteTemplate(w, "404", nil)
175 | }
176 |
177 | ////////////////////////////////
178 | // MAIN
179 | ////////////////////////////////
180 | func main() {
181 | // Sample
182 | Superior.Print("Kayden love it!", "normal", "green")
183 | // Conf
184 | config = models.Conf()
185 | v = models.Values(config)
186 | // Open mysql
187 | db, _ = sql.Open("mysql", config.Mysql)
188 | // Invoke folders
189 | http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("public"))))
190 | http.Handle("/uploads/", http.StripPrefix("/uploads/", http.FileServer(http.Dir("uploads"))))
191 | // Routes
192 | http.HandleFunc("/", index)
193 | http.HandleFunc("/note/", single)
194 | http.HandleFunc("/all/", allPosts)
195 | http.HandleFunc("/tag/", tagPosts)
196 | http.HandleFunc("/rss/", rssPosts)
197 | http.HandleFunc("/404/", notfound)
198 | http.HandleFunc("/kayden/login", login)
199 | http.HandleFunc("/kayden/", kayden)
200 | http.HandleFunc("/kayden/new/", newPost)
201 | http.HandleFunc("/kayden/new/save", savePost)
202 | http.HandleFunc("/kayden/edit/", editPost)
203 | http.HandleFunc("/kayden/edit/save", updatePost)
204 | http.HandleFunc("/kayden/delete/", deletePost)
205 | http.HandleFunc("/kayden/upload", uploads)
206 | http.HandleFunc("/kayden/upload/delete/", uploadsDelete)
207 | http.HandleFunc("/kayden/drafts/", drafts)
208 | http.HandleFunc("/kayden/drafts/new/", newDraft)
209 | http.HandleFunc("/kayden/drafts/new/save", saveDraft)
210 | http.HandleFunc("/kayden/drafts/edit/", editDraft)
211 | http.HandleFunc("/kayden/drafts/edit/save", updateDraft)
212 | http.HandleFunc("/kayden/drafts/delete/", deleteDraft)
213 | http.HandleFunc("/kayden/drafts/publish/", publishDraft)
214 |
215 | // Get port
216 | http.ListenAndServe(":3000", nil)
217 | }
--------------------------------------------------------------------------------
/config-sample.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Blog title",
3 | "titleheader": "Title for header",
4 | "subtitle": "Your subtitle",
5 | "uri": "http://your-blog-url.com",
6 | "description": "Description",
7 | "keywords": "Keywords",
8 | "mysql": "user:pass@/base?charset=utf8",
9 | "cookieName": "kayden",
10 | "pass": "pass",
11 | "Social": [
12 | {"url": "URL", "title": "TITLE #1"},
13 | {"url": "URL", "title": "TITLE #2"}
14 | ]
15 | }
--------------------------------------------------------------------------------
/kayden.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 | "fmt"
6 | "time"
7 | "io"
8 | "io/ioutil"
9 | "os"
10 | )
11 |
12 |
13 | ////////////////////////////////
14 | // Login page
15 | ////////////////////////////////
16 | func login(w http.ResponseWriter, r *http.Request) {
17 | if r.Method == "GET" {
18 | t.ExecuteTemplate(w, "header", v)
19 | t.ExecuteTemplate(w, "login", nil)
20 | } else {
21 | r.ParseForm()
22 | if r.FormValue("pass") == config.Pass {
23 | expiration := time.Now().Add(365 * 24 * time.Hour)
24 | cookie := http.Cookie{Name: config.CookieName, Value: config.Pass, Expires: expiration}
25 | http.SetCookie(w, &cookie)
26 | http.Redirect(w, r, "/kayden", 302)
27 | } else {
28 | http.Redirect(w, r, "/kayden/login", 302)
29 | }
30 | }
31 | }
32 |
33 | ////////////////////////////////
34 | // Admin all posts
35 | ////////////////////////////////
36 | func kayden(w http.ResponseWriter, r *http.Request) {
37 | // Access
38 | access := CheckCookies(r)
39 | if !access {
40 | http.Redirect(w, r, "/kayden/login", 302)
41 | }
42 |
43 | // Query
44 | rows, _ := db.Query("SELECT * FROM kayden_blog_posts ORDER BY id DESC LIMIT 100000")
45 | defer rows.Close()
46 | posts := []post{}
47 | for rows.Next() {
48 | p := post{}
49 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
50 |
51 | posts = append(posts, p)
52 | }
53 | t.ExecuteTemplate(w, "header", v)
54 | t.ExecuteTemplate(w, "posts", posts)
55 | }
56 |
57 | ////////////////////////////////
58 | // New post
59 | ////////////////////////////////
60 | func newPost(w http.ResponseWriter, r *http.Request) {
61 | // Access
62 | access := CheckCookies(r)
63 | if !access {
64 | http.Redirect(w, r, "/kayden/login", 302)
65 | }
66 |
67 | t.ExecuteTemplate(w, "header", v)
68 | t.ExecuteTemplate(w, "new", nil)
69 | }
70 |
71 | ////////////////////////////////
72 | // Save post
73 | ////////////////////////////////
74 | func savePost(w http.ResponseWriter, r *http.Request) {
75 | // Access
76 | access := CheckCookies(r)
77 | if !access {
78 | http.Redirect(w, r, "/kayden/login", 302)
79 | }
80 |
81 | // Catch data
82 | title := r.FormValue("title")
83 | body := r.FormValue("body")
84 | tags := r.FormValue("tags")
85 |
86 | now := time.Now()
87 | time := now.Unix()
88 |
89 | // Prepare and insert
90 | stmt, _ := db.Prepare("INSERT kayden_blog_posts SET title=?,body=?,tags=?,time=?")
91 | stmt.Exec(title, body, tags, time)
92 | // Redirect
93 | http.Redirect(w, r, "/", 302)
94 | }
95 |
96 | ////////////////////////////////
97 | // Edit post
98 | ////////////////////////////////
99 | func editPost(w http.ResponseWriter, r *http.Request) {
100 | // Access
101 | access := CheckCookies(r)
102 | if !access {
103 | http.Redirect(w, r, "/kayden/login", 302)
104 | }
105 |
106 | // Get single
107 | id := r.URL.Path[len("/kayden/edit/"):]
108 | stmt, _ := db.Prepare("SELECT * FROM kayden_blog_posts where id = ? LIMIT 1")
109 | rows, _ := stmt.Query(id)
110 | defer rows.Close()
111 | posts := []post{}
112 | for rows.Next() {
113 | p := post{}
114 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
115 | p.Time = timeX(p.Time)
116 | posts = append(posts, p)
117 | }
118 | t.ExecuteTemplate(w, "header", v)
119 | t.ExecuteTemplate(w, "edit", posts)
120 | }
121 |
122 | ////////////////////////////////
123 | // Update post
124 | ////////////////////////////////
125 | func updatePost(w http.ResponseWriter, r *http.Request) {
126 | // Access
127 | access := CheckCookies(r)
128 | if !access {
129 | http.Redirect(w, r, "/kayden/login", 302)
130 | }
131 |
132 | // Catch data
133 | id := r.FormValue("id")
134 | title := r.FormValue("title")
135 | body := r.FormValue("body")
136 | tags := r.FormValue("tags")
137 |
138 | // Prepare and insert
139 | stmt, _ := db.Prepare("update kayden_blog_posts SET title=?,body=?,tags=? where id=?")
140 | stmt.Exec(title, body, tags, id)
141 | // Redirect
142 | http.Redirect(w, r, "/kayden", 302)
143 | }
144 |
145 | ////////////////////////////////
146 | // Delete post
147 | ////////////////////////////////
148 | func deletePost(w http.ResponseWriter, r *http.Request) {
149 | // Access
150 | access := CheckCookies(r)
151 | if !access {
152 | http.Redirect(w, r, "/kayden/login", 302)
153 | }
154 |
155 | // Get single
156 | id := r.URL.Path[len("/kayden/delete/"):]
157 | stmt, _ := db.Prepare("DELETE from kayden_blog_posts where id=?")
158 | stmt.Exec(id)
159 | http.Redirect(w, r, "/kayden", 302)
160 | }
161 |
162 | ////////////////////////////////
163 | // Upload files
164 | ////////////////////////////////
165 | func uploads(w http.ResponseWriter, r *http.Request) {
166 | type file struct {
167 | Name string
168 | }
169 |
170 | if r.Method == "GET" {
171 | filez, _ := ioutil.ReadDir("./uploads/")
172 | files := []file{}
173 | for _, fn := range filez {
174 | f := file{}
175 | f.Name = fn.Name()
176 | files = append(files, f)
177 | }
178 | t.ExecuteTemplate(w, "header", v)
179 | t.ExecuteTemplate(w, "upload", files)
180 | } else {
181 | r.ParseMultipartForm(32 << 20)
182 | file, handler, err := r.FormFile("uploadfile")
183 | if err != nil {
184 | fmt.Println(err)
185 | return
186 | }
187 | defer file.Close()
188 | f, err := os.OpenFile("./uploads/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
189 | if err != nil {
190 | fmt.Println(err)
191 | return
192 | }
193 | defer f.Close()
194 | io.Copy(f, file)
195 | http.Redirect(w, r, "/kayden/upload", 302)
196 | }
197 | }
198 |
199 | ////////////////////////////////
200 | // Delete uploaded file
201 | ////////////////////////////////
202 | func uploadsDelete(w http.ResponseWriter, r *http.Request) {
203 | // Access
204 | access := CheckCookies(r)
205 | if !access {
206 | http.Redirect(w, r, "/kayden/login", 302)
207 | }
208 |
209 | // Get file name
210 | file := r.URL.Path[len("/upload/upload/delete/"):]
211 |
212 | err := os.Remove("./uploads/"+file)
213 | if err != nil {
214 | fmt.Println(err)
215 | return
216 | }
217 |
218 | http.Redirect(w, r, "/kayden/upload", 302)
219 | }
220 |
221 | ////////////////////////////////
222 | // All drafts
223 | ////////////////////////////////
224 | func drafts(w http.ResponseWriter, r *http.Request) {
225 | // Access
226 | access := CheckCookies(r)
227 | if !access {
228 | http.Redirect(w, r, "/kayden/login", 302)
229 | }
230 |
231 | // Query
232 | rows, _ := db.Query("SELECT * FROM kayden_blog_drafts ORDER BY id DESC LIMIT 100000")
233 | defer rows.Close()
234 | posts := []post{}
235 | for rows.Next() {
236 | p := post{}
237 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
238 |
239 | posts = append(posts, p)
240 | }
241 | t.ExecuteTemplate(w, "header", v)
242 | t.ExecuteTemplate(w, "drafts", posts)
243 | }
244 |
245 | ////////////////////////////////
246 | // New draft
247 | ////////////////////////////////
248 | func newDraft(w http.ResponseWriter, r *http.Request) {
249 | // Access
250 | access := CheckCookies(r)
251 | if !access {
252 | http.Redirect(w, r, "/kayden/login", 302)
253 | }
254 |
255 | t.ExecuteTemplate(w, "header", v)
256 | t.ExecuteTemplate(w, "draft_new", nil)
257 | }
258 |
259 | ////////////////////////////////
260 | // Save draft
261 | ////////////////////////////////
262 | func saveDraft(w http.ResponseWriter, r *http.Request) {
263 | // Access
264 | access := CheckCookies(r)
265 | if !access {
266 | http.Redirect(w, r, "/kayden/login", 302)
267 | }
268 |
269 | // Catch data
270 | title := r.FormValue("title")
271 | body := r.FormValue("body")
272 | tags := r.FormValue("tags")
273 |
274 | now := time.Now()
275 | time := now.Unix()
276 |
277 | // Prepare and insert
278 | stmt, _ := db.Prepare("INSERT kayden_blog_drafts SET title=?,body=?,tags=?,time=?")
279 | stmt.Exec(title, body, tags, time)
280 | // Redirect
281 | http.Redirect(w, r, "/kayden/drafts", 302)
282 | }
283 |
284 | ////////////////////////////////
285 | // Edit draft
286 | ////////////////////////////////
287 | func editDraft(w http.ResponseWriter, r *http.Request) {
288 | // Access
289 | access := CheckCookies(r)
290 | if !access {
291 | http.Redirect(w, r, "/kayden/login", 302)
292 | }
293 |
294 | // Get single
295 | id := r.URL.Path[len("/kayden/drafts/edit/"):]
296 | stmt, _ := db.Prepare("SELECT * FROM kayden_blog_drafts where id = ? LIMIT 1")
297 | rows, _ := stmt.Query(id)
298 | defer rows.Close()
299 | posts := []post{}
300 | for rows.Next() {
301 | p := post{}
302 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
303 | p.Time = timeX(p.Time)
304 | posts = append(posts, p)
305 | }
306 | t.ExecuteTemplate(w, "header", v)
307 | t.ExecuteTemplate(w, "draft_edit", posts)
308 | }
309 |
310 | ////////////////////////////////
311 | // Update draft
312 | ////////////////////////////////
313 | func updateDraft(w http.ResponseWriter, r *http.Request) {
314 | // Access
315 | access := CheckCookies(r)
316 | if !access {
317 | http.Redirect(w, r, "/kayden/login", 302)
318 | }
319 |
320 | // Catch data
321 | id := r.FormValue("id")
322 | title := r.FormValue("title")
323 | body := r.FormValue("body")
324 | tags := r.FormValue("tags")
325 |
326 | // Prepare and insert
327 | stmt, _ := db.Prepare("update kayden_blog_drafts SET title=?,body=?,tags=? where id=?")
328 | stmt.Exec(title, body, tags, id)
329 | // Redirect
330 | http.Redirect(w, r, "/kayden/drafts", 302)
331 | }
332 |
333 | ////////////////////////////////
334 | // Delete draft
335 | ////////////////////////////////
336 | func deleteDraft(w http.ResponseWriter, r *http.Request) {
337 | // Access
338 | access := CheckCookies(r)
339 | if !access {
340 | http.Redirect(w, r, "/kayden/login", 302)
341 | }
342 |
343 | // Get single
344 | id := r.URL.Path[len("/kayden/drafts/delete/"):]
345 | stmt, _ := db.Prepare("DELETE from kayden_blog_drafts where id=?")
346 | stmt.Exec(id)
347 | http.Redirect(w, r, "/kayden/drafts", 302)
348 | }
349 |
350 | ////////////////////////////////
351 | // Publish draft
352 | ////////////////////////////////
353 | func publishDraft(w http.ResponseWriter, r *http.Request) {
354 | // Access
355 | access := CheckCookies(r)
356 | if !access {
357 | http.Redirect(w, r, "/kayden/login", 302)
358 | }
359 |
360 | id := r.URL.Path[len("/kayden/drafts/publish/"):]
361 |
362 | stmt, _ := db.Prepare("SELECT * FROM kayden_blog_drafts where id = ? LIMIT 1")
363 | rows, _ := stmt.Query(id)
364 | defer rows.Close()
365 | posts := []post{}
366 | for rows.Next() {
367 | p := post{}
368 | rows.Scan(&p.Id, &p.Title, &p.Body, &p.Tags, &p.Time)
369 | p.Time = timeX(p.Time)
370 | posts = append(posts, p)
371 | }
372 |
373 | stmta, _ := db.Prepare("INSERT kayden_blog_posts SET title=?,body=?,tags=?,time=?")
374 | stmta.Exec(posts[0].Title, posts[0].Body, posts[0].Tags, posts[0].Time)
375 |
376 | stmtd, _ := db.Prepare("DELETE from kayden_blog_drafts where id=?")
377 | stmtd.Exec(id)
378 |
379 | http.Redirect(w, r, "/kayden", 302)
380 | }
--------------------------------------------------------------------------------
/models/Config.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | )
7 |
8 | type Config struct {
9 | Title string `json:"title"`
10 | Subtitle string `json:"subtitle"`
11 | TitleHeader string `json:"titleheader"`
12 | URI string `json:"uri"`
13 | Description string `json:"description"`
14 | Keywords string `json:"keywords"`
15 | Mysql string `json:"mysql"`
16 | CookieName string `json:"cookieName"`
17 | Pass string `json:"pass"`
18 | Social []struct {
19 | Url string `json:"url"`
20 | Title string `json:"title"`
21 | } `json:"social"`
22 | }
23 |
24 | type Vars struct {
25 | Title string
26 | TitleHeader string
27 | Subtitle string
28 | URI string
29 | Description string
30 | Keywords string
31 | Social []*Soc
32 | }
33 |
34 | type Soc struct {
35 | Url string
36 | Title string
37 | }
38 |
39 | var SocData struct{
40 | Social []*Soc `json:"Social"`
41 | }
42 |
43 | func Conf() Config {
44 | var с Config
45 | configFile, _ := ioutil.ReadFile("config.json")
46 | json.Unmarshal([]byte(configFile), &с)
47 | return с
48 | }
49 |
50 | func Values(vars Config) Vars {
51 | varz, _ := json.Marshal(vars)
52 | json.Unmarshal(varz, &SocData)
53 | p := Vars{
54 | Title: vars.Title,
55 | TitleHeader: vars.TitleHeader,
56 | Subtitle: vars.Subtitle,
57 | URI: vars.URI,
58 | Description: vars.Description,
59 | Keywords: vars.Keywords,
60 | Social: []*Soc{},
61 | }
62 | p.Social = append(p.Social, SocData.Social...)
63 | return p
64 | }
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | /************ General */
2 | body {background: #fff;max-width: 960px;padding: 40px;margin: 20px auto;}
3 | a {color: #1c5c76;text-decoration: underline;}
4 |
5 | /************ Header */
6 | .header {text-align: center;padding: 0 0 10px 0;}
7 | .header h1 {margin: 0;font-size: 30px;text-shadow: -4px 4px rgba(0, 0, 0, 0.16);}
8 | .header h1 a {text-decoration: none;color: #000;font-family: Tahoma;font-weight: 700;}
9 | .header p {font-family: Georgia;}
10 | .header p.social a {margin: 4px;}
11 |
12 | /************ Content */
13 | .content {font-family: Georgia;}
14 | .content article {margin: 0 0 80px 0;display: block;}
15 | .content article h1 a {width: 100%;font-family: Georgia;color: #000;font-size: 30px;text-decoration: none;font-weight: 100;}
16 | .content article h1 a:hover {text-decoration: underline;}
17 | .content article section {width: 100%;line-height: 1.5em;color: #3E4349;font-size: 17px;}
18 | .content article section img.big {text-align: center;margin: 0% -16% auto;display: block;width: 132%;}
19 | .content article mark {display: inline-table;padding: 3px 5px;margin: 8px 0 0 0;color: #aa9c84;background:#fff;line-height: 28px;}
20 | .content article mark a {background: #e2e9f8;color: #1c5c76;border: 1px solid transparent;border-radius: 12px;padding: 0px 6px;text-decoration: none;border-bottom: 1px dotted #999;}
21 | .content a.tag {margin: 5px;}
22 |
23 | /************ Custom */
24 | .social a:hover {text-decoration: underline;}
25 | pre {background: #eee;padding: 7px 30px;margin: 15px -30px;line-height: 1.3em;overflow: auto;}
26 |
27 | @media only screen and (min-width: 100px) and (max-width: 800px) {
28 | body {padding: 10px;margin: 20px;}
29 | }
30 |
31 | /************ Kayden */
32 | .kayden {font-size: 14px;}
33 | .kayden .menu a {font-size: 22px;margin-right: 14px;}
34 | .kayden .posts h2 {border: 1px dashed #DADADA;padding: 8px;}
35 | .kayden .posts h2 a {font-family: Georgia;color: #000;text-decoration: none;font-weight: 100;}
36 | .kayden .posts h2 a.options {float: right;font-style: italic;color: brown;}
37 | .kayden form input, .kayden form textarea {border: 2px solid;border-radius: 5px;margin: 10px 0;width: 98%;padding: 10px 7px;font-family: Georgia;font-size: 20px;color: #5A5A5A;}
38 | .kayden form input:focus, .kayden form textarea:focus {outline: none;}
39 | .kayden form input.new {cursor:pointer;background: #00A1E8;color: #fff;width: 30%;}
40 | .kayden form input.update {cursor:pointer;background: #6EA709;color: #fff;width: 30%;}
41 | .kayden .uploads img {}
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liamka/Kayden-blog-engine/254d53a0343b2eb5fb9c7d82d2e7d5b99c59397f/public/favicon.ico
--------------------------------------------------------------------------------
/public/img/404.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liamka/Kayden-blog-engine/254d53a0343b2eb5fb9c7d82d2e7d5b99c59397f/public/img/404.jpg
--------------------------------------------------------------------------------
/uploads/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liamka/Kayden-blog-engine/254d53a0343b2eb5fb9c7d82d2e7d5b99c59397f/uploads/01.jpg
--------------------------------------------------------------------------------
/utils.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/russross/blackfriday"
5 | "net/http"
6 | "strconv"
7 | "time"
8 | "strings"
9 | )
10 |
11 | func ConvertMarkdownToHtml(markdawn string) string {
12 | return string(blackfriday.MarkdownBasic([]byte(markdawn)))
13 | }
14 |
15 | func CheckCookies(r *http.Request) bool {
16 | a := false
17 | for _, cookie := range r.Cookies() {
18 | if cookie.Name == config.CookieName {
19 | if cookie.Value == config.Pass {
20 | a = true
21 | } else {
22 | a = false
23 | }
24 | }
25 | }
26 | return a
27 | }
28 |
29 | func timeX(t string) string{
30 | i, _ := strconv.ParseInt(t, 10, 64)
31 | tm := time.Unix(i, 0)
32 | Time := tm.Format("_2-01-2006")
33 | return Time
34 | }
35 |
36 | func tagsX(t string, u string) string{
37 | t_ := strings.Split(t, ", ")
38 | var tags string
39 | var ta string
40 | for _,element := range t_ {
41 | if element == "" {
42 | continue
43 | }
44 | if u != "true" {
45 | ta += element+", "
46 | tagsz := len(ta)
47 | tags = ta[:tagsz-2]
48 | } else {
49 | tags += ""+element+""
50 | }
51 | }
52 | return tags
53 | }
54 |
55 | func timeRFC(t string) string{
56 | i, _ := strconv.ParseInt(t, 10, 64)
57 | tm := time.Unix(i, 0)
58 | Time := tm.Format(time.RFC1123Z)
59 | return Time
60 | }
--------------------------------------------------------------------------------
/views/404.html:
--------------------------------------------------------------------------------
1 | {{ define "404" }}
2 | {{ template "header" }}
3 |
4 |
Запись не найдена
6 |
8 | {{ .Title }} ({{ .Tags }}) 9 |
10 | {{ end }} 11 | 12 |