~yerinalexey/gobin

ref: 2c2abcab2ed755c1869e66b2c7ef8ff2dba9d15b gobin/main.go -rw-r--r-- 3.1 KiB
2c2abcab — Alexey Yerin fix: return 404 on invalid request param 1 year, 1 month ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// gobin - Simple text pasting service
// Copyright (c) 2020 Alexey Yerin
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <https://www.gnu.org/licenses/>.

package main

import (
	"context"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/jackc/pgx/v4/pgxpool"
)

type Paste struct {
	Id       int
	Contents string
}

func main() {
	db_uri, ok := os.LookupEnv("DATABASE_URI")
	if !ok {
		log.Fatalln("Can't connect to the database: DATABASE_URI is not set")
	}

	pool, err := pgxpool.Connect(context.Background(), db_uri)
	if err != nil {
		log.Fatalln("Can't connect to the database: ", err)
	}
	defer pool.Close()

	port, ok := os.LookupEnv("PORT")
	if !ok || port == "" {
		port = "4000"
	}

	base_url, ok := os.LookupEnv("BASE_URL")
	if !ok || base_url == "" {
		base_url = "http://localhost:4000"
	}

	tmpl := template.Must(template.ParseGlob("templates/*.html"))

	r := mux.NewRouter()
	r.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		tmpl.ExecuteTemplate(w, "home", nil)
	})

	r.HandleFunc("/new", func(w http.ResponseWriter, req *http.Request) {
		tmpl.ExecuteTemplate(w, "new", nil)
	}).Methods("GET")

	r.HandleFunc("/new", func(w http.ResponseWriter, req *http.Request) {
		contents := req.FormValue("contents")

		var id int64
		err := pool.QueryRow(context.Background(), "insert into pastes (contents) values ($1) returning id", contents).Scan(&id)

		if err != nil {
			// Not Fatalf because nobody wants server to crash when database
			// insert fails
			log.Printf("Failed to submit paste: %v\n", err)
		}

		link := fmt.Sprintf("%s/paste/%d", base_url, id)

		tmpl.ExecuteTemplate(w, "saved", link)
	}).Methods("POST")

	r.HandleFunc("/paste/{id}", func(w http.ResponseWriter, req *http.Request) {
		vars := mux.Vars(req)
		id, err := strconv.Atoi(vars["id"])

		if err != nil {
			w.WriteHeader(404)
			tmpl.ExecuteTemplate(w, "not-found", nil)

			return
		}

		var contents string

		err = pool.QueryRow(context.Background(), "select contents from pastes where id=$1", id).Scan(&contents)

		if err != nil {
			w.WriteHeader(404)
			tmpl.ExecuteTemplate(w, "not-found", nil)

			return
		}

		tmpl.ExecuteTemplate(w, "paste", Paste{id, contents})
	})

	r.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			w.WriteHeader(404)
			tmpl.ExecuteTemplate(w, "not-found", nil)
	})

	// Static files
	fs := http.FileServer(http.Dir("static/"))
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs))

	log.Printf("Staring on port %s\n", port)
	log.Fatalln(http.ListenAndServe(":"+port, r))
}