Skip to content

Commit

Permalink
feat: add manual migrations using gormigration
Browse files Browse the repository at this point in the history
- initial migration (postgres and sqlite)
- update preimage to be NULL instead of ''
- add composite index for summing payments in budget period

also fixes preimage to be NULL by default and removes gorm model hints
  • Loading branch information
rolznz committed Sep 27, 2023
1 parent 437be81 commit 9c9311f
Show file tree
Hide file tree
Showing 12 changed files with 382 additions and 26 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
google.golang.org/grpc v1.53.0
gopkg.in/DataDog/dd-trace-go.v1 v1.47.0
gopkg.in/macaroon.v2 v2.1.0
gorm.io/gorm v1.25.0
gorm.io/gorm v1.25.4
)

require (
Expand Down Expand Up @@ -55,6 +55,7 @@ require (
github.com/fergusstrange/embedded-postgres v1.19.0 // indirect
github.com/glebarez/go-sqlite v1.20.3 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gormigrate/gormigrate/v2 v2.1.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-macaroon-bakery/macaroonpb v1.0.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gormigrate/gormigrate/v2 v2.1.1 h1:eGS0WTFRV30r103lU8JNXY27KbviRnqqIDobW3EV3iY=
github.com/go-gormigrate/gormigrate/v2 v2.1.1/go.mod h1:L7nJ620PFDKei9QOhJzqA8kRCk+E3UbV2f5gv+1ndLc=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
Expand Down Expand Up @@ -1319,6 +1321,8 @@ gorm.io/driver/sqlserver v1.0.4 h1:V15fszi0XAo7fbx3/cF50ngshDSN4QT0MXpWTylyPTY=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU=
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
2 changes: 1 addition & 1 deletion handle_payment_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (svc *Service) HandlePayInvoiceEvent(ctx context.Context, request *Nip47Req
},
}, ss)
}
payment.Preimage = preimage
payment.Preimage = &preimage
nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED
svc.db.Save(&nostrEvent)
svc.db.Save(&payment)
Expand Down
7 changes: 2 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

echologrus "github.com/davrux/echo-logrus/v4"
"github.com/getAlby/nostr-wallet-connect/migrations"
"github.com/glebarez/sqlite"
"github.com/joho/godotenv"
"github.com/kelseyhightower/envconfig"
Expand Down Expand Up @@ -79,11 +80,7 @@ func main() {
sqlDb.SetMaxIdleConns(cfg.DatabaseMaxIdleConns)
sqlDb.SetConnMaxLifetime(time.Duration(cfg.DatabaseConnMaxLifetime) * time.Second)

// Migrate the schema
err = db.AutoMigrate(&User{}, &App{}, &AppPermission{}, &NostrEvent{}, &Payment{}, &Identity{})
if err != nil {
log.Fatalf("Failed migrate DB %v", err)
}
migrations.Migrate(db)

if cfg.NostrSecretKey == "" {
if cfg.LNBackendType == AlbyBackendType {
Expand Down
45 changes: 45 additions & 0 deletions migrations/202309271616.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package migrations

import (
_ "embed"
"log"

"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

//go:embed initial_migration_postgres.sql
var initialMigrationPostgres string
//go:embed initial_migration_sqlite.sql
var initialMigrationSqlite string

// Initial migration
var _202309271616 = &gormigrate.Migration {
ID: "202309271616",
Migrate: func(tx *gorm.DB) error {

// only execute migration if apps table doesn't exist
err := tx.Exec("Select * from apps").Error;
if err != nil {
// find which initial migration should be executed
var initialMigration string
if tx.Dialector.Name() == "postgres" {
initialMigration = initialMigrationPostgres
} else if tx.Dialector.Name() == "sqlite" {
initialMigration = initialMigrationSqlite
} else {
log.Fatalf("unsupported database type: %s", tx.Dialector.Name())
}

err := tx.Exec(initialMigration).Error
if err != nil {
return err
}
}

return nil
},
Rollback: func(tx *gorm.DB) error {
return nil;
},
}
22 changes: 22 additions & 0 deletions migrations/202309271617.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package migrations

import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

// Update payments with preimage as an empty string to use NULL instead
var _202309271617 = &gormigrate.Migration {
ID: "202309271617",
Migrate: func(tx *gorm.DB) error {
err := tx.Table("payments").Where("preimage = ?", "").Update("preimage", nil).Error;

if err != nil {
return err
}
return nil
},
Rollback: func(tx *gorm.DB) error {
return nil;
},
}
34 changes: 34 additions & 0 deletions migrations/202309271618.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package migrations

import (
"log"

"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

// Create a composite index to improve performance of summing payments in the current budget period
var _202309271618 = &gormigrate.Migration {
ID: "202309271618",
Migrate: func(tx *gorm.DB) error {

var sql string
if tx.Dialector.Name() == "postgres" {
sql = "CREATE INDEX idx_payment_sum ON payments USING btree (app_id, preimage, created_at) INCLUDE(amount)"
} else if tx.Dialector.Name() == "sqlite" {
sql = "CREATE INDEX idx_payment_sum ON payments (app_id, preimage, created_at)"
} else {
log.Fatalf("unsupported database type: %s", tx.Dialector.Name())
}

err := tx.Exec(sql).Error
if err != nil {
return err
}

return nil
},
Rollback: func(tx *gorm.DB) error {
return nil;
},
}
33 changes: 33 additions & 0 deletions migrations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Creating a new migration

1. Create a new file based on the current date and time (see existing migration format)
2. Copy the following code and update MY_ID_HERE and MY_COMMENT_HERE and DO_SOMETHING_HERE
3. Add the ID to the list of migrations in migrate.go
4. If possible, add a rollback function.

*For Postgres/Sqlite specific migrations, see the [initial migration](202309271616.go)*

```go
package migrations

import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

// MY_COMMENT_HERE
var _MY_ID_HERE = &gormigrate.Migration {
ID: "MY_ID_HERE",
Migrate: func(tx *gorm.DB) error {
err := DO_SOMETHING_HERE.Error;

if err != nil {
return err
}
return nil
},
Rollback: func(tx *gorm.DB) error {
return nil;
},
}
```
182 changes: 182 additions & 0 deletions migrations/initial_migration_postgres.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
CREATE TABLE app_permissions (
id bigint NOT NULL,
app_id bigint,
request_method text,
max_amount bigint,
budget_renewal text,
expires_at timestamp with time zone,
created_at timestamp with time zone,
updated_at timestamp with time zone
);

CREATE SEQUENCE app_permissions_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER SEQUENCE app_permissions_id_seq OWNED BY app_permissions.id;

CREATE TABLE apps (
id bigint NOT NULL,
user_id bigint,
name text,
description text,
nostr_pubkey text,
created_at timestamp with time zone,
updated_at timestamp with time zone
);

CREATE SEQUENCE apps_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER SEQUENCE apps_id_seq OWNED BY apps.id;

CREATE TABLE identities (
id bigint NOT NULL,
created_at timestamp with time zone,
updated_at timestamp with time zone,
deleted_at timestamp with time zone,
privkey text
);

CREATE SEQUENCE identities_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER SEQUENCE identities_id_seq OWNED BY identities.id;

CREATE TABLE nostr_events (
id bigint NOT NULL,
app_id bigint,
nostr_id text,
reply_id text,
content text,
state text,
replied_at timestamp with time zone,
created_at timestamp with time zone,
updated_at timestamp with time zone
);

CREATE SEQUENCE nostr_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER SEQUENCE nostr_events_id_seq OWNED BY nostr_events.id;

CREATE TABLE payments (
id bigint NOT NULL,
app_id bigint,
nostr_event_id bigint,
amount bigint,
payment_request text,
preimage text,
created_at timestamp with time zone,
updated_at timestamp with time zone
);

CREATE SEQUENCE payments_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER SEQUENCE payments_id_seq OWNED BY payments.id;

CREATE TABLE users (
id bigint NOT NULL,
alby_identifier text,
access_token text,
refresh_token text,
email text,
expiry timestamp with time zone,
lightning_address text,
created_at timestamp with time zone,
updated_at timestamp with time zone
);

CREATE SEQUENCE users_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;

ALTER SEQUENCE users_id_seq OWNED BY users.id;

ALTER TABLE ONLY app_permissions ALTER COLUMN id SET DEFAULT nextval('app_permissions_id_seq'::regclass);

ALTER TABLE ONLY apps ALTER COLUMN id SET DEFAULT nextval('apps_id_seq'::regclass);

ALTER TABLE ONLY identities ALTER COLUMN id SET DEFAULT nextval('identities_id_seq'::regclass);

ALTER TABLE ONLY nostr_events ALTER COLUMN id SET DEFAULT nextval('nostr_events_id_seq'::regclass);

ALTER TABLE ONLY payments ALTER COLUMN id SET DEFAULT nextval('payments_id_seq'::regclass);

ALTER TABLE ONLY users ALTER COLUMN id SET DEFAULT nextval('users_id_seq'::regclass);

ALTER TABLE ONLY app_permissions
ADD CONSTRAINT app_permissions_pkey PRIMARY KEY (id);

ALTER TABLE ONLY apps
ADD CONSTRAINT apps_pkey PRIMARY KEY (id);

ALTER TABLE ONLY identities
ADD CONSTRAINT identities_pkey PRIMARY KEY (id);

ALTER TABLE ONLY nostr_events
ADD CONSTRAINT nostr_events_pkey PRIMARY KEY (id);

ALTER TABLE ONLY payments
ADD CONSTRAINT payments_pkey PRIMARY KEY (id);

ALTER TABLE ONLY users
ADD CONSTRAINT users_pkey PRIMARY KEY (id);

CREATE INDEX idx_app_permissions_app_id ON app_permissions USING btree (app_id);

CREATE INDEX idx_app_permissions_request_method ON app_permissions USING btree (request_method);

CREATE INDEX idx_apps_nostr_pubkey ON apps USING btree (nostr_pubkey);

CREATE INDEX idx_apps_user_id ON apps USING btree (user_id);

CREATE INDEX idx_identities_deleted_at ON identities USING btree (deleted_at);

CREATE INDEX idx_nostr_events_app_id ON nostr_events USING btree (app_id);

CREATE UNIQUE INDEX idx_nostr_events_nostr_id ON nostr_events USING btree (nostr_id);

CREATE INDEX idx_payments_app_id ON payments USING btree (app_id);

CREATE INDEX idx_payments_nostr_event_id ON payments USING btree (nostr_event_id);

CREATE UNIQUE INDEX idx_users_alby_identifier ON users USING btree (alby_identifier);

ALTER TABLE ONLY app_permissions
ADD CONSTRAINT fk_app_permissions_app FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE;

ALTER TABLE ONLY nostr_events
ADD CONSTRAINT fk_nostr_events_app FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE;

ALTER TABLE ONLY payments
ADD CONSTRAINT fk_payments_app FOREIGN KEY (app_id) REFERENCES apps(id) ON DELETE CASCADE;

ALTER TABLE ONLY payments
ADD CONSTRAINT fk_payments_nostr_event FOREIGN KEY (nostr_event_id) REFERENCES nostr_events(id);

ALTER TABLE ONLY apps
ADD CONSTRAINT fk_users_apps FOREIGN KEY (user_id) REFERENCES users(id);
Loading

0 comments on commit 9c9311f

Please sign in to comment.