diff --git a/src/migration/migration.go b/src/migration/migration.go index 305f2a7a..76cf5865 100644 --- a/src/migration/migration.go +++ b/src/migration/migration.go @@ -102,9 +102,9 @@ func getSortedMigrationVersions() []types.MigrationVersion { return allVersions } -func getCurrentVersion(conn *pgx.Conn) (types.MigrationVersion, error) { +func getCurrentVersion(ctx context.Context, conn *pgx.Conn) (types.MigrationVersion, error) { var currentVersion time.Time - row := conn.QueryRow(context.Background(), "SELECT version FROM hmn_migration") + row := conn.QueryRow(ctx, "SELECT version FROM hmn_migration") err := row.Scan(¤tVersion) if err != nil { return types.MigrationVersion{}, err @@ -115,10 +115,12 @@ func getCurrentVersion(conn *pgx.Conn) (types.MigrationVersion, error) { } func ListMigrations() { - conn := db.NewConn() - defer conn.Close(context.Background()) + ctx := context.Background() - currentVersion, _ := getCurrentVersion(conn) + conn := db.NewConn() + defer conn.Close(ctx) + + currentVersion, _ := getCurrentVersion(ctx, conn) for _, version := range getSortedMigrationVersions() { migration := migrations.All[version] indicator := " " @@ -130,11 +132,13 @@ func ListMigrations() { } func Migrate(targetVersion types.MigrationVersion) { + ctx := context.Background() // In the future, this could actually do something cool. + conn := db.NewConn() - defer conn.Close(context.Background()) + defer conn.Close(ctx) // create migration table - _, err := conn.Exec(context.Background(), ` + _, err := conn.Exec(ctx, ` CREATE TABLE IF NOT EXISTS hmn_migration ( version TIMESTAMP WITH TIME ZONE ) @@ -144,21 +148,21 @@ func Migrate(targetVersion types.MigrationVersion) { } // ensure there is a row - row := conn.QueryRow(context.Background(), "SELECT COUNT(*) FROM hmn_migration") + row := conn.QueryRow(ctx, "SELECT COUNT(*) FROM hmn_migration") var numRows int err = row.Scan(&numRows) if err != nil { panic(err) } if numRows < 1 { - _, err := conn.Exec(context.Background(), "INSERT INTO hmn_migration (version) VALUES ($1)", time.Time{}) + _, err := conn.Exec(ctx, "INSERT INTO hmn_migration (version) VALUES ($1)", time.Time{}) if err != nil { panic(fmt.Errorf("failed to insert initial migration row: %w", err)) } } // run migrations - currentVersion, err := getCurrentVersion(conn) + currentVersion, err := getCurrentVersion(ctx, conn) if err != nil { panic(fmt.Errorf("failed to get current version: %w", err)) } @@ -196,25 +200,25 @@ func Migrate(targetVersion types.MigrationVersion) { migration := migrations.All[version] fmt.Printf("Applying migration %v (%v)\n", version, migration.Name()) - tx, err := conn.Begin(context.Background()) + tx, err := conn.Begin(ctx) if err != nil { panic(fmt.Errorf("failed to start transaction: %w", err)) } - defer tx.Rollback(context.Background()) + defer tx.Rollback(ctx) - err = migration.Up(tx) + err = migration.Up(ctx, tx) if err != nil { fmt.Printf("MIGRATION FAILED for migration %v.\n", version) fmt.Printf("Error: %v\n", err) return } - _, err = tx.Exec(context.Background(), "UPDATE hmn_migration SET version = $1", version) + _, err = tx.Exec(ctx, "UPDATE hmn_migration SET version = $1", version) if err != nil { panic(fmt.Errorf("failed to update version in migrations table: %w", err)) } - err = tx.Commit(context.Background()) + err = tx.Commit(ctx) if err != nil { panic(fmt.Errorf("failed to commit transaction: %w", err)) } @@ -228,27 +232,27 @@ func Migrate(targetVersion types.MigrationVersion) { previousVersion = allVersions[i-1] } - tx, err := conn.Begin(context.Background()) + tx, err := conn.Begin(ctx) if err != nil { panic(fmt.Errorf("failed to start transaction: %w", err)) } - defer tx.Rollback(context.Background()) + defer tx.Rollback(ctx) fmt.Printf("Rolling back migration %v\n", version) migration := migrations.All[version] - err = migration.Down(tx) + err = migration.Down(ctx, tx) if err != nil { fmt.Printf("MIGRATION FAILED for migration %v.\n", version) fmt.Printf("Error: %v\n", err) return } - _, err = tx.Exec(context.Background(), "UPDATE hmn_migration SET version = $1", previousVersion) + _, err = tx.Exec(ctx, "UPDATE hmn_migration SET version = $1", previousVersion) if err != nil { panic(fmt.Errorf("failed to update version in migrations table: %w", err)) } - err = tx.Commit(context.Background()) + err = tx.Commit(ctx) if err != nil { panic(fmt.Errorf("failed to commit transaction: %w", err)) } diff --git a/src/migration/migrationTemplate.txt b/src/migration/migrationTemplate.txt index 302f35ef..d8490be4 100644 --- a/src/migration/migrationTemplate.txt +++ b/src/migration/migrationTemplate.txt @@ -25,10 +25,10 @@ func (m %NAME%) Description() string { return %DESCRIPTION% } -func (m %NAME%) Up(tx pgx.Tx) error { +func (m %NAME%) Up(ctx context.Context, tx pgx.Tx) error { panic("Implement me") } -func (m %NAME%) Down(tx pgx.Tx) error { +func (m %NAME%) Down(ctx context.Context, tx pgx.Tx) error { panic("Implement me") } diff --git a/src/migration/migrations/2021-03-10T051621Z_Initial.go b/src/migration/migrations/2021-03-10T051621Z_Initial.go index 203a95ec..637a7b78 100644 --- a/src/migration/migrations/2021-03-10T051621Z_Initial.go +++ b/src/migration/migrations/2021-03-10T051621Z_Initial.go @@ -26,8 +26,8 @@ func (m Initial) Description() string { return "Creates all the tables from the old site" } -func (m Initial) Up(tx pgx.Tx) error { - _, err := tx.Exec(context.Background(), ` +func (m Initial) Up(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, ` -- -- PostgreSQL database dump -- @@ -4648,6 +4648,6 @@ func (m Initial) Up(tx pgx.Tx) error { return nil } -func (m Initial) Down(tx pgx.Tx) error { +func (m Initial) Down(ctx context.Context, tx pgx.Tx) error { panic("nope, ha ha, I'm the initial migration. how did you even run this function anyway") } diff --git a/src/migration/migrations/2021-03-26T033834Z_AddSessionTable.go b/src/migration/migrations/2021-03-26T033834Z_AddSessionTable.go index 8caba7d0..328307b4 100644 --- a/src/migration/migrations/2021-03-26T033834Z_AddSessionTable.go +++ b/src/migration/migrations/2021-03-26T033834Z_AddSessionTable.go @@ -26,8 +26,8 @@ func (m AddSessionTable) Description() string { return "Adds a session table to replace the Django session table" } -func (m AddSessionTable) Up(tx pgx.Tx) error { - _, err := tx.Exec(context.Background(), ` +func (m AddSessionTable) Up(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, ` CREATE TABLE sessions ( id VARCHAR(40) PRIMARY KEY, username VARCHAR(150) NOT NULL, @@ -37,8 +37,8 @@ func (m AddSessionTable) Up(tx pgx.Tx) error { return err } -func (m AddSessionTable) Down(tx pgx.Tx) error { - _, err := tx.Exec(context.Background(), ` +func (m AddSessionTable) Down(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, ` DROP TABLE sessions; `) return err diff --git a/src/migration/migrations/2021-03-28T152630Z_AllowLongerPasswordHashes.go b/src/migration/migrations/2021-03-28T152630Z_AllowLongerPasswordHashes.go index 2b22819a..6f1164d7 100644 --- a/src/migration/migrations/2021-03-28T152630Z_AllowLongerPasswordHashes.go +++ b/src/migration/migrations/2021-03-28T152630Z_AllowLongerPasswordHashes.go @@ -26,16 +26,16 @@ func (m AllowLongerPasswordHashes) Description() string { return "Increase the storage size limit on hashed passwords" } -func (m AllowLongerPasswordHashes) Up(tx pgx.Tx) error { - _, err := tx.Exec(context.Background(), ` +func (m AllowLongerPasswordHashes) Up(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, ` ALTER TABLE auth_user ALTER COLUMN password TYPE VARCHAR(256) `) return err } -func (m AllowLongerPasswordHashes) Down(tx pgx.Tx) error { - _, err := tx.Exec(context.Background(), ` +func (m AllowLongerPasswordHashes) Down(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, ` ALTER TABLE auth_user ALTER COLUMN password TYPE VARCHAR(128) `) diff --git a/src/migration/migrations/2021-04-09T000000Z_DeleteOrphanedData.go b/src/migration/migrations/2021-04-09T000000Z_DeleteOrphanedData.go index 3d95c641..ce3416a6 100644 --- a/src/migration/migrations/2021-04-09T000000Z_DeleteOrphanedData.go +++ b/src/migration/migrations/2021-04-09T000000Z_DeleteOrphanedData.go @@ -28,9 +28,9 @@ func (m DeleteOrphanedData) Description() string { return "Delete data that doesn't have other important associated records" } -func (m DeleteOrphanedData) Up(tx pgx.Tx) error { +func (m DeleteOrphanedData) Up(ctx context.Context, tx pgx.Tx) error { // Delete orphaned users (no member) - res, err := tx.Exec(context.Background(), ` + res, err := tx.Exec(ctx, ` DELETE FROM auth_user WHERE id IN ( @@ -58,7 +58,7 @@ func (m DeleteOrphanedData) Up(tx pgx.Tx) error { // Delete memberextended<->links joins for memberextendeds that are about to die // (my kingdom for ON DELETE CASCADE, I mean come on) - res, err = tx.Exec(context.Background(), ` + res, err = tx.Exec(ctx, ` DELETE FROM handmade_memberextended_links WHERE memberextended_id IN ( @@ -71,7 +71,7 @@ func (m DeleteOrphanedData) Up(tx pgx.Tx) error { fmt.Printf("Deleted %v memberextended<->links joins\n", res.RowsAffected()) // Delete orphaned memberextendeds (no member) - res, err = tx.Exec(context.Background(), ` + res, err = tx.Exec(ctx, ` DELETE FROM handmade_memberextended WHERE id IN ( @@ -84,7 +84,7 @@ func (m DeleteOrphanedData) Up(tx pgx.Tx) error { fmt.Printf("Deleted %v memberextendeds\n", res.RowsAffected()) // Delete orphaned links (no member or project) - res, err = tx.Exec(context.Background(), ` + res, err = tx.Exec(ctx, ` DELETE FROM handmade_links WHERE id IN ( @@ -106,6 +106,6 @@ func (m DeleteOrphanedData) Up(tx pgx.Tx) error { return nil } -func (m DeleteOrphanedData) Down(tx pgx.Tx) error { +func (m DeleteOrphanedData) Down(ctx context.Context, tx pgx.Tx) error { panic("Implement me") } diff --git a/src/migration/migrations/2021-04-11T195747Z_RemoveMemberAndExtended.go b/src/migration/migrations/2021-04-11T195747Z_RemoveMemberAndExtended.go index 066ae0d3..71ec342b 100644 --- a/src/migration/migrations/2021-04-11T195747Z_RemoveMemberAndExtended.go +++ b/src/migration/migrations/2021-04-11T195747Z_RemoveMemberAndExtended.go @@ -32,7 +32,7 @@ func (m RemoveMemberAndExtended) Description() string { return "Remove the member and member extended records, collapsing their data into users" } -func (m RemoveMemberAndExtended) Up(tx pgx.Tx) error { +func (m RemoveMemberAndExtended) Up(ctx context.Context, tx pgx.Tx) error { // Creates a column that will eventually be a foreign key to auth_user. createUserColumn := func(ctx context.Context, tx pgx.Tx, table string, before string, notNull bool) { nullConstraint := "" @@ -51,18 +51,18 @@ func (m RemoveMemberAndExtended) Up(tx pgx.Tx) error { } // Migrate a lot of simple foreign keys - createUserColumn(context.Background(), tx, "handmade_communicationchoice", "member_id", true) - createUserColumn(context.Background(), tx, "handmade_communicationsubcategory", "member_id", true) - createUserColumn(context.Background(), tx, "handmade_communicationsubthread", "member_id", true) - createUserColumn(context.Background(), tx, "handmade_discord", "member_id", true) - createUserColumn(context.Background(), tx, "handmade_categorylastreadinfo", "member_id", true) - createUserColumn(context.Background(), tx, "handmade_threadlastreadinfo", "member_id", true) - createUserColumn(context.Background(), tx, "handmade_posttextversion", "editor_id", false) - createUserColumn(context.Background(), tx, "handmade_post", "author_id", false) - createUserColumn(context.Background(), tx, "handmade_member_projects", "member_id", true) + createUserColumn(ctx, tx, "handmade_communicationchoice", "member_id", true) + createUserColumn(ctx, tx, "handmade_communicationsubcategory", "member_id", true) + createUserColumn(ctx, tx, "handmade_communicationsubthread", "member_id", true) + createUserColumn(ctx, tx, "handmade_discord", "member_id", true) + createUserColumn(ctx, tx, "handmade_categorylastreadinfo", "member_id", true) + createUserColumn(ctx, tx, "handmade_threadlastreadinfo", "member_id", true) + createUserColumn(ctx, tx, "handmade_posttextversion", "editor_id", false) + createUserColumn(ctx, tx, "handmade_post", "author_id", false) + createUserColumn(ctx, tx, "handmade_member_projects", "member_id", true) // Directly associate links with members - _, err := tx.Exec(context.Background(), ` + _, err := tx.Exec(ctx, ` ALTER TABLE handmade_links ADD COLUMN user_id INTEGER DEFAULT 99999, ADD COLUMN project_id INTEGER DEFAULT 99999; @@ -92,7 +92,7 @@ func (m RemoveMemberAndExtended) Up(tx pgx.Tx) error { return oops.New(err, "failed to associate links with members and projects") } - _, err = tx.Exec(context.Background(), ` + _, err = tx.Exec(ctx, ` ALTER TABLE auth_user -- From handmade_member -- ADD blurb VARCHAR(140) NOT NULL DEFAULT '', @@ -178,6 +178,6 @@ func (m RemoveMemberAndExtended) Up(tx pgx.Tx) error { return nil } -func (m RemoveMemberAndExtended) Down(tx pgx.Tx) error { +func (m RemoveMemberAndExtended) Down(ctx context.Context, tx pgx.Tx) error { panic("Implement me") } diff --git a/src/migration/migrations/2021-04-11T210958Z_RemoveMemberAndExtended2.go b/src/migration/migrations/2021-04-11T210958Z_RemoveMemberAndExtended2.go index d1ae5dde..3edf227e 100644 --- a/src/migration/migrations/2021-04-11T210958Z_RemoveMemberAndExtended2.go +++ b/src/migration/migrations/2021-04-11T210958Z_RemoveMemberAndExtended2.go @@ -32,7 +32,7 @@ func (m RemoveMemberAndExtended2) Description() string { return "Phase 2 of the above" } -func (m RemoveMemberAndExtended2) Up(tx pgx.Tx) error { +func (m RemoveMemberAndExtended2) Up(ctx context.Context, tx pgx.Tx) error { dropOldColumn := func(ctx context.Context, tx pgx.Tx, table string, before, after string, onDelete string) { _, err := tx.Exec(ctx, ` ALTER TABLE `+table+` @@ -48,17 +48,17 @@ func (m RemoveMemberAndExtended2) Up(tx pgx.Tx) error { } } - dropOldColumn(context.Background(), tx, "handmade_communicationchoice", "member_id", "user_id", "CASCADE") - dropOldColumn(context.Background(), tx, "handmade_communicationsubcategory", "member_id", "user_id", "CASCADE") - dropOldColumn(context.Background(), tx, "handmade_communicationsubthread", "member_id", "user_id", "CASCADE") - dropOldColumn(context.Background(), tx, "handmade_discord", "member_id", "hmn_user_id", "CASCADE") - dropOldColumn(context.Background(), tx, "handmade_categorylastreadinfo", "member_id", "user_id", "CASCADE") - dropOldColumn(context.Background(), tx, "handmade_threadlastreadinfo", "member_id", "user_id", "CASCADE") - dropOldColumn(context.Background(), tx, "handmade_posttextversion", "editor_id", "editor_id", "SET NULL") - dropOldColumn(context.Background(), tx, "handmade_post", "author_id", "author_id", "SET NULL") - dropOldColumn(context.Background(), tx, "handmade_member_projects", "member_id", "user_id", "SET NULL") + dropOldColumn(ctx, tx, "handmade_communicationchoice", "member_id", "user_id", "CASCADE") + dropOldColumn(ctx, tx, "handmade_communicationsubcategory", "member_id", "user_id", "CASCADE") + dropOldColumn(ctx, tx, "handmade_communicationsubthread", "member_id", "user_id", "CASCADE") + dropOldColumn(ctx, tx, "handmade_discord", "member_id", "hmn_user_id", "CASCADE") + dropOldColumn(ctx, tx, "handmade_categorylastreadinfo", "member_id", "user_id", "CASCADE") + dropOldColumn(ctx, tx, "handmade_threadlastreadinfo", "member_id", "user_id", "CASCADE") + dropOldColumn(ctx, tx, "handmade_posttextversion", "editor_id", "editor_id", "SET NULL") + dropOldColumn(ctx, tx, "handmade_post", "author_id", "author_id", "SET NULL") + dropOldColumn(ctx, tx, "handmade_member_projects", "member_id", "user_id", "SET NULL") - _, err := tx.Exec(context.Background(), ` + _, err := tx.Exec(ctx, ` ALTER TABLE handmade_member_projects RENAME TO handmade_user_projects; `) @@ -66,7 +66,7 @@ func (m RemoveMemberAndExtended2) Up(tx pgx.Tx) error { return oops.New(err, "failed to rename member projects table") } - _, err = tx.Exec(context.Background(), ` + _, err = tx.Exec(ctx, ` ALTER TABLE handmade_links ADD FOREIGN KEY (user_id) REFERENCES auth_user ON DELETE CASCADE, ALTER user_id DROP DEFAULT, @@ -87,7 +87,7 @@ func (m RemoveMemberAndExtended2) Up(tx pgx.Tx) error { } // And now, the moment you've all been waiting for: - _, err = tx.Exec(context.Background(), ` + _, err = tx.Exec(ctx, ` DROP TABLE handmade_member; DROP TABLE handmade_memberextended; `) @@ -96,7 +96,7 @@ func (m RemoveMemberAndExtended2) Up(tx pgx.Tx) error { } // And finally, a little cleanup. - _, err = tx.Exec(context.Background(), ` + _, err = tx.Exec(ctx, ` ALTER INDEX handmade_member_projects_b098ad43 RENAME TO user_projects_btree; ALTER SEQUENCE handmade_member_projects_id_seq RENAME TO user_projects_id_seq; ALTER INDEX handmade_member_projects_pkey RENAME TO user_projects_pkey; @@ -108,6 +108,6 @@ func (m RemoveMemberAndExtended2) Up(tx pgx.Tx) error { return nil } -func (m RemoveMemberAndExtended2) Down(tx pgx.Tx) error { +func (m RemoveMemberAndExtended2) Down(ctx context.Context, tx pgx.Tx) error { panic("Implement me") } diff --git a/src/migration/migrations/2021-04-23T000000Z_FixOrphanedPostTextVersions.go b/src/migration/migrations/2021-04-23T000000Z_FixOrphanedPostTextVersions.go new file mode 100644 index 00000000..c14f9362 --- /dev/null +++ b/src/migration/migrations/2021-04-23T000000Z_FixOrphanedPostTextVersions.go @@ -0,0 +1,52 @@ +package migrations + +import ( + "context" + "time" + + "git.handmade.network/hmn/hmn/src/migration/types" + "git.handmade.network/hmn/hmn/src/oops" + "github.com/jackc/pgx/v4" +) + +func init() { + registerMigration(FixOrphanedPostTextVersions{}) +} + +type FixOrphanedPostTextVersions struct{} + +func (m FixOrphanedPostTextVersions) Version() types.MigrationVersion { + return types.MigrationVersion(time.Date(2021, 4, 23, 0, 0, 0, 0, time.UTC)) +} + +func (m FixOrphanedPostTextVersions) Name() string { + return "FixOrphanedPostTextVersions" +} + +func (m FixOrphanedPostTextVersions) Description() string { + return "Set the post_id on posttextversions that lost track of their parents" +} + +func (m FixOrphanedPostTextVersions) Up(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, ` + UPDATE handmade_posttextversion AS tver + SET + post_id = ( + SELECT id + FROM handmade_post AS post + WHERE + post.current_id = tver.id + ) + WHERE + tver.post_id IS NULL + `) + if err != nil { + return oops.New(err, "failed to fix up half-orphaned posttextversions") + } + + return nil +} + +func (m FixOrphanedPostTextVersions) Down(ctx context.Context, tx pgx.Tx) error { + panic("Implement me") +} diff --git a/src/migration/migrations/2021-04-23T021147Z_RemovePostText.go b/src/migration/migrations/2021-04-23T021147Z_RemovePostText.go new file mode 100644 index 00000000..6a24694d --- /dev/null +++ b/src/migration/migrations/2021-04-23T021147Z_RemovePostText.go @@ -0,0 +1,97 @@ +package migrations + +import ( + "context" + "time" + + "git.handmade.network/hmn/hmn/src/migration/types" + "git.handmade.network/hmn/hmn/src/oops" + "github.com/jackc/pgx/v4" +) + +func init() { + registerMigration(RemovePostText{}) +} + +type RemovePostText struct{} + +func (m RemovePostText) Version() types.MigrationVersion { + return types.MigrationVersion(time.Date(2021, 4, 23, 2, 11, 47, 0, time.UTC)) +} + +func (m RemovePostText) Name() string { + return "RemovePostText" +} + +func (m RemovePostText) Description() string { + return "Collapse handmade_posttext and handmade_posttextversion into one table" +} + +func (m RemovePostText) Up(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, ` + CREATE TABLE handmade_postversion ( + id INT PRIMARY KEY, + post_id INT NOT NULL REFERENCES handmade_post(id) ON DELETE CASCADE, + + text_raw TEXT NOT NULL, + text_parsed TEXT NOT NULL, + parser INT NOT NULL, + + edit_ip INET, + edit_date TIMESTAMP WITH TIME ZONE NOT NULL, + edit_reason VARCHAR(255) NOT NULL DEFAULT '', + editor_id INT REFERENCES auth_user(id) ON DELETE SET NULL + ); + `) + if err != nil { + return oops.New(err, "failed to create new postversion table") + } + + _, err = tx.Exec(ctx, ` + INSERT INTO handmade_postversion + SELECT + tver.id, + tver.post_id, + + t.text, + t.textparsed, + t.parser, + + tver.editip, + COALESCE(tver.editdate, p.postdate), + COALESCE(tver.editreason, ''), + tver.editor_id + FROM + handmade_posttextversion AS tver + JOIN handmade_posttext AS t ON tver.text_id = t.id + JOIN handmade_post AS p ON tver.post_id = p.id + WHERE + tver.post_id IS NOT NULL + `) + if err != nil { + return oops.New(err, "failed to create postversions") + } + + _, err = tx.Exec(ctx, ` + ALTER TABLE handmade_post + DROP CONSTRAINT handmade_post_current_id_762211b7_fk_handmade_, + ADD FOREIGN KEY (current_id) REFERENCES handmade_postversion ON DELETE RESTRICT; + `) + if err != nil { + return oops.New(err, "failed to drop old current constraint") + } + + _, err = tx.Exec(ctx, ` + DROP TABLE handmade_posttextversion; + DROP TABLE handmade_posttext; + `) + if err != nil { + return oops.New(err, "failed to drop tables") + } + + return nil +} + +func (m RemovePostText) Down(ctx context.Context, tx pgx.Tx) error { + panic("Implement me") +} diff --git a/src/migration/types/types.go b/src/migration/types/types.go index 4b521bdf..3a16dd50 100644 --- a/src/migration/types/types.go +++ b/src/migration/types/types.go @@ -1,6 +1,7 @@ package types import ( + "context" "time" "github.com/jackc/pgx/v4" @@ -10,8 +11,8 @@ type Migration interface { Version() MigrationVersion Name() string Description() string - Up(conn pgx.Tx) error - Down(conn pgx.Tx) error + Up(ctx context.Context, conn pgx.Tx) error + Down(ctx context.Context, conn pgx.Tx) error } type MigrationVersion time.Time diff --git a/src/models/post.go b/src/models/post.go index 660507a9..a7b7de50 100644 --- a/src/models/post.go +++ b/src/models/post.go @@ -29,3 +29,25 @@ type Post struct { Preview string `db:"preview"` ReadOnly bool `db:"readonly"` } + +type Parser int + +const ( + ParserBBCode Parser = 1 + ParserCleanHTML = 2 + ParserMarkdown = 3 +) + +type PostVersion struct { + ID int `db:"id"` + PostID int `db:"post_id"` + + TextRaw string `db:"text_raw"` + TextParsed string `db:"text_parsed"` + Parser Parser `db:"parser"` + + EditIP *net.IPNet `db:"edit_ip"` + EditDate time.Time `db:"edit_date"` + EditReason string `db:"edit_reason"` + EditorID *int `db:"editor_id"` +} diff --git a/src/templates/mapping.go b/src/templates/mapping.go index d5e9596f..1380bcf1 100644 --- a/src/templates/mapping.go +++ b/src/templates/mapping.go @@ -5,13 +5,25 @@ import ( "git.handmade.network/hmn/hmn/src/models" ) -func PostToTemplate(p *models.Post) Post { +func PostToTemplate(p *models.Post, author models.User) Post { return Post{ + Author: UserToTemplate(&author), Preview: p.Preview, ReadOnly: p.ReadOnly, + + // No content. Do it yourself if you care. + + IP: p.IP.String(), } } +func PostToTemplateWithContent(p *models.Post, author models.User, content string) Post { + post := PostToTemplate(p, author) + post.Content = content + + return post +} + func ProjectToTemplate(p *models.Project) Project { return Project{ Name: maybeString(p.Name), diff --git a/src/templates/src/include/post_list_item.html b/src/templates/src/include/post_list_item.html index cde112bd..200ac4ed 100644 --- a/src/templates/src/include/post_list_item.html +++ b/src/templates/src/include/post_list_item.html @@ -4,7 +4,7 @@ This template is intended to display a single post or thread in the context of a It should be called with PostListItemData. */}} -
+