Compare commits

...

7 Commits

3 changed files with 126 additions and 37 deletions

View File

@ -2,11 +2,13 @@ package db
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"time" "time"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/pgxpool"
) )
@ -48,3 +50,30 @@ func transaction(handler func(context.Context, pgx.Tx) (statusCode int, err erro
} }
return return
} }
// Handle database error
func handleDBError(errIn error) (statusCode int, err error) {
if errIn == nil {
statusCode = http.StatusOK
return
}
if errors.Is(errIn, pgx.ErrNoRows) {
err = fmt.Errorf("not found")
statusCode = http.StatusNotFound
return
}
var pgErr *pgconn.PgError
if errors.As(errIn, &pgErr) {
switch pgErr.Code {
case "22P02", "22007": // Invalid data format
err = fmt.Errorf("%s", pgErr.Message)
statusCode = http.StatusBadRequest
return
case "23505": // Unique constraint violation
err = fmt.Errorf("already exists")
statusCode = http.StatusConflict
return
}
}
return http.StatusInternalServerError, errIn
}

View File

@ -1,6 +1,7 @@
package models package models
import ( import (
"encoding/json"
"time" "time"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
@ -8,7 +9,7 @@ import (
type User struct { type User struct {
Name string `json:"name"` Name string `json:"name"`
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"isAdmin"`
} }
type MIME struct { type MIME struct {
@ -16,50 +17,95 @@ type MIME struct {
Extension string `json:"extension"` Extension string `json:"extension"`
} }
type Category struct { type (
ID string `json:"id"` CategoryCore struct {
Name string `json:"name"` ID string `json:"id"`
Color pgtype.Text `json:"color"` Name string `json:"name"`
CreatedAt time.Time `json:"created_at"` Color pgtype.Text `json:"color"`
Creator User `json:"creator"` }
} CategoryItem struct {
CategoryCore
}
CategoryFull struct {
CategoryCore
CreatedAt time.Time `json:"createdAt"`
Creator User `json:"creator"`
Notes pgtype.Text `json:"notes"`
}
)
type File struct { type (
ID string `json:"id"` FileCore struct {
Name pgtype.Text `json:"name"` ID string `json:"id"`
MIME MIME `json:"mime"` Name pgtype.Text `json:"name"`
CreatedAt time.Time `json:"created_at"` MIME MIME `json:"mime"`
Creator User `json:"creator"` }
} FileItem struct {
FileCore
CreatedAt time.Time `json:"createdAt"`
Creator User `json:"creator"`
}
FileFull struct {
FileCore
CreatedAt time.Time `json:"createdAt"`
Creator User `json:"creator"`
Notes pgtype.Text `json:"notes"`
Metadata *json.RawMessage `json:"metadata"`
Tags []TagCore `json:"tags"`
Viewed int `json:"viewed"`
}
)
type Tag struct { type (
ID string `json:"id"` TagCore struct {
Name string `json:"name"` ID string `json:"id"`
Color pgtype.Text `json:"color"` Name string `json:"name"`
Category Category `json:"category"` Color pgtype.Text `json:"color"`
CreatedAt time.Time `json:"created_at"` }
Creator User `json:"creator"` TagItem struct {
} TagCore
Category CategoryCore `json:"category"`
}
TagFull struct {
TagCore
Category CategoryCore `json:"category"`
CreatedAt time.Time `json:"createdAt"`
Creator User `json:"creator"`
Notes pgtype.Text `json:"notes"`
UsedIncl int `json:"usedIncl"`
UsedExcl int `json:"usedExcl"`
}
)
type Autotag struct { type Autotag struct {
TriggerTag Tag `json:"trigger_tag"` TriggerTag TagCore `json:"triggerTag"`
AddTag Tag `json:"add_tag"` AddTag TagCore `json:"addTag"`
IsActive bool `json:"is_active"` IsActive bool `json:"isActive"`
} }
type Pool struct { type (
ID string `json:"id"` PoolCore struct {
Name string `json:"name"` ID string `json:"id"`
CreatedAt time.Time `json:"created_at"` Name string `json:"name"`
Creator User `json:"creator"` }
} PoolItem struct {
PoolCore
}
PoolFull struct {
PoolCore
CreatedAt time.Time `json:"createdAt"`
Creator User `json:"creator"`
Notes pgtype.Text `json:"notes"`
Viewed int `json:"viewed"`
}
)
type Session struct { type Session struct {
ID int `json:"id"` ID int `json:"id"`
UserAgent string `json:"user_agent"` UserAgent string `json:"userAgent"`
StartedAt time.Time `json:"started_at"` StartedAt time.Time `json:"startedAt"`
ExpiresAt time.Time `json:"expires_at"` ExpiresAt time.Time `json:"expiresAt"`
LastActivity time.Time `json:"last_activity"` LastActivity time.Time `json:"lastActivity"`
} }
type Pagination struct { type Pagination struct {

View File

@ -80,6 +80,20 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public;
COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)';
--
-- Name: uuid_extract_timestamp(uuid); Type: FUNCTION; Schema: public; Owner: -
--
CREATE FUNCTION public.uuid_extract_timestamp(uuid_val uuid) RETURNS timestamp with time zone
LANGUAGE sql IMMUTABLE
AS $$
SELECT to_timestamp(
('x' || LEFT(REPLACE(uuid_val::TEXT, '-', ''), 12))::BIT(48)::BIGINT
/ 1000.0
);
$$;
-- --
-- Name: uuid_v7(timestamp with time zone); Type: FUNCTION; Schema: public; Owner: - -- Name: uuid_v7(timestamp with time zone); Type: FUNCTION; Schema: public; Owner: -
-- --
@ -290,7 +304,7 @@ CREATE TABLE data.files (
mime_id smallint NOT NULL, mime_id smallint NOT NULL,
datetime timestamp with time zone DEFAULT clock_timestamp() NOT NULL, datetime timestamp with time zone DEFAULT clock_timestamp() NOT NULL,
notes text, notes text,
metadata jsonb NOT NULL, metadata jsonb,
creator_id smallint NOT NULL creator_id smallint NOT NULL
); );