progress
This commit is contained in:
parent
96479f1389
commit
94f996c10d
|
@ -0,0 +1,11 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func Path(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Index(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package links
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.sr.ht/~kisom/goutils/config"
|
||||||
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The keys are all set up as constants to avoid typos and
|
||||||
|
// runtime surprises. I've prefixed the names with k, which
|
||||||
|
// perhaps counterintuitively is not for 'kyle' but for 'key'.
|
||||||
|
kDriver = "DB_ENGINE"
|
||||||
|
kName = "DB_NAME"
|
||||||
|
kUser = "DB_USER"
|
||||||
|
kPass = "DB_PASSWORD"
|
||||||
|
kHost = "DB_HOST"
|
||||||
|
kPort = "DB_PORT"
|
||||||
|
|
||||||
|
defaultDriver = "postgres"
|
||||||
|
defaultName = "kls"
|
||||||
|
defaultUser = "kls"
|
||||||
|
defaultPort = "5432"
|
||||||
|
)
|
||||||
|
|
||||||
|
func connString(user, pass, host, name, port string) string {
|
||||||
|
return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=verify-full",
|
||||||
|
user, pass, host, port, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect will try to open a connection to the database using the
|
||||||
|
// standard configuration vars, etc.
|
||||||
|
func Connect(ctx context.Context) (*pgxpool.Pool, error) {
|
||||||
|
driver := config.GetDefault(kDriver, defaultDriver)
|
||||||
|
if driver != defaultDriver {
|
||||||
|
return nil, errors.F("database: unsupported driver %s", driver)
|
||||||
|
}
|
||||||
|
|
||||||
|
user := config.GetDefault(kUser, defaultUser)
|
||||||
|
pass := config.Get(kPass)
|
||||||
|
host := config.Get(kHost)
|
||||||
|
name := config.GetDefault(kName, defaultName)
|
||||||
|
port := config.GetDefault(kPort, defaultPort)
|
||||||
|
cstr := connString(user, pass, host, name, port)
|
||||||
|
|
||||||
|
return pgxpool.Connect(ctx, cstr)
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package links
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Masterminds/squirrel"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/jackc/pgx/v4"
|
||||||
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
var psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar)
|
||||||
|
|
||||||
|
type URL struct {
|
||||||
|
ID string
|
||||||
|
URL string
|
||||||
|
NURL string // normalized URL
|
||||||
|
Short string
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *URL) StoreURL(ctx context.Context, db *pgxpool.Pool) error {
|
||||||
|
stmt := psql.Insert("urls").
|
||||||
|
Columns("id", "url", "nurl", "short", "created_at").
|
||||||
|
Values(u.ID, u.URL, u.NURL, u.Short, u.Short, u.CreatedAt)
|
||||||
|
query, args, err := stmt.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := db.Exec(ctx, query, args...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize cleans the URL to only the parts we care about
|
||||||
|
func Normalize(u *url.URL) *url.URL {
|
||||||
|
norm := &url.URL{
|
||||||
|
Scheme: u.Scheme,
|
||||||
|
Host: u.Host,
|
||||||
|
Path: u.Path,
|
||||||
|
RawPath: u.RawPath,
|
||||||
|
}
|
||||||
|
return norm
|
||||||
|
}
|
||||||
|
|
||||||
|
func NormalizeString(s string) (string, error) {
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
u = Normalize(u)
|
||||||
|
return u.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(u *url.URL) *URL {
|
||||||
|
link := URL{
|
||||||
|
ID: uuid.NewString(),
|
||||||
|
URL: u.String(),
|
||||||
|
NURL: Normalize(u).String(),
|
||||||
|
Short: GenCode(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromString(s string) (*URL, error) {
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return New(u), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Lookup(ctx context.Context, db *pgxpool.Pool, s string) (string, error) {
|
||||||
|
u, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
u = Normalize(u)
|
||||||
|
|
||||||
|
stmt := psql.Select("short").From("urls").
|
||||||
|
Where(squirrel.Eq{"nurl": u.String()})
|
||||||
|
query, args, err := stmt.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
row := db.QueryRow(ctx, query, args...)
|
||||||
|
|
||||||
|
var short string
|
||||||
|
err := row.Scan(&short)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return short, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StoreURL(ctx context.Context, db *pgxpool.Pool, s string) (string, error) {
|
||||||
|
short, err := Lookup(ctx, db, s)
|
||||||
|
if err == nil {
|
||||||
|
return short, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != pgx.ErrNoRows {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := FromString(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = u.Store(ctx, db)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.Short, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RetrieveURL(ctx context.Context, db *pgxpool.Pool, short string) (string, error) {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue