package main import ( "bytes" "context" "database/sql" "fmt" "log" "net/http" "os" "strconv" "time" "github.com/joho/godotenv" "github.com/yuin/goldmark" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/renderer/html" "paterissa.net/mblog/internal" _ "github.com/jackc/pgx/v5/stdlib" ) var db *sql.DB var mdParser goldmark.Markdown func writeErr(w http.ResponseWriter, errcode int, msg string) { w.WriteHeader(errcode) w.Write([]byte(msg)) return } func mdServe(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { writeErr(w, 405, "Method Not Allowed") return } err := r.ParseMultipartForm(4 << 20) if err != nil { writeErr(w, 500, "Failed to retrieve form data") return } var buf bytes.Buffer md := r.PostForm.Get("raw") err = mdParser.Convert([]byte(md), &buf) if err != nil { writeErr(w, 500, fmt.Sprintf("Failed to compile markdown into html: %v", err)) return } ctx, cancel := context.WithTimeout(r.Context(), 5 * time.Second) defer cancel() tx, err := db.BeginTx(ctx, nil) if err != nil { writeErr(w, 500, fmt.Sprintf("Failed to initialize transaction: %v", err)) return } defer tx.Rollback() stmt, err := tx.PrepareContext(ctx, "INSERT INTO posts (name, content) VALUES ($1, $2);") if err != nil { writeErr(w, 500, fmt.Sprintf("Failed to prepare DB statement: %v", err)) return } defer stmt.Close() _, err = stmt.ExecContext(ctx, os.Getenv("webmaster"), buf.Bytes()) if err != nil { writeErr(w, 500, fmt.Sprintf("Failed to execute statement: %v", err)) return } err = tx.Commit() if err != nil { writeErr(w, 500, fmt.Sprintf("Failed to commit DB transaction: %v", err)) } w.WriteHeader(200) w.Write([]byte("Successfully parsed and stored markdown")) } func main() { err := godotenv.Load() if err != nil { log.Fatal("Failed to load env") } mdParser = goldmark.New( goldmark.WithExtensions( extension.GFM, extension.Footnote, extension.Typographer, ), goldmark.WithParserOptions( parser.WithAutoHeadingID(), ), goldmark.WithRendererOptions( html.WithHardWraps(), ), ) host := os.Getenv("db_host") port, err := strconv.ParseUint(os.Getenv("db_port"), 10, 64) if err != nil { port = 5432 } dbName := os.Getenv("db_name") user := os.Getenv("db_user") pass := os.Getenv("db_pass") connStr := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, pass, dbName) db, err = sql.Open("pgx", connStr) if err != nil { fmt.Fprintf(os.Stderr, "Unable to connect to DB: %v\n", err) os.Exit(1) } defer db.Close() internal.Migrate(db, os.Getenv("webmaster"), os.Getenv("blog_pass1"), os.Getenv("blog_pass2")) router := http.NewServeMux() router.HandleFunc("/", mdServe) appPort, err := strconv.ParseUint(os.Getenv("parser_port"), 10, 64) if err != nil { appPort = 5006 } srv := &http.Server{ Addr: fmt.Sprintf(":%d", appPort), Handler: router, } err = srv.ListenAndServe() log.Fatal(err) }