Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add manual migrations using gormigration #148

Merged
merged 3 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: 4 additions & 3 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,11 @@ 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{})
err = migrations.Migrate(db)
if err != nil {
log.Fatalf("Failed migrate DB %v", err)
log.Fatalf("Migration failed: %v", err)
}
log.Println("Any pending migrations ran successfully")

if cfg.NostrSecretKey == "" {
if cfg.LNBackendType == AlbyBackendType {
Expand Down
42 changes: 42 additions & 0 deletions migrations/202309271616_initial_migration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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

var initialMigrations = map[string]string {
"postgres": initialMigrationPostgres,
"sqlite": initialMigrationSqlite,
}

// Initial migration
var _202309271616_initial_migration = &gormigrate.Migration {
ID: "202309271616_initial_migration",
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
initialMigration := initialMigrations[tx.Dialector.Name()]
if initialMigration == "" {
log.Fatalf("unsupported database type: %s", tx.Dialector.Name())
}

return tx.Exec(initialMigration).Error
}

return nil
},
Rollback: func(tx *gorm.DB) error {
return nil;
},
}
17 changes: 17 additions & 0 deletions migrations/202309271617_fix_preimage_null.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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_fix_preimage_null = &gormigrate.Migration {
ID: "202309271617_fix_preimage_null",
Migrate: func(tx *gorm.DB) error {
return tx.Table("payments").Where("preimage = ?", "").Update("preimage", nil).Error;
},
Rollback: func(tx *gorm.DB) error {
return nil;
},
}
29 changes: 29 additions & 0 deletions migrations/202309271618_add_payment_sum_index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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_add_payment_sum_index = &gormigrate.Migration {
ID: "202309271618_add_payment_sum_index",
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())
}

return tx.Exec(sql).Error
},
Rollback: func(tx *gorm.DB) error {
return nil;
},
}
28 changes: 28 additions & 0 deletions migrations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 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 {
return DO_SOMETHING_HERE.Error;
},
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
rolznz marked this conversation as resolved.
Show resolved Hide resolved
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);
16 changes: 16 additions & 0 deletions migrations/initial_migration_sqlite.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CREATE TABLE `apps` (`id` integer,`user_id` integer,`name` text,`description` text,`nostr_pubkey` text,`created_at` datetime,`updated_at` datetime,PRIMARY KEY (`id`),CONSTRAINT `fk_users_apps` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`));
CREATE INDEX `idx_apps_nostr_pubkey` ON `apps`(`nostr_pubkey`);
CREATE INDEX `idx_apps_user_id` ON `apps`(`user_id`);
CREATE TABLE `app_permissions` (`id` integer,`app_id` integer,`request_method` text,`max_amount` integer,`budget_renewal` text,`expires_at` datetime,`created_at` datetime,`updated_at` datetime,PRIMARY KEY (`id`),CONSTRAINT `fk_app_permissions_app` FOREIGN KEY (`app_id`) REFERENCES `apps`(`id`) ON DELETE CASCADE);
CREATE INDEX `idx_app_permissions_request_method` ON `app_permissions`(`request_method`);
CREATE INDEX `idx_app_permissions_app_id` ON `app_permissions`(`app_id`);
CREATE TABLE `payments` (`id` integer,`app_id` integer,`nostr_event_id` integer,`amount` integer,`payment_request` text,`preimage` text,`created_at` datetime,`updated_at` datetime, `preimage2` text,PRIMARY KEY (`id`),CONSTRAINT `fk_payments_app` FOREIGN KEY (`app_id`) REFERENCES `apps`(`id`) ON DELETE CASCADE,CONSTRAINT `fk_payments_nostr_event` FOREIGN KEY (`nostr_event_id`) REFERENCES `nostr_events`(`id`));
CREATE INDEX `idx_payments_nostr_event_id` ON `payments`(`nostr_event_id`);
CREATE INDEX `idx_payments_app_id` ON `payments`(`app_id`);
CREATE TABLE `identities` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`privkey` text,PRIMARY KEY (`id`));
CREATE INDEX `idx_identities_deleted_at` ON `identities`(`deleted_at`);
CREATE TABLE IF NOT EXISTS "users" (`id` integer,`alby_identifier` text UNIQUE,`access_token` text,`refresh_token` text,`email` text,`expiry` datetime,`lightning_address` text,`created_at` datetime,`updated_at` datetime,PRIMARY KEY (`id`));
CREATE UNIQUE INDEX `idx_users_alby_identifier` ON `users`(`alby_identifier`);
CREATE TABLE IF NOT EXISTS "nostr_events" (`id` integer,`app_id` integer,`nostr_id` text UNIQUE,`reply_id` text,`content` text,`state` text,`replied_at` datetime,`created_at` datetime,`updated_at` datetime,PRIMARY KEY (`id`),CONSTRAINT `fk_nostr_events_app` FOREIGN KEY (`app_id`) REFERENCES `apps`(`id`) ON DELETE CASCADE);
CREATE UNIQUE INDEX `idx_nostr_events_nostr_id` ON `nostr_events`(`nostr_id`);
CREATE INDEX `idx_nostr_events_app_id` ON `nostr_events`(`app_id`);
Loading
Loading