Skip to content

Commit

Permalink
split database logic
Browse files Browse the repository at this point in the history
  • Loading branch information
artem-streltsov committed Sep 16, 2024
1 parent 3356668 commit 33560e4
Show file tree
Hide file tree
Showing 12 changed files with 178 additions and 159 deletions.
142 changes: 23 additions & 119 deletions database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package database
import (
"database/sql"
"fmt"
"os"
"path/filepath"
"time"

_ "modernc.org/sqlite"
Expand Down Expand Up @@ -31,6 +33,11 @@ type URL struct {
}

func NewDB(dbPath string) (*DB, error) {
err := touchDB(dbPath)
if err != nil {
return nil, fmt.Errorf("error creating database: %w", err)
}

db, err := sql.Open("sqlite", dbPath)
if err != nil {
return nil, fmt.Errorf("error opening database: %w", err)
Expand All @@ -47,6 +54,22 @@ func NewDB(dbPath string) (*DB, error) {
return &DB{db}, nil
}

func touchDB(dbPath string) error {
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
dir := filepath.Dir(dbPath)
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("error creating directory for database: %w", err)
}
file, err := os.Create(dbPath)
if err != nil {
return fmt.Errorf("error creating database file: %w", err)
}
file.Close()
}

return nil
}

func initSchema(db *sql.DB) error {
schema := `
CREATE TABLE IF NOT EXISTS users (
Expand All @@ -72,122 +95,3 @@ func initSchema(db *sql.DB) error {
_, err := db.Exec(schema)
return err
}

func (db *DB) CreateUser(username, email, password string) (*User, error) {
stmt, err := db.Prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)")
if err != nil {
return nil, fmt.Errorf("error preparing statement: %w", err)
}
defer stmt.Close()

result, err := stmt.Exec(username, email, password)
if err != nil {
return nil, fmt.Errorf("error inserting user: %w", err)
}

id, err := result.LastInsertId()
if err != nil {
return nil, fmt.Errorf("error getting last insert ID: %w", err)
}

return &User{ID: id, Username: username, Email: email, Password: password}, nil
}

func (db *DB) GetUserByUsername(username string) (*User, error) {
var user User
err := db.QueryRow("SELECT id, username, email, password FROM users WHERE username = ?", username).Scan(&user.ID, &user.Username, &user.Email, &user.Password)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, fmt.Errorf("error querying user: %w", err)
}
return &user, nil
}

func (db *DB) InsertURL(url, key string, userID int64, password string, qrCode string) error {
stmt, err := db.Prepare("INSERT INTO urls (url, key, user_id, password, qr_code) VALUES (?, ?, ?, ?, ?)")
if err != nil {
return fmt.Errorf("error preparing statement: %w", err)
}
defer stmt.Close()

_, err = stmt.Exec(url, key, userID, password, qrCode) // Add qrCode as an argument
if err != nil {
return fmt.Errorf("error inserting URL: %w", err)
}

return nil
}

func (db *DB) GetURL(key string) (*URL, error) {
var url URL
err := db.QueryRow("SELECT id, user_id, url, key, created_at, clicks, password FROM urls WHERE key = ?", key).Scan(&url.ID, &url.UserID, &url.URL, &url.Key, &url.CreatedAt, &url.Clicks, &url.Password)
if err != nil {
if err == sql.ErrNoRows {
return nil, fmt.Errorf("no URL found for key: %s", key)
}
return nil, fmt.Errorf("error querying URL: %w", err)
}
return &url, nil
}

func (db *DB) GetURLsByUserID(userID int64) ([]URL, error) {
rows, err := db.Query("SELECT id, user_id, url, key, created_at, clicks, password FROM urls WHERE user_id = ?", userID)
if err != nil {
return nil, fmt.Errorf("error querying URLs: %w", err)
}
defer rows.Close()

var urls []URL
for rows.Next() {
var url URL
if err := rows.Scan(&url.ID, &url.UserID, &url.URL, &url.Key, &url.CreatedAt, &url.Clicks, &url.Password); err != nil {
return nil, fmt.Errorf("error scanning row: %w", err)
}
urls = append(urls, url)
}

if err := rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating rows: %w", err)
}

return urls, nil
}

func (db *DB) IncrementClicks(urlID int64) error {
_, err := db.Exec("UPDATE urls SET clicks = clicks + 1 WHERE id = ?", urlID)
if err != nil {
return fmt.Errorf("error incrementing clicks: %w", err)
}
return nil
}

func (db *DB) UpdateURL(id int64, url string, password string) error {
_, err := db.Exec("UPDATE urls SET url = ?, password = ? WHERE id = ?", url, password, id)
if err != nil {
return fmt.Errorf("error updating URL: %w", err)
}
return nil
}

func (db *DB) DeleteURL(id int64) error {
_, err := db.Exec("DELETE FROM urls WHERE id = ?", id)
if err != nil {
return fmt.Errorf("error deleting URL: %w", err)
}
return nil
}

func (db *DB) GetURLByID(id int64) (*URL, error) {
var url URL
err := db.QueryRow("SELECT id, user_id, url, key, created_at, clicks, password, qr_code FROM urls WHERE id = ?", id).Scan(
&url.ID, &url.UserID, &url.URL, &url.Key, &url.CreatedAt, &url.Clicks, &url.Password, &url.QRCode)
if err != nil {
if err == sql.ErrNoRows {
return nil, fmt.Errorf("no URL found for id: %d", id)
}
return nil, fmt.Errorf("error querying URL: %w", err)
}
return &url, nil
}
95 changes: 95 additions & 0 deletions database/url.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package database

import (
"database/sql"
"fmt"

_ "modernc.org/sqlite"
)

func (db *DB) InsertURL(url, key string, userID int64, password string, qrCode string) error {
stmt, err := db.Prepare("INSERT INTO urls (url, key, user_id, password, qr_code) VALUES (?, ?, ?, ?, ?)")
if err != nil {
return fmt.Errorf("error preparing statement: %w", err)
}
defer stmt.Close()

_, err = stmt.Exec(url, key, userID, password, qrCode) // Add qrCode as an argument
if err != nil {
return fmt.Errorf("error inserting URL: %w", err)
}

return nil
}

func (db *DB) GetURL(key string) (*URL, error) {
var url URL
err := db.QueryRow("SELECT id, user_id, url, key, created_at, clicks, password FROM urls WHERE key = ?", key).Scan(&url.ID, &url.UserID, &url.URL, &url.Key, &url.CreatedAt, &url.Clicks, &url.Password)
if err != nil {
if err == sql.ErrNoRows {
return nil, fmt.Errorf("no URL found for key: %s", key)
}
return nil, fmt.Errorf("error querying URL: %w", err)
}
return &url, nil
}

func (db *DB) GetURLsByUserID(userID int64) ([]URL, error) {
rows, err := db.Query("SELECT id, user_id, url, key, created_at, clicks, password FROM urls WHERE user_id = ?", userID)
if err != nil {
return nil, fmt.Errorf("error querying URLs: %w", err)
}
defer rows.Close()

var urls []URL
for rows.Next() {
var url URL
if err := rows.Scan(&url.ID, &url.UserID, &url.URL, &url.Key, &url.CreatedAt, &url.Clicks, &url.Password); err != nil {
return nil, fmt.Errorf("error scanning row: %w", err)
}
urls = append(urls, url)
}

if err := rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating rows: %w", err)
}

return urls, nil
}

func (db *DB) IncrementClicks(urlID int64) error {
_, err := db.Exec("UPDATE urls SET clicks = clicks + 1 WHERE id = ?", urlID)
if err != nil {
return fmt.Errorf("error incrementing clicks: %w", err)
}
return nil
}

func (db *DB) UpdateURL(id int64, url string, password string) error {
_, err := db.Exec("UPDATE urls SET url = ?, password = ? WHERE id = ?", url, password, id)
if err != nil {
return fmt.Errorf("error updating URL: %w", err)
}
return nil
}

func (db *DB) DeleteURL(id int64) error {
_, err := db.Exec("DELETE FROM urls WHERE id = ?", id)
if err != nil {
return fmt.Errorf("error deleting URL: %w", err)
}
return nil
}

func (db *DB) GetURLByID(id int64) (*URL, error) {
var url URL
err := db.QueryRow("SELECT id, user_id, url, key, created_at, clicks, password, qr_code FROM urls WHERE id = ?", id).Scan(
&url.ID, &url.UserID, &url.URL, &url.Key, &url.CreatedAt, &url.Clicks, &url.Password, &url.QRCode)
if err != nil {
if err == sql.ErrNoRows {
return nil, fmt.Errorf("no URL found for id: %d", id)
}
return nil, fmt.Errorf("error querying URL: %w", err)
}
return &url, nil
}
40 changes: 40 additions & 0 deletions database/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package database

import (
"database/sql"
"fmt"

_ "modernc.org/sqlite"
)

func (db *DB) CreateUser(username, email, password string) (*User, error) {
stmt, err := db.Prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)")
if err != nil {
return nil, fmt.Errorf("error preparing statement: %w", err)
}
defer stmt.Close()

result, err := stmt.Exec(username, email, password)
if err != nil {
return nil, fmt.Errorf("error inserting user: %w", err)
}

id, err := result.LastInsertId()
if err != nil {
return nil, fmt.Errorf("error getting last insert ID: %w", err)
}

return &User{ID: id, Username: username, Email: email, Password: password}, nil
}

func (db *DB) GetUserByUsername(username string) (*User, error) {
var user User
err := db.QueryRow("SELECT id, username, email, password FROM users WHERE username = ?", username).Scan(&user.ID, &user.Username, &user.Email, &user.Password)
if err != nil {
if err == sql.ErrNoRows {
return nil, nil
}
return nil, fmt.Errorf("error querying user: %w", err)
}
return &user, nil
}
7 changes: 3 additions & 4 deletions handlers/auth.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package handlers

import (
"log"
"net/http"
"log"
"net/http"

"golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/bcrypt"
)

func (h *Handler) registerHandler(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -115,4 +115,3 @@ func (h *Handler) logoutHandler(w http.ResponseWriter, r *http.Request) {
session.Save(r, w)
http.Redirect(w, r, "/", http.StatusSeeOther)
}

9 changes: 4 additions & 5 deletions handlers/dashboard.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package handlers

import (
"net/http"
"log"
"github.com/artem-streltsov/url-shortener/database"
"log"
"net/http"

"github.com/artem-streltsov/url-shortener/database"
)

func (h *Handler) dashboardHandler(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -70,4 +70,3 @@ func (h *Handler) dashboardHandler(w http.ResponseWriter, r *http.Request) {
return
}
}

5 changes: 2 additions & 3 deletions handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ func NewHandler(db *database.DB) *Handler {
// TODO: use environment variable
templatesDir := "./templates"
templates := template.Must(template.ParseGlob(filepath.Join(templatesDir, "*.html")))
// TODO: move to main and pass as argument

// TODO: move to main and pass as argument
secretKey := os.Getenv("SESSION_SECRET_KEY")
if secretKey == "" {
log.Fatalf("SESSION_SECRET_KEY environment variable is not set")
Expand All @@ -50,4 +50,3 @@ func (h *Handler) Routes() http.Handler {
rl := middleware.NewRateLimiter(100, time.Minute)
return middleware.LoggingMiddleware(middleware.RateLimitingMiddleware(rl)(mux))
}

7 changes: 3 additions & 4 deletions handlers/index.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package handlers

import (
"net/http"
"github.com/artem-streltsov/url-shortener/database"
"net/http"

"github.com/artem-streltsov/url-shortener/database"
)

func (h *Handler) indexHandler(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -37,4 +37,3 @@ func (h *Handler) indexHandler(w http.ResponseWriter, r *http.Request) {
return
}
}

9 changes: 4 additions & 5 deletions handlers/redirect.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package handlers

import (
"net/http"
"strings"

"golang.org/x/crypto/bcrypt"
"net/http"
"strings"

"github.com/artem-streltsov/url-shortener/safebrowsing"
"golang.org/x/crypto/bcrypt"
)

func (h *Handler) redirectHandler(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -60,4 +60,3 @@ func (h *Handler) redirectHandler(w http.ResponseWriter, r *http.Request) {

http.Redirect(w, r, url.URL, http.StatusFound)
}

Loading

0 comments on commit 33560e4

Please sign in to comment.