Skip to content

Commit

Permalink
feat(database): add mysql driver
Browse files Browse the repository at this point in the history
  • Loading branch information
lukewhrit committed Sep 6, 2024
1 parent 737730b commit 78e30b6
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 21 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Pastebins are a type of online content storage service where users can store pla
- [x] Configurable ratelimiting, expiration, compression, etc.
- [x] Modern, JavaScript-free user interface
- [x] Syntax highlighting for all the most popular languages and Raw text mode
- [x] SQLite and PostgreSQL Support
- [x] SQLite, MySQL, and PostgreSQL Support
- [x] Basic Auth for private instances
- [ ] Password-protected encrypted pastes
- [ ] Paste collections
Expand Down Expand Up @@ -106,7 +106,7 @@ volumes:
#### Manually
> [!IMPORTANT]
> **Requires: [Git](https://git-scm.com/downloads), [Go 1.22.4](https://go.dev/doc/install), [GNU Makefile](https://www.gnu.org/software/make/#download), and a SQLite database or [PostgreSQL](https://www.postgresql.org/download/) [server](https://m.do.co/c/beaf675c3e00).**
> **Requires: [Git](https://git-scm.com/downloads), [Go 1.22.4](https://go.dev/doc/install), [GNU Makefile](https://www.gnu.org/software/make/#download), and a SQLite, MySQL, or [PostgreSQL](https://www.postgresql.org/download/) [server](https://m.do.co/c/beaf675c3e00).**
```sh
# Clone the Github repository
Expand All @@ -118,6 +118,7 @@ $ make spirit

# Start Spacebin
$ SPIRIT_CONNECTION_URI="sqlite://database.sqlite" ./bin/spirit # SQLite
$ SPIRIT_CONNECTION_URI="mysql://<your instance URI>?parseTime=true" ./bin/spirit
$ SPIRIT_CONNECTION_URI="postgres://<your PostgreSQL instance URI>" ./bin/spirit # PostgreSQL

# Success! Spacebin is now available at port 9000 on your machine.
Expand Down Expand Up @@ -149,6 +150,8 @@ Spacebin supports two database formats: **SQLite** and **Postgres**
- For SQLite, use either the scheme `file://` or `sqlite://` and a file name.
- Example: `file://database.db`
- For PostgreSQL, use [the standard PostgreSQL URI format](https://stackoverflow.com/questions/3582552/what-is-the-format-for-the-postgresql-connection-string-url#20722229).
- For MySQL, use the [DSN format](https://github.com/go-sql-driver/mysql?tab=readme-ov-file#dsn-data-source-name) prefixed with `mysql://` or `mariadb://`
- You must set the `parseTime` option to true; append `?parseTime=true` to the end of the URI

### Usage

Expand Down
30 changes: 15 additions & 15 deletions cmd/spacebin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,29 +61,27 @@ func main() {
// Connect either to SQLite or PostgreSQL
switch uri.Scheme {
case "file", "sqlite":
sq, err := database.NewSQLite(uri.Host)
if err != nil {
log.Fatal().
Err(err).
Msg("Could not connect to database")
}
db = sq
case "postgresql":
pg, err := database.NewPostgres(uri.String())
if err != nil {
log.Fatal().
Err(err).
Msg("Could not connect to database")
}
db = pg
db, err = database.NewSQLite(uri)
case "postgresql", "postgres":
db, err = database.NewPostgres(uri)
case "mysql", "mariadb":
db, err = database.NewMySQL(uri)
}

if err != nil {
log.Fatal().
Err(err).
Msg("Could not connect to database")
}

// Perform migrations
if err := db.Migrate(context.Background()); err != nil {
log.Fatal().
Err(err).
Msg("Failed migrations; Could not create DOCUMENTS tables.")
}

// Create a new server and register middleware, security headers, static files, and handlers
m := server.NewServer(&config.Config, db)

m.MountMiddleware()
Expand All @@ -95,11 +93,13 @@ func main() {

m.MountHandlers()

// Create the server on the specified host and port
srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", config.Config.Host, config.Config.Port),
Handler: m.Router,
}

// Graceful shutdown
srvCtx, srvStopCtx := context.WithCancel(context.Background())

// Watch for OS signals
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
)

require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
Expand All @@ -35,6 +36,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-sql-driver/mysql v1.8.1
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
Expand Down Expand Up @@ -28,6 +30,8 @@ github.com/go-chi/httprate v0.14.1 h1:EKZHYEZ58Cg6hWcYzoZILsv7ppb46Wt4uQ738IRtpZ
github.com/go-chi/httprate v0.14.1/go.mod h1:TUepLXaz/pCjmCtf/obgOQJ2Sz6rC8fSf5cAt5cnTt0=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
Expand Down
79 changes: 79 additions & 0 deletions internal/database/database_mysql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2020-2024 Luke Whritenour
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package database

import (
"context"
"database/sql"
"net/url"
"strings"
"time"

_ "github.com/go-sql-driver/mysql"
)

type MySQL struct {
*sql.DB
}

func NewMySQL(uri *url.URL) (Database, error) {
_, uriTrimmed, _ := strings.Cut(uri.String(), uri.Scheme+"://")
db, err := sql.Open("mysql", uriTrimmed)

db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10)

return &MySQL{db}, err
}

func (m *MySQL) Migrate(ctx context.Context) error {
_, err := m.Exec(`
CREATE TABLE IF NOT EXISTS documents (
id VARCHAR(255) PRIMARY KEY,
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)`)

return err
}

func (m *MySQL) GetDocument(ctx context.Context, id string) (Document, error) {
doc := new(Document)
row := m.QueryRow("SELECT * FROM documents WHERE id=?", id)
err := row.Scan(&doc.ID, &doc.Content, &doc.CreatedAt, &doc.UpdatedAt)

return *doc, err
}

func (m *MySQL) CreateDocument(ctx context.Context, id, content string) error {
tx, err := m.Begin()

if err != nil {
return err
}

_, err = tx.Exec("INSERT INTO documents (id, content) VALUES (?, ?)",
id, content) // created_at and updated_at are auto-generated

if err != nil {
return err
}

return tx.Commit()
}
5 changes: 3 additions & 2 deletions internal/database/database_pg.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package database
import (
"context"
"database/sql"
"net/url"

_ "github.com/lib/pq"
)
Expand All @@ -27,8 +28,8 @@ type Postgres struct {
*sql.DB
}

func NewPostgres(uri string) (Database, error) {
db, err := sql.Open("postgres", uri)
func NewPostgres(uri *url.URL) (Database, error) {
db, err := sql.Open("postgres", uri.String())

return &Postgres{db}, err
}
Expand Down
5 changes: 3 additions & 2 deletions internal/database/database_sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package database
import (
"context"
"database/sql"
"net/url"
"sync"

_ "modernc.org/sqlite"
Expand All @@ -29,8 +30,8 @@ type SQLite struct {
sync.RWMutex
}

func NewSQLite(filesath string) (Database, error) {
db, err := sql.Open("sqlite", filesath)
func NewSQLite(uri *url.URL) (Database, error) {
db, err := sql.Open("sqlite", uri.Host)

return &SQLite{db, sync.RWMutex{}}, err
}
Expand Down

0 comments on commit 78e30b6

Please sign in to comment.