refactor: update stores

This commit is contained in:
Steven
2023-06-20 15:54:56 +08:00
parent 8fb410ec3b
commit 805ab9996e
16 changed files with 1160 additions and 403 deletions

View File

@ -23,12 +23,12 @@ var migrationFS embed.FS
var seedFS embed.FS
type DB struct {
profile *profile.Profile
// sqlite db connection instance
DBInstance *sql.DB
profile *profile.Profile
}
// NewDB returns a new instance of DB associated with the given datasource name.
// NewDB returns a new instance of DB.
func NewDB(profile *profile.Profile) *DB {
db := &DB{
profile: profile,
@ -50,64 +50,70 @@ func (db *DB) Open(ctx context.Context) (err error) {
db.DBInstance = sqliteDB
if db.profile.Mode == "prod" {
// If db file not exists, we should migrate the database.
if _, err := os.Stat(db.profile.DSN); errors.Is(err, os.ErrNotExist) {
if err := db.applyLatestSchema(ctx); err != nil {
return fmt.Errorf("failed to apply latest schema: %w", err)
}
}
currentVersion := version.GetCurrentVersion(db.profile.Mode)
migrationHistoryList, err := db.FindMigrationHistoryList(ctx, &MigrationHistoryFind{})
_, err := os.Stat(db.profile.DSN)
if err != nil {
return fmt.Errorf("failed to find migration history, err: %w", err)
}
if len(migrationHistoryList) == 0 {
_, err := db.UpsertMigrationHistory(ctx, &MigrationHistoryUpsert{
Version: currentVersion,
})
// If db file not exists, we should apply the latest schema.
if errors.Is(err, os.ErrNotExist) {
if err := db.applyLatestSchema(ctx); err != nil {
return fmt.Errorf("failed to apply latest schema: %w", err)
}
} else {
return fmt.Errorf("failed to check database file: %w", err)
}
} else {
// If db file exists, we should check the migration history and apply the migration if needed.
currentVersion := version.GetCurrentVersion(db.profile.Mode)
migrationHistoryList, err := db.FindMigrationHistoryList(ctx, &MigrationHistoryFind{})
if err != nil {
return fmt.Errorf("failed to upsert migration history, err: %w", err)
return fmt.Errorf("failed to find migration history, err: %w", err)
}
return nil
}
migrationHistoryVersionList := []string{}
for _, migrationHistory := range migrationHistoryList {
migrationHistoryVersionList = append(migrationHistoryVersionList, migrationHistory.Version)
}
sort.Sort(version.SortVersion(migrationHistoryVersionList))
latestMigrationHistoryVersion := migrationHistoryVersionList[len(migrationHistoryVersionList)-1]
if version.IsVersionGreaterThan(version.GetSchemaVersion(currentVersion), latestMigrationHistoryVersion) {
minorVersionList := getMinorVersionList()
// backup the raw database file before migration
rawBytes, err := os.ReadFile(db.profile.DSN)
if err != nil {
return fmt.Errorf("failed to read raw database file, err: %w", err)
if len(migrationHistoryList) == 0 {
_, err := db.UpsertMigrationHistory(ctx, &MigrationHistoryUpsert{
Version: currentVersion,
})
if err != nil {
return fmt.Errorf("failed to upsert migration history, err: %w", err)
}
return nil
}
backupDBFilePath := fmt.Sprintf("%s/shortify_%s_%d_backup.db", db.profile.Data, db.profile.Version, time.Now().Unix())
if err := os.WriteFile(backupDBFilePath, rawBytes, 0644); err != nil {
return fmt.Errorf("failed to write raw database file, err: %w", err)
}
println("succeed to copy a backup database file")
println("start migrate")
for _, minorVersion := range minorVersionList {
normalizedVersion := minorVersion + ".0"
if version.IsVersionGreaterThan(normalizedVersion, latestMigrationHistoryVersion) && version.IsVersionGreaterOrEqualThan(currentVersion, normalizedVersion) {
println("applying migration for", normalizedVersion)
if err := db.applyMigrationForMinorVersion(ctx, minorVersion); err != nil {
return fmt.Errorf("failed to apply minor version migration: %w", err)
migrationHistoryVersionList := []string{}
for _, migrationHistory := range migrationHistoryList {
migrationHistoryVersionList = append(migrationHistoryVersionList, migrationHistory.Version)
}
sort.Sort(version.SortVersion(migrationHistoryVersionList))
latestMigrationHistoryVersion := migrationHistoryVersionList[len(migrationHistoryVersionList)-1]
if version.IsVersionGreaterThan(version.GetSchemaVersion(currentVersion), latestMigrationHistoryVersion) {
minorVersionList := getMinorVersionList()
// backup the raw database file before migration
rawBytes, err := os.ReadFile(db.profile.DSN)
if err != nil {
return fmt.Errorf("failed to read raw database file, err: %w", err)
}
backupDBFilePath := fmt.Sprintf("%s/shortify_%s_%d_backup.db", db.profile.Data, db.profile.Version, time.Now().Unix())
if err := os.WriteFile(backupDBFilePath, rawBytes, 0644); err != nil {
return fmt.Errorf("failed to write raw database file, err: %w", err)
}
println("succeed to copy a backup database file")
println("start migrate")
for _, minorVersion := range minorVersionList {
normalizedVersion := minorVersion + ".0"
if version.IsVersionGreaterThan(normalizedVersion, latestMigrationHistoryVersion) && version.IsVersionGreaterOrEqualThan(currentVersion, normalizedVersion) {
println("applying migration for", normalizedVersion)
if err := db.applyMigrationForMinorVersion(ctx, minorVersion); err != nil {
return fmt.Errorf("failed to apply minor version migration: %w", err)
}
}
}
}
println("end migrate")
println("end migrate")
// remove the created backup db file after migrate succeed
if err := os.Remove(backupDBFilePath); err != nil {
println(fmt.Sprintf("Failed to remove temp database file, err %v", err))
// remove the created backup db file after migrate succeed
if err := os.Remove(backupDBFilePath); err != nil {
println(fmt.Sprintf("Failed to remove temp database file, err %v", err))
}
}
}
} else {

View File

@ -4,23 +4,47 @@ CREATE TABLE migration_history (
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now'))
);
-- workspace
CREATE TABLE workspace (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
resource_id TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT ''
);
INSERT INTO
sqlite_sequence (name, seq)
VALUES
('workspace', 1);
-- workspace_setting
CREATE TABLE workspace_setting (
workspace_id INTEGER NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL,
UNIQUE(workspace_id, key)
);
-- user
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
email TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
username TEXT NOT NULL UNIQUE,
nickname TEXT NOT NULL,
email TEXT NOT NULL,
password_hash TEXT NOT NULL,
open_id TEXT NOT NULL UNIQUE,
role TEXT NOT NULL CHECK (role IN ('ADMIN', 'USER')) DEFAULT 'USER'
);
INSERT INTO
sqlite_sequence (name, seq)
VALUES
('user', 100);
('user', 10);
-- user_setting
CREATE TABLE user_setting (
@ -30,31 +54,6 @@ CREATE TABLE user_setting (
UNIQUE(user_id, key)
);
-- workspace
CREATE TABLE workspace (
id INTEGER PRIMARY KEY AUTOINCREMENT,
creator_id INTEGER NOT NULL,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
name TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT ''
);
INSERT INTO
sqlite_sequence (name, seq)
VALUES
('workspace', 10);
-- workspace_setting
CREATE TABLE workspace_setting (
workspace_id INTEGER NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL,
UNIQUE(workspace_id, key)
);
-- workspace_user
CREATE TABLE workspace_user (
workspace_id INTEGER NOT NULL,
@ -80,4 +79,4 @@ CREATE TABLE shortcut (
INSERT INTO
sqlite_sequence (name, seq)
VALUES
('shortcut', 1000);
('shortcut', 100);

View File

@ -4,23 +4,47 @@ CREATE TABLE migration_history (
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now'))
);
-- workspace
CREATE TABLE workspace (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
resource_id TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT ''
);
INSERT INTO
sqlite_sequence (name, seq)
VALUES
('workspace', 1);
-- workspace_setting
CREATE TABLE workspace_setting (
workspace_id INTEGER NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL,
UNIQUE(workspace_id, key)
);
-- user
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
email TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
username TEXT NOT NULL UNIQUE,
nickname TEXT NOT NULL,
email TEXT NOT NULL,
password_hash TEXT NOT NULL,
open_id TEXT NOT NULL UNIQUE,
role TEXT NOT NULL CHECK (role IN ('ADMIN', 'USER')) DEFAULT 'USER'
);
INSERT INTO
sqlite_sequence (name, seq)
VALUES
('user', 100);
('user', 10);
-- user_setting
CREATE TABLE user_setting (
@ -30,31 +54,6 @@ CREATE TABLE user_setting (
UNIQUE(user_id, key)
);
-- workspace
CREATE TABLE workspace (
id INTEGER PRIMARY KEY AUTOINCREMENT,
creator_id INTEGER NOT NULL,
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
row_status TEXT NOT NULL CHECK (row_status IN ('NORMAL', 'ARCHIVED')) DEFAULT 'NORMAL',
name TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT ''
);
INSERT INTO
sqlite_sequence (name, seq)
VALUES
('workspace', 10);
-- workspace_setting
CREATE TABLE workspace_setting (
workspace_id INTEGER NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL,
UNIQUE(workspace_id, key)
);
-- workspace_user
CREATE TABLE workspace_user (
workspace_id INTEGER NOT NULL,
@ -80,4 +79,4 @@ CREATE TABLE shortcut (
INSERT INTO
sqlite_sequence (name, seq)
VALUES
('shortcut', 1000);
('shortcut', 100);

View File

@ -67,7 +67,7 @@ func findMigrationHistoryList(ctx context.Context, tx *sql.Tx, find *MigrationHi
FROM
migration_history
WHERE ` + strings.Join(where, " AND ") + `
ORDER BY version DESC
ORDER BY created_ts DESC
`
rows, err := tx.QueryContext(ctx, query, args...)
if err != nil {

View File

@ -1,17 +1,11 @@
DELETE FROM
shortcut;
DELETE FROM shortcut;
DELETE FROM
workspace_user;
DELETE FROM workspace_user;
DELETE FROM
user_setting;
DELETE FROM user_setting;
DELETE FROM
user;
DELETE FROM user;
DELETE FROM
workspace_setting;
DELETE FROM workspace_setting;
DELETE FROM
workspace;
DELETE FROM workspace;

View File

@ -1,35 +1,35 @@
INSERT INTO
user (
`id`,
`username`,
`nickname`,
`email`,
`display_name`,
`password_hash`,
`open_id`
`password_hash`
)
VALUES
(
101,
'frank@shortify.demo',
11,
'frank',
'Frank',
'frank@shortify.demo',
-- raw password: secret
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK',
'frank_open_id'
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
);
INSERT INTO
user (
`id`,
`username`,
`nickname`,
`email`,
`display_name`,
`password_hash`,
`open_id`
`password_hash`
)
VALUES
(
102,
'bob@shortify.demo',
12,
'bob',
'Bob',
'bob@shortify.demo',
-- raw password: secret
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK',
'bob_open_id'
);
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
);

View File

@ -1,33 +1,14 @@
INSERT INTO
workspace (
`id`,
`creator_id`,
`name`,
`title`,
`description`
)
VALUES
(
11,
101,
1,
'minecraft',
'minecraft',
''
);
INSERT INTO
workspace (
`id`,
`creator_id`,
`name`,
`title`,
`description`
)
VALUES
(
12,
102,
'bob',
'bob-room',
''
);

View File

@ -6,8 +6,8 @@ INSERT INTO
)
VALUES
(
11,
101,
1,
11,
'ADMIN'
);
@ -19,33 +19,7 @@ INSERT INTO
)
VALUES
(
11,
102,
'USER'
);
INSERT INTO
workspace_user (
`workspace_id`,
`user_id`,
`role`
)
VALUES
(
12,
102,
'ADMIN'
);
INSERT INTO
workspace_user (
`workspace_id`,
`user_id`,
`role`
)
VALUES
(
12,
101,
1,
12,
'USER'
);

View File

@ -9,8 +9,8 @@ INSERT INTO
)
VALUES
(
101,
11,
11,
1,
'baidu',
'https://baidu.com',
'百度搜索',
@ -28,8 +28,8 @@ INSERT INTO
)
VALUES
(
102,
11,
12,
1,
'bl',
'https://bilibili.com',
'B站',
@ -47,48 +47,10 @@ INSERT INTO
)
VALUES
(
101,
11,
11,
1,
'ph',
'https://producthunt.com',
'PH',
'PRIVATE'
);
INSERT INTO
shortcut (
`creator_id`,
`workspace_id`,
`name`,
`link`,
`description`,
`visibility`
)
VALUES
(
101,
12,
'github',
'https://producthunt.com',
'GitHub',
'PRIVATE'
);
INSERT INTO
shortcut (
`creator_id`,
`workspace_id`,
`name`,
`link`,
`description`,
`visibility`
)
VALUES
(
102,
12,
'go',
'https://google.com',
'google',
'WORKSPACE'
);