initial commit
This commit is contained in:
commit
c21f569144
37 changed files with 3956 additions and 0 deletions
75
database/box.go
Normal file
75
database/box.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"foobar/model"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func SelectBox(boxURL model.BoxURL) (model.Box, error) {
|
||||
row := instance.QueryRow(
|
||||
`SELECT id, edit_code, header, private, moderation, created_at, last_updated_at
|
||||
FROM box WHERE url = $1;`,
|
||||
boxURL,
|
||||
)
|
||||
if row.Err() != nil {
|
||||
return model.Box{}, row.Err()
|
||||
}
|
||||
var (
|
||||
id uuid.UUID
|
||||
edit_code string
|
||||
header *model.Markdown
|
||||
private bool
|
||||
moderation bool
|
||||
createdAt time.Time
|
||||
lastUpdatedAt time.Time
|
||||
)
|
||||
err := row.Scan(&id, &edit_code, &header, &private, &moderation, &createdAt, &lastUpdatedAt)
|
||||
if err != nil {
|
||||
return model.Box{}, err
|
||||
}
|
||||
if header == nil {
|
||||
header = new(model.Markdown)
|
||||
*header = ""
|
||||
}
|
||||
return model.NewBox(id, boxURL, edit_code, *header, private, moderation, createdAt, lastUpdatedAt), nil
|
||||
}
|
||||
|
||||
func UpdateHeader(tx *sql.Tx, boxId uuid.UUID, text model.Markdown) error {
|
||||
result, err := tx.Exec(`UPDATE box SET header = $1 where id = $2`, text, boxId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return errNoRowsAffected
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func InsertBox(tx *sql.Tx, box model.Box, editCode string, header model.Markdown) error {
|
||||
result, err := tx.Exec(
|
||||
`INSERT INTO box (id, url, edit_code, header, private, moderation, created_at, last_updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
|
||||
box.ID(), box.Url(), editCode, header, box.Private(), box.Moderation(),
|
||||
box.CreatedAt(), box.LastUpdatedAt(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return errNoRowsAffected
|
||||
}
|
||||
return nil
|
||||
}
|
||||
86
database/database.go
Normal file
86
database/database.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/pressly/goose/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
instance *sql.DB
|
||||
|
||||
errNoRowsAffected = fmt.Errorf("no rows affected")
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
embedMigrations embed.FS
|
||||
)
|
||||
|
||||
func New() *sql.DB {
|
||||
|
||||
// Evita criar mais de um banco
|
||||
if instance != nil {
|
||||
return instance
|
||||
}
|
||||
|
||||
// Carrega as variáveis de ambiente definadas no arquivo .env na raiz do projeto.
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Para rodar os testes desse pacote, É necessário que haja um symlink de um .env nesta
|
||||
// pasta (database/) apontando para o .env no topo do projeto, já que 'go test' usa o
|
||||
// diretório raiz do pacote (./database) para executar os testes enquanto 'go run' usa o diretório
|
||||
// raiz do projeto.
|
||||
|
||||
// variáveis ambientais de .env são carregadas para o ambiente do nosso processo, basta
|
||||
// usar a forma padrão para pegar os valores dessas variáveis.
|
||||
var (
|
||||
user = os.Getenv("PG_USER")
|
||||
pass = os.Getenv("PG_PASS")
|
||||
dbName = os.Getenv("PG_DB")
|
||||
hostAddr = os.Getenv("PG_ADDR")
|
||||
)
|
||||
|
||||
connectionString := fmt.Sprintf("postgresql://%s:%s@%s:5432/%s", user, pass, hostAddr, dbName)
|
||||
db, err := sql.Open("pgx", connectionString)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Verifica se a conexão foi um sucesso
|
||||
if err := db.Ping(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
goose.SetBaseFS(embedMigrations)
|
||||
if err := goose.SetDialect("postgres"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Aplica todas as migrações disponíveis
|
||||
if err := goose.Up(db, "migrations"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
instance = db
|
||||
log.Println("Nova instância do banco de dados foi criada.")
|
||||
return instance
|
||||
}
|
||||
|
||||
func MustBeginTx() *sql.Tx {
|
||||
if instance == nil {
|
||||
panic(fmt.Errorf("Banco de dados não foi inicializado. Não foi possível criar nova transação."))
|
||||
}
|
||||
tx, err := instance.BeginTx(context.Background(), nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
82
database/database_test.go
Normal file
82
database/database_test.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"foobar/model"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
_ = New()
|
||||
}
|
||||
|
||||
func TestGetBox(t *testing.T) {
|
||||
_ = New()
|
||||
|
||||
boxURL, err := model.CheckBoxURL("1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b, err := SelectBox(boxURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if b.ID().String() == "" {
|
||||
t.Fatalf("")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertBox(t *testing.T) {
|
||||
|
||||
_ = New()
|
||||
tx := MustBeginTx()
|
||||
defer tx.Rollback()
|
||||
|
||||
url, err := model.CheckBoxURL("foobar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
box := model.NewBox(uuid.New(), url, "foobar", "", false, false, time.Now(), time.Now())
|
||||
|
||||
err = InsertBox(tx, box, "foobar", "foobar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterFile(t *testing.T) {
|
||||
_ = New()
|
||||
tx := MustBeginTx()
|
||||
defer tx.Rollback()
|
||||
|
||||
url, err := model.CheckBoxURL("foobar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
box := model.NewBox(uuid.New(), url, "foobar", "", false, false, time.Now(), time.Now())
|
||||
|
||||
err = InsertBox(tx, box, "foobar", "foobar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var file = model.NewFile(
|
||||
uuid.New(), "foobar", 6, time.Now(), model.FileMime("plain/text"), md5.Sum([]byte("foobar")),
|
||||
)
|
||||
|
||||
err = InsertFile(tx, box.ID(), file)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
65
database/file.go
Normal file
65
database/file.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"foobar/model"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func InsertFile(tx *sql.Tx, boxId uuid.UUID, file model.File) error {
|
||||
|
||||
row, err := tx.Exec(
|
||||
`INSERT INTO file (id, id_box, name, created_at, size, mime, md5)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7);`,
|
||||
file.ID(), boxId, file.Name(), file.CreatedAt(),
|
||||
file.Size(), file.Mime(), file.Checksum(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := row.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected != 1 {
|
||||
return errNoRowsAffected
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SelectBoxFiles(boxID uuid.UUID) (f []model.File, e error) {
|
||||
// TODO: teste
|
||||
row, err := instance.Query(
|
||||
`SELECT id, name, size, created_at, mime, md5
|
||||
FROM file WHERE id_box = $1 ORDER BY created_at DESC`,
|
||||
boxID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for row.Next() {
|
||||
var (
|
||||
id uuid.UUID
|
||||
name model.Filename
|
||||
size int64
|
||||
createdAt time.Time
|
||||
mime model.FileMime
|
||||
encodedMd5 string
|
||||
)
|
||||
err = row.Scan(&id, &name, &size, &createdAt, &mime, &encodedMd5)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decodedMD5, err := hex.DecodeString(encodedMd5)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f = append(f, model.NewFile(id, name, size, createdAt, mime, model.MD5Checksum(decodedMD5)))
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
36
database/migrations/20250202024620_init_usuario.sql
Normal file
36
database/migrations/20250202024620_init_usuario.sql
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- Diff code generated with pgModeler (PostgreSQL Database Modeler)
|
||||
-- pgModeler version: 1.2.0-alpha1
|
||||
-- Diff date: 2025-02-01 23:56:43
|
||||
-- Source model: dev1
|
||||
-- Database: dev1
|
||||
-- PostgreSQL version: 16.0
|
||||
|
||||
-- [ Diff summary ]
|
||||
-- Dropped objects: 0
|
||||
-- Created objects: 1
|
||||
-- Changed objects: 0
|
||||
|
||||
SET search_path=public,pg_catalog;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
-- [ Created objects ] --
|
||||
-- object: public.usuario | type: TABLE --
|
||||
-- DROP TABLE IF EXISTS public.usuario CASCADE;
|
||||
CREATE TABLE public.usuario (
|
||||
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ,
|
||||
email text NOT NULL,
|
||||
senha_hash text NOT NULL,
|
||||
nome text NOT NULL,
|
||||
ctime timestamp NOT NULL DEFAULT current_timestamp,
|
||||
CONSTRAINT usuario_pk PRIMARY KEY (id)
|
||||
);
|
||||
-- ddl-end --
|
||||
ALTER TABLE public.usuario OWNER TO dev;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
36
database/migrations/20250412193502_file.sql
Normal file
36
database/migrations/20250412193502_file.sql
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
-- +goose Up
|
||||
-- Diff code generated with pgModeler (PostgreSQL Database Modeler)
|
||||
-- pgModeler version: 1.2.0-alpha1
|
||||
-- Diff date: 2025-04-12 16:39:19
|
||||
-- Source model: dev1
|
||||
-- Database: dev1
|
||||
-- PostgreSQL version: 17.0
|
||||
|
||||
-- [ Diff summary ]
|
||||
-- Dropped objects: 1
|
||||
-- Created objects: 1
|
||||
-- Changed objects: 0
|
||||
|
||||
SET search_path=public,pg_catalog;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
-- [ Dropped objects ] --
|
||||
DROP TABLE IF EXISTS public.usuario CASCADE;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
-- [ Created objects ] --
|
||||
-- object: public.file | type: TABLE --
|
||||
-- DROP TABLE IF EXISTS public.file CASCADE;
|
||||
CREATE TABLE public.file (
|
||||
id uuid NOT NULL,
|
||||
name varchar(256) NOT NULL,
|
||||
size integer NOT NULL,
|
||||
created_at timestamp NOT NULL,
|
||||
CONSTRAINT file_pk PRIMARY KEY (id)
|
||||
);
|
||||
-- ddl-end --
|
||||
ALTER TABLE public.file OWNER TO dev;
|
||||
-- ddl-end --
|
||||
|
||||
32
database/migrations/20250414041807_file_mime.sql
Normal file
32
database/migrations/20250414041807_file_mime.sql
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
-- +goose Up
|
||||
-- Diff code generated with pgModeler (PostgreSQL Database Modeler)
|
||||
-- pgModeler version: 1.2.0-alpha1
|
||||
-- Diff date: 2025-04-14 01:18:24
|
||||
-- Source model: dev1
|
||||
-- Database: dev1
|
||||
-- PostgreSQL version: 17.0
|
||||
|
||||
-- [ Diff summary ]
|
||||
-- Dropped objects: 0
|
||||
-- Created objects: 2
|
||||
-- Changed objects: 0
|
||||
|
||||
SET search_path=public,pg_catalog;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
-- [ Created objects ] --
|
||||
-- object: mime | type: COLUMN --
|
||||
ALTER TABLE public.file ADD COLUMN mime text;
|
||||
-- ddl-end --
|
||||
|
||||
UPDATE public.file SET mime = '';
|
||||
|
||||
ALTER TABLE public.file ALTER COLUMN mime SET NOT NULL;
|
||||
|
||||
|
||||
-- object: message | type: COLUMN --
|
||||
ALTER TABLE public.file ADD COLUMN message varchar(2048);
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
71
database/migrations/20250415031815_box_table.sql
Normal file
71
database/migrations/20250415031815_box_table.sql
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
-- +goose Up
|
||||
-- Diff code generated with pgModeler (PostgreSQL Database Modeler)
|
||||
-- pgModeler version: 1.2.0-alpha1
|
||||
-- Diff date: 2025-04-15 00:18:26
|
||||
-- Source model: dev1
|
||||
-- Database: dev1
|
||||
-- PostgreSQL version: 17.0
|
||||
|
||||
-- [ Diff summary ]
|
||||
-- Dropped objects: 0
|
||||
-- Created objects: 6
|
||||
-- Changed objects: 0
|
||||
|
||||
SET search_path=public,pg_catalog;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
-- [ Created objects ] --
|
||||
-- object: width | type: COLUMN --
|
||||
ALTER TABLE public.file ADD COLUMN width smallint;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
-- object: height | type: COLUMN --
|
||||
ALTER TABLE public.file ADD COLUMN height smallint;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
-- object: public.box | type: TABLE --
|
||||
-- DROP TABLE IF EXISTS public.box CASCADE;
|
||||
CREATE TABLE public.box (
|
||||
id uuid NOT NULL,
|
||||
url varchar(256) NOT NULL,
|
||||
edit_code varchar(64) NOT NULL,
|
||||
header varchar(4096),
|
||||
private boolean NOT NULL DEFAULT false,
|
||||
moderation boolean NOT NULL DEFAULT false,
|
||||
created_at timestamp NOT NULL DEFAULT current_timestamp,
|
||||
last_updated_at timestamp NOT NULL DEFAULT current_timestamp,
|
||||
CONSTRAINT url_uq UNIQUE (url),
|
||||
CONSTRAINT box_pk PRIMARY KEY (id)
|
||||
);
|
||||
-- ddl-end --
|
||||
ALTER TABLE public.box OWNER TO dev;
|
||||
-- ddl-end --
|
||||
|
||||
-- object: id_box | type: COLUMN --
|
||||
ALTER TABLE public.file ADD COLUMN id_box uuid;
|
||||
-- ddl-end --
|
||||
INSERT INTO public.box (id, url, edit_code) VALUES ('ce407978-b599-4cd6-9ddf-e9249280d321', '1', 'foobar');
|
||||
UPDATE public.file SET id_box = 'ce407978-b599-4cd6-9ddf-e9249280d321';
|
||||
ALTER TABLE public.file ALTER COLUMN id_box SET NOT NULL;
|
||||
|
||||
|
||||
-- object: md5 | type: COLUMN --
|
||||
ALTER TABLE public.file ADD COLUMN md5 text;
|
||||
-- ddl-end --
|
||||
UPDATE public.file SET md5 = '';
|
||||
ALTER TABLE public.file ALTER COLUMN md5 SET NOT NULL;
|
||||
|
||||
|
||||
|
||||
|
||||
-- [ Created foreign keys ] --
|
||||
-- object: box_fk | type: CONSTRAINT --
|
||||
-- ALTER TABLE public.file DROP CONSTRAINT IF EXISTS box_fk CASCADE;
|
||||
ALTER TABLE public.file ADD CONSTRAINT box_fk FOREIGN KEY (id_box)
|
||||
REFERENCES public.box (id) MATCH FULL
|
||||
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
-- ddl-end --
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
-- +goose Up
|
||||
-- Diff code generated with pgModeler (PostgreSQL Database Modeler)
|
||||
-- pgModeler version: 1.2.0-alpha1
|
||||
-- Diff date: 2025-04-19 17:49:54
|
||||
-- Source model: dev1
|
||||
-- Database: dev1
|
||||
-- PostgreSQL version: 17.0
|
||||
|
||||
-- [ Diff summary ]
|
||||
-- Dropped objects: 0
|
||||
-- Created objects: 0
|
||||
-- Changed objects: 2
|
||||
|
||||
SET search_path=public,pg_catalog;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
-- [ Changed objects ] --
|
||||
ALTER TABLE public.file ALTER COLUMN message TYPE varchar(8192);
|
||||
-- ddl-end --
|
||||
ALTER TABLE public.box ALTER COLUMN header TYPE varchar(16384);
|
||||
-- ddl-end --
|
||||
20
database/migrations/20250421015240_box_header_to_text.sql
Normal file
20
database/migrations/20250421015240_box_header_to_text.sql
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
-- +goose Up
|
||||
-- Diff code generated with pgModeler (PostgreSQL Database Modeler)
|
||||
-- pgModeler version: 1.2.0-alpha1
|
||||
-- Diff date: 2025-04-20 22:52:53
|
||||
-- Source model: dev1
|
||||
-- Database: dev1
|
||||
-- PostgreSQL version: 17.0
|
||||
|
||||
-- [ Diff summary ]
|
||||
-- Dropped objects: 0
|
||||
-- Created objects: 0
|
||||
-- Changed objects: 1
|
||||
|
||||
SET search_path=public,pg_catalog;
|
||||
-- ddl-end --
|
||||
|
||||
|
||||
-- [ Changed objects ] --
|
||||
ALTER TABLE public.box ALTER COLUMN header TYPE text;
|
||||
-- ddl-end --
|
||||
47
database/model.go
Normal file
47
database/model.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type Model interface {
|
||||
Id() (int, error)
|
||||
SetId(int)
|
||||
Saved() bool
|
||||
}
|
||||
|
||||
type identity struct {
|
||||
id int
|
||||
saved bool
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNotSaved = fmt.Errorf("primary key id não foi salva")
|
||||
)
|
||||
|
||||
func (p *identity) Id() (int, error) {
|
||||
if !p.saved {
|
||||
return 0, ErrNotSaved
|
||||
}
|
||||
return p.id, nil
|
||||
}
|
||||
|
||||
func (p *identity) SetId(i int) {
|
||||
p.id = i
|
||||
p.saved = true
|
||||
}
|
||||
|
||||
func (p *identity) Saved() bool {
|
||||
return p.saved
|
||||
}
|
||||
|
||||
func checkPasswordHash(password, hash string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
}
|
||||
|
||||
func HashPassword(password string) (string, error) {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
|
||||
return string(bytes), err
|
||||
}
|
||||
153
database/pgmodeler/model.dbm
Normal file
153
database/pgmodeler/model.dbm
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
CAUTION: Do not modify this file unless you know what you are doing.
|
||||
Unexpected results may occur if the code is changed deliberately.
|
||||
-->
|
||||
<dbmodel pgmodeler-ver="1.2.0-alpha1" use-changelog="false" max-obj-count="4"
|
||||
last-position="0,29" last-zoom="1"
|
||||
default-schema="public" default-owner="dev"
|
||||
layers="Default layer"
|
||||
active-layers="0"
|
||||
layer-name-colors="#000000"
|
||||
layer-rect-colors="#b4b4b4"
|
||||
show-layer-names="false" show-layer-rects="false">
|
||||
<role name="dev"
|
||||
superuser="true"
|
||||
createdb="true"
|
||||
replication="true"
|
||||
createrole="true"
|
||||
inherit="true"
|
||||
login="true"
|
||||
bypassrls="true"
|
||||
password="********"
|
||||
sql-disabled="true">
|
||||
</role>
|
||||
|
||||
<database name="dev1" encoding="UTF8" lc-collate="en_US.utf8" lc-ctype="en_US.utf8" is-template="false" allow-conns="true">
|
||||
<role name="dev"/>
|
||||
<tablespace name="pg_default"/>
|
||||
</database>
|
||||
|
||||
<schema name="public" layers="0" rect-visible="true" fill-color="#e1e1e1" name-color="#000000" sql-disabled="true">
|
||||
</schema>
|
||||
|
||||
<table name="goose_db_version" layers="0" collapse-mode="2" max-obj-count="4" z-value="0">
|
||||
<schema name="public"/>
|
||||
<role name="dev"/>
|
||||
<position x="440" y="300"/>
|
||||
<column name="id" not-null="true"
|
||||
identity-type="BY DEFAULT" start="1" increment="1" min-value="1" max-value="2147483647" cache="1">
|
||||
<type name="integer" length="0"/>
|
||||
</column>
|
||||
<column name="version_id" not-null="true">
|
||||
<type name="bigint" length="0"/>
|
||||
</column>
|
||||
<column name="is_applied" not-null="true">
|
||||
<type name="boolean" length="0"/>
|
||||
</column>
|
||||
<column name="tstamp" not-null="true" default-value="now()">
|
||||
<type name="timestamp" length="0"/>
|
||||
</column>
|
||||
<constraint name="goose_db_version_pkey" type="pk-constr" table="public.goose_db_version">
|
||||
<columns names="id" ref-type="src-columns"/>
|
||||
</constraint>
|
||||
</table>
|
||||
|
||||
<sequence name="goose_db_version_id_seq" cycle="false" start="1" increment="1" min-value="1" max-value="2147483647" cache="1" sql-disabled="true">
|
||||
<schema name="public"/>
|
||||
<role name="dev"/>
|
||||
</sequence>
|
||||
|
||||
<sequence name="usuario_id_seq" cycle="false" start="1" increment="1" min-value="1" max-value="2147483647" cache="1" sql-disabled="true">
|
||||
<schema name="public"/>
|
||||
<role name="dev"/>
|
||||
</sequence>
|
||||
|
||||
<table name="file" layers="0" collapse-mode="2" max-obj-count="12" z-value="0">
|
||||
<schema name="public"/>
|
||||
<role name="dev"/>
|
||||
<position x="1160" y="520"/>
|
||||
<column name="id" not-null="true">
|
||||
<type name="uuid" length="0"/>
|
||||
</column>
|
||||
<column name="name" not-null="true">
|
||||
<type name="varchar" length="256"/>
|
||||
</column>
|
||||
<column name="size" not-null="true">
|
||||
<type name="integer" length="0"/>
|
||||
</column>
|
||||
<column name="created_at" not-null="true">
|
||||
<type name="timestamp" length="0"/>
|
||||
</column>
|
||||
<column name="mime" not-null="true">
|
||||
<type name="text" length="0"/>
|
||||
</column>
|
||||
<column name="message">
|
||||
<type name="varchar" length="8192"/>
|
||||
</column>
|
||||
<column name="width">
|
||||
<type name="smallint" length="0"/>
|
||||
</column>
|
||||
<column name="height">
|
||||
<type name="smallint" length="0"/>
|
||||
</column>
|
||||
<column name="md5" not-null="true">
|
||||
<type name="text" length="0"/>
|
||||
</column>
|
||||
<constraint name="file_pk" type="pk-constr" table="public.file">
|
||||
<columns names="id" ref-type="src-columns"/>
|
||||
</constraint>
|
||||
|
||||
<customidxs object-type="column">
|
||||
<object name="id_box" index="8"/>
|
||||
</customidxs>
|
||||
<customidxs object-type="constraint">
|
||||
<object name="box_fk" index="1"/>
|
||||
</customidxs></table>
|
||||
|
||||
<table name="box" layers="0" collapse-mode="2" max-obj-count="9" z-value="0">
|
||||
<schema name="public"/>
|
||||
<role name="dev"/>
|
||||
<position x="560" y="560"/>
|
||||
<column name="id" not-null="true">
|
||||
<type name="uuid" length="0"/>
|
||||
</column>
|
||||
<column name="url" not-null="true">
|
||||
<type name="varchar" length="256"/>
|
||||
</column>
|
||||
<column name="edit_code" not-null="true">
|
||||
<type name="varchar" length="64"/>
|
||||
</column>
|
||||
<column name="header">
|
||||
<type name="text" length="16384"/>
|
||||
</column>
|
||||
<column name="private" not-null="true" default-value="false">
|
||||
<type name="boolean" length="0"/>
|
||||
</column>
|
||||
<column name="moderation" not-null="true" default-value="false">
|
||||
<type name="boolean" length="0"/>
|
||||
</column>
|
||||
<column name="created_at" not-null="true" default-value="current_timestamp">
|
||||
<type name="timestamp" length="0"/>
|
||||
</column>
|
||||
<column name="last_updated_at" not-null="true" default-value="current_timestamp">
|
||||
<type name="timestamp" length="0"/>
|
||||
</column>
|
||||
<constraint name="url_uq" type="uq-constr" table="public.box">
|
||||
<columns names="url" ref-type="src-columns"/>
|
||||
</constraint>
|
||||
<constraint name="box_pk" type="pk-constr" table="public.box">
|
||||
<columns names="id" ref-type="src-columns"/>
|
||||
</constraint>
|
||||
</table>
|
||||
|
||||
<relationship name="box_has_many_file" type="rel1n" layers="0"
|
||||
src-col-pattern="{sc}_{st}"
|
||||
pk-pattern="{dt}_pk" uq-pattern="{dt}_uq"
|
||||
src-fk-pattern="{st}_fk"
|
||||
custom-color="#ccd394"
|
||||
src-table="public.box"
|
||||
dst-table="public.file"
|
||||
src-required="true" dst-required="false"/>
|
||||
|
||||
</dbmodel>
|
||||
69
database/usuario.go
Normal file
69
database/usuario.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Usuario struct {
|
||||
identity
|
||||
Email string
|
||||
Nome string
|
||||
// senhaHash string `json:"-"`
|
||||
Ctime time.Time
|
||||
}
|
||||
|
||||
func VerifyUser(tx *sql.Tx, email, senha string) (*Usuario, error) {
|
||||
row := tx.QueryRow(
|
||||
`SELECT id, nome, ctime, senha_hash FROM usuario WHERE email = $1 LIMIT 1`,
|
||||
email,
|
||||
)
|
||||
if row.Err() != nil {
|
||||
return nil, row.Err()
|
||||
}
|
||||
usuario := &Usuario{
|
||||
Email: email,
|
||||
}
|
||||
|
||||
var id int
|
||||
var senhaHash string
|
||||
err := row.Scan(
|
||||
&id,
|
||||
&usuario.Nome,
|
||||
&usuario.Ctime,
|
||||
&senhaHash,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usuario.SetId(id)
|
||||
|
||||
if err = checkPasswordHash(senha, senhaHash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return usuario, nil
|
||||
}
|
||||
|
||||
func FindUsuarioByID(tx *sql.Tx, id int) (*Usuario, error) {
|
||||
row := tx.QueryRow(
|
||||
`SELECT id, nome, email, ctime FROM usuario WHERE id = $1 LIMIT 1`,
|
||||
id,
|
||||
)
|
||||
if row.Err() != nil {
|
||||
return nil, row.Err()
|
||||
}
|
||||
usuario := &Usuario{}
|
||||
err := row.Scan(
|
||||
&id,
|
||||
&usuario.Nome,
|
||||
&usuario.Email,
|
||||
&usuario.Ctime,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
usuario.SetId(id)
|
||||
return usuario, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue