feat: services: crus, migrations
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
bin/
|
bin/
|
||||||
|
database.db
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1 +1 @@
|
|||||||
7fb10105afd7388bbe8ff39ed98ded4e
|
273060acaa225744e95d4c8164ad961d
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
15653840ace748e3e2e77eba7f8ad2aa
|
ff91e2561ee95f47290ca5b41d1bc0ec
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore: Unused imports
|
|
||||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|
||||||
|
|
||||||
export function Greet(name: string): Promise<string> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(1411160069, name) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
import * as GreetService from "./greetservice.js";
|
|
||||||
export {
|
|
||||||
GreetService
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore: Unused imports
|
|
||||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|
||||||
|
|
||||||
export function Greet(name: string): Promise<string> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(1954554457, name) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
import * as GreetService from "./greetservice.js";
|
|
||||||
export {
|
|
||||||
GreetService
|
|
||||||
};
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: implement service and migrator
|
|
||||||
* @module
|
|
||||||
*/
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore: Unused imports
|
|
||||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|
||||||
|
|
||||||
export function Greet(name: string): Promise<string> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(4216357642, name) as any;
|
|
||||||
return $resultPromise;
|
|
||||||
}
|
|
||||||
2
go.mod
2
go.mod
@@ -6,6 +6,7 @@ toolchain go1.23.4
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.8.3
|
github.com/wailsapp/wails/v3 v3.0.0-alpha.8.3
|
||||||
|
gorm.io/driver/sqlite v1.5.0
|
||||||
gorm.io/gen v0.3.26
|
gorm.io/gen v0.3.26
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
gorm.io/plugin/dbresolver v1.5.3
|
gorm.io/plugin/dbresolver v1.5.3
|
||||||
@@ -40,6 +41,7 @@ require (
|
|||||||
github.com/lmittmann/tint v1.0.4 // indirect
|
github.com/lmittmann/tint v1.0.4 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
|||||||
BIN
internal/dal/gen_test.db
Normal file
BIN
internal/dal/gen_test.db
Normal file
Binary file not shown.
118
internal/dal/gen_test.go
Normal file
118
internal/dal/gen_test.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package dal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Input struct {
|
||||||
|
Args []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Expectation struct {
|
||||||
|
Ret []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestCase struct {
|
||||||
|
Input
|
||||||
|
Expectation
|
||||||
|
}
|
||||||
|
|
||||||
|
const _gen_test_db_name = "gen_test.db"
|
||||||
|
|
||||||
|
var _gen_test_db *gorm.DB
|
||||||
|
var _gen_test_once sync.Once
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
InitializeDB()
|
||||||
|
_gen_test_db.AutoMigrate(&_another{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitializeDB() {
|
||||||
|
_gen_test_once.Do(func() {
|
||||||
|
var err error
|
||||||
|
_gen_test_db, err = gorm.Open(sqlite.Open(_gen_test_db_name), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("open sqlite %q fail: %w", _gen_test_db_name, err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func assert(t *testing.T, methodName string, res, exp interface{}) {
|
||||||
|
if !reflect.DeepEqual(res, exp) {
|
||||||
|
t.Errorf("%v() gotResult = %v, want %v", methodName, res, exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type _another struct {
|
||||||
|
ID uint64 `gorm:"primaryKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*_another) TableName() string { return "another_for_unit_test" }
|
||||||
|
|
||||||
|
func Test_Available(t *testing.T) {
|
||||||
|
if !Use(_gen_test_db).Available() {
|
||||||
|
t.Errorf("query.Available() == false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_WithContext(t *testing.T) {
|
||||||
|
query := Use(_gen_test_db)
|
||||||
|
if !query.Available() {
|
||||||
|
t.Errorf("query Use(_gen_test_db) fail: query.Available() == false")
|
||||||
|
}
|
||||||
|
|
||||||
|
type Content string
|
||||||
|
var key, value Content = "gen_tag", "unit_test"
|
||||||
|
qCtx := query.WithContext(context.WithValue(context.Background(), key, value))
|
||||||
|
|
||||||
|
for _, ctx := range []context.Context{
|
||||||
|
qCtx.Post.UnderlyingDB().Statement.Context,
|
||||||
|
} {
|
||||||
|
if v := ctx.Value(key); v != value {
|
||||||
|
t.Errorf("get value from context fail, expect %q, got %q", value, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Transaction(t *testing.T) {
|
||||||
|
query := Use(_gen_test_db)
|
||||||
|
if !query.Available() {
|
||||||
|
t.Errorf("query Use(_gen_test_db) fail: query.Available() == false")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := query.Transaction(func(tx *Query) error { return nil })
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("query.Transaction execute fail: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := query.Begin()
|
||||||
|
|
||||||
|
err = tx.SavePoint("point")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("query tx SavePoint fail: %s", err)
|
||||||
|
}
|
||||||
|
err = tx.RollbackTo("point")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("query tx RollbackTo fail: %s", err)
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("query tx Commit fail: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = query.Begin().Rollback()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("query tx Rollback fail: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
145
internal/dal/posts.gen_test.go
Normal file
145
internal/dal/posts.gen_test.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
// Code generated by gorm.io/gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package dal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"app/internal/models"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gorm.io/gen"
|
||||||
|
"gorm.io/gen/field"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
InitializeDB()
|
||||||
|
err := _gen_test_db.AutoMigrate(&models.Post{})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: AutoMigrate(&models.Post{}) fail: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_postQuery(t *testing.T) {
|
||||||
|
post := newPost(_gen_test_db)
|
||||||
|
post = *post.As(post.TableName())
|
||||||
|
_do := post.WithContext(context.Background()).Debug()
|
||||||
|
|
||||||
|
primaryKey := field.NewString(post.TableName(), clause.PrimaryKey)
|
||||||
|
_, err := _do.Unscoped().Where(primaryKey.IsNotNull()).Delete()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("clean table <posts> fail:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := post.GetFieldByName("")
|
||||||
|
if ok {
|
||||||
|
t.Error("GetFieldByName(\"\") from post success")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _do.Create(&models.Post{})
|
||||||
|
if err != nil {
|
||||||
|
t.Error("create item in table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _do.Save(&models.Post{})
|
||||||
|
if err != nil {
|
||||||
|
t.Error("create item in table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _do.CreateInBatches([]*models.Post{{}, {}}, 10)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("create item in table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Select(post.ALL).Take()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Take() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.First()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("First() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Last()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("First() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Where(primaryKey.IsNotNull()).FindInBatch(10, func(tx gen.Dao, batch int) error { return nil })
|
||||||
|
if err != nil {
|
||||||
|
t.Error("FindInBatch() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _do.Where(primaryKey.IsNotNull()).FindInBatches(&[]*models.Post{}, 10, func(tx gen.Dao, batch int) error { return nil })
|
||||||
|
if err != nil {
|
||||||
|
t.Error("FindInBatches() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Select(post.ALL).Where(primaryKey.IsNotNull()).Order(primaryKey.Desc()).Find()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Find() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Distinct(primaryKey).Take()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("select Distinct() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Select(post.ALL).Omit(primaryKey).Take()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Omit() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Group(primaryKey).Find()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Group() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Scopes(func(dao gen.Dao) gen.Dao { return dao.Where(primaryKey.IsNotNull()) }).Find()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Scopes() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = _do.FindByPage(0, 1)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("FindByPage() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.ScanByPage(&models.Post{}, 0, 1)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("ScanByPage() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrInit()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("FirstOrInit() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrCreate()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("FirstOrCreate() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _a _another
|
||||||
|
var _aPK = field.NewString(_a.TableName(), "id")
|
||||||
|
|
||||||
|
err = _do.Join(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Join() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = _do.LeftJoin(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
t.Error("LeftJoin() on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = _do.Not().Or().Clauses().Take()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Not/Or/Clauses on table <posts> fail:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
71
internal/database/database.go
Normal file
71
internal/database/database.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"app/internal/dal"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
db *gorm.DB
|
||||||
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
const Path = "database.db"
|
||||||
|
|
||||||
|
func initialize() error {
|
||||||
|
var err error
|
||||||
|
db, err = gorm.Open(sqlite.Open("file:"+Path+"?_fk=1"), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res := db.Exec(`PRAGMA foreign_keys = ON`); res.Error != nil {
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := limitConnectionPool(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dal.SetDefault(db)
|
||||||
|
|
||||||
|
log.Println("Initialized database")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func limitConnectionPool() error {
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sqlDB.SetMaxIdleConns(10)
|
||||||
|
sqlDB.SetMaxOpenConns(100)
|
||||||
|
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Shutdown() error {
|
||||||
|
db, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = db.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
once = sync.Once{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInstance() *gorm.DB {
|
||||||
|
once.Do(func() {
|
||||||
|
err := initialize()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return db
|
||||||
|
}
|
||||||
17
internal/dialogs/dialogs.go
Normal file
17
internal/dialogs/dialogs.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package dialogs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InfoDialog(title string, message string) {
|
||||||
|
application.InfoDialog().SetTitle(title).SetMessage(message).Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func WarningDialog(title string, message string) {
|
||||||
|
application.WarningDialog().SetTitle(title).SetMessage(message).Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorDialog(title string, message string) {
|
||||||
|
application.ErrorDialog().SetTitle(title).SetMessage(message).Show()
|
||||||
|
}
|
||||||
@@ -6,16 +6,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Initialize the generator with configuration
|
|
||||||
g := gen.NewGenerator(gen.Config{
|
g := gen.NewGenerator(gen.Config{
|
||||||
OutPath: "../dal", // output directory, default value is ./query
|
OutPath: "../dal", // output directory, default value is ./query
|
||||||
Mode: gen.WithDefaultQuery | gen.WithQueryInterface | gen.WithoutContext,
|
Mode: gen.WithDefaultQuery | gen.WithQueryInterface | gen.WithoutContext,
|
||||||
FieldNullable: true,
|
FieldNullable: true,
|
||||||
|
WithUnitTest: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Generate default DAO interface for those specified structs
|
|
||||||
g.ApplyBasic(models.Entities...)
|
g.ApplyBasic(models.Entities...)
|
||||||
|
|
||||||
// Execute the generator
|
|
||||||
g.Execute()
|
g.Execute()
|
||||||
}
|
}
|
||||||
|
|||||||
36
internal/services/default_data.go
Normal file
36
internal/services/default_data.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"app/internal/dialogs"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InsertDefaultData() {
|
||||||
|
insertPosts()
|
||||||
|
}
|
||||||
|
|
||||||
|
func InsertDefaultEntityData[T any](service Service[T], entities []T) {
|
||||||
|
for _, item := range entities {
|
||||||
|
createdItem, err := service.Create(item)
|
||||||
|
if err != nil {
|
||||||
|
dialogs.ErrorDialog("Ошибка при вставке данных по умолчанию", fmt.Sprintf("Произошла ошибка при вставке значения %#v: %s", createdItem, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertPosts() {
|
||||||
|
InsertDefaultEntityData(&PostService{}, []Post{
|
||||||
|
{
|
||||||
|
Id: 1,
|
||||||
|
Text: "Жителям Кузбасса запретили болеть.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: 2,
|
||||||
|
Text: "⚡️⚡️⚡️Дома будут летать.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: 3,
|
||||||
|
Text: "В Кузбассе начали строить дома выше, чтобы жители были ближе к богу и солнцу.",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
116
internal/services/migrator.go
Normal file
116
internal/services/migrator.go
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"app/internal/database"
|
||||||
|
"app/internal/models"
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Migrator struct{}
|
||||||
|
|
||||||
|
var MigratorService = application.NewService(&Migrator{})
|
||||||
|
|
||||||
|
var db = database.GetInstance()
|
||||||
|
|
||||||
|
func CalculateFileSha256Checksum(filePath string) (string, error) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
hasher := sha256.New()
|
||||||
|
|
||||||
|
if _, err := io.Copy(hasher, file); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", hasher.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dropDatabase() error {
|
||||||
|
_ = database.Shutdown()
|
||||||
|
if _, err := os.Stat(database.Path); err == nil {
|
||||||
|
err := os.Remove(database.Path)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDatabase(entities ...any) error {
|
||||||
|
// Close current connections and create new database
|
||||||
|
err := dropDatabase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
db = database.GetInstance()
|
||||||
|
|
||||||
|
err = db.AutoMigrate(entities...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertDefaultData()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Migrate(entities ...any) error {
|
||||||
|
hashBeforeMigration, err := CalculateFileSha256Checksum(database.Path)
|
||||||
|
|
||||||
|
slog.Info("Calculating hash...")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("Apply migrations")
|
||||||
|
err = db.AutoMigrate(entities...)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
slog.Info("Error occurred while migrations: %s. Recreate database...", err)
|
||||||
|
if err = createDatabase(entities...); err != nil {
|
||||||
|
slog.Error("Error occurred again: %s. Panic!", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
slog.Info("Calculating hash after migrations...")
|
||||||
|
var hashAfterMigration string
|
||||||
|
hashAfterMigration, err = CalculateFileSha256Checksum(database.Path)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to calc hash: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hashAfterMigration != hashBeforeMigration {
|
||||||
|
slog.Info("Hashes before and after migrations are different. Recreate database...")
|
||||||
|
err = createDatabase(entities...)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to create new database: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slog.Info("Migrations proceeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migrator *Migrator) OnStartup(ctx context.Context, options application.ServiceOptions) error {
|
||||||
|
return Migrate(models.Entities...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migrator *Migrator) OnShutdown() {
|
||||||
|
err := database.Shutdown()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"app/internal/dal"
|
"app/internal/dal"
|
||||||
"app/internal/models"
|
"app/internal/models"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"gorm.io/gen/field"
|
"gorm.io/gen/field"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|||||||
10
internal/services/service.go
Normal file
10
internal/services/service.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
type Service[T any] interface {
|
||||||
|
GetAll() ([]*T, error)
|
||||||
|
GetById(id uint) (*T, error)
|
||||||
|
Create(item T) (T, error)
|
||||||
|
Update(item T) (T, error)
|
||||||
|
Delete(item T) (T, error)
|
||||||
|
Count() (int64, error)
|
||||||
|
}
|
||||||
7
internal/services/services.go
Normal file
7
internal/services/services.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import "github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
|
||||||
|
var ExportedServices = []application.Service{
|
||||||
|
application.NewService(&PostService{}),
|
||||||
|
}
|
||||||
4
main.go
4
main.go
@@ -14,9 +14,7 @@ func main() {
|
|||||||
app := application.New(application.Options{
|
app := application.New(application.Options{
|
||||||
Name: "nto_starterkit",
|
Name: "nto_starterkit",
|
||||||
Description: "A demo of using raw HTML & CSS",
|
Description: "A demo of using raw HTML & CSS",
|
||||||
Services: []application.Service{
|
Services: append([]application.Service{services.MigratorService}, services.ExportedServices...),
|
||||||
application.NewService(&services.PostService{}),
|
|
||||||
},
|
|
||||||
Assets: application.AssetOptions{
|
Assets: application.AssetOptions{
|
||||||
Handler: application.AssetFileServerFS(assets),
|
Handler: application.AssetFileServerFS(assets),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user