From 9b0c8b06583b478bf4b7a19d9e2397ce6734a974 Mon Sep 17 00:00:00 2001
From: Kursat Aktas
Date: Mon, 18 Nov 2024 19:53:02 +0300
Subject: [PATCH 1/5] Introducing IHP Guru on Gurubase.io
Signed-off-by: Kursat Aktas
---
README.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/README.md b/README.md
index 66a463077..bdb43dd0e 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,10 @@
+
+
+
+
From f59b36f448cc0ee80684df56f4b7a90be56b0df2 Mon Sep 17 00:00:00 2001
From: Montmorency
Date: Tue, 19 Nov 2024 20:52:11 +0000
Subject: [PATCH 2/5] Migration path Env Var. (#2015)
* changed migrations path to detect an environemnt variable or use default.
* updated docs.
* updated options and migrate.nix.
* fix lifting cs into IO.
* updates to ihp.nix
* recommenting testing flags in ihp.nix
---
Guide/database-migrations.markdown | 14 ++++++++
IHP/SchemaMigration.hs | 11 ++++--
NixSupport/nixosModules/options.nix | 36 +++++++++++--------
NixSupport/nixosModules/services/migrate.nix | 3 +-
.../SchemaDesigner/Controller/Migrations.hs | 14 ++++----
5 files changed, 53 insertions(+), 25 deletions(-)
diff --git a/Guide/database-migrations.markdown b/Guide/database-migrations.markdown
index fa65e2b8a..692ef2f80 100644
--- a/Guide/database-migrations.markdown
+++ b/Guide/database-migrations.markdown
@@ -73,6 +73,20 @@ migrate
A good value for `MINIMUM_REVISION` is typically the unix timestamp of the time when the database was initially created.
+
+### IHP MIGRATIONS DIR
+
+In production when running the migrations binary it is sometimes convenient to have all your Migrations in a non-standard place:
+e.g. if you need to push migrations onto production server without rebuilding the application. There is an Environment variable
+`IHP_MIGRATION_DIR` to accomplish this.
+
+```
+IHP_MIGRATION_DIR=path/to/my/migration/dir
+```
+
+This can be set in the environment attribute set of your IHP app flake.
+
+
## Common Issues
### ALTER TYPE ... ADD cannot run inside a transaction block
diff --git a/IHP/SchemaMigration.hs b/IHP/SchemaMigration.hs
index 942ed67ae..48f5ffab3 100644
--- a/IHP/SchemaMigration.hs
+++ b/IHP/SchemaMigration.hs
@@ -12,6 +12,7 @@ import qualified Data.Text.IO as Text
import IHP.ModelSupport hiding (withTransaction)
import qualified Data.Char as Char
import IHP.Log.Types
+import IHP.EnvVar
data Migration = Migration
{ revision :: Int
@@ -37,7 +38,9 @@ migrate options = do
-- All queries are executed inside a database transaction to make sure that it can be restored when something goes wrong.
runMigration :: (?modelContext :: ModelContext) => Migration -> IO ()
runMigration migration@Migration { revision, migrationFile } = do
- migrationSql <- Text.readFile (cs $ migrationPath migration)
+ -- | User can specify migrations directory as environment variable (defaults to /Application/Migrations/...)
+ migrationFilePath <- migrationPath migration
+ migrationSql <- Text.readFile (cs migrationFilePath)
let fullSql = [trimming|
BEGIN;
@@ -123,5 +126,7 @@ pathToMigration fileName = case revision of
|> fmap textToInt
|> join
-migrationPath :: Migration -> Text
-migrationPath Migration { migrationFile } = "Application/Migration/" <> migrationFile
+migrationPath :: Migration -> IO Text
+migrationPath Migration { migrationFile } = do
+ migrationDir <- envOrDefault "IHP_MIGRATION_DIR" "Application/Migration/"
+ pure (migrationDir <> migrationFile)
diff --git a/NixSupport/nixosModules/options.nix b/NixSupport/nixosModules/options.nix
index 952d571f6..310bc1d8e 100644
--- a/NixSupport/nixosModules/options.nix
+++ b/NixSupport/nixosModules/options.nix
@@ -13,63 +13,70 @@ with lib;
type = types.str;
default = "https://${config.services.ihp.domain}";
};
-
+
migrations = mkOption {
type = types.path;
};
-
+
schema = mkOption {
type = types.path;
};
-
+
fixtures = mkOption {
type = types.path;
};
-
+
httpsEnabled = mkOption {
type = types.bool;
default = true;
};
-
+
databaseName = mkOption {
type = types.str;
default = "app";
};
-
+
databaseUser = mkOption {
type = types.str;
default = "ihp";
};
-
+
databaseUrl = mkOption {
type = types.str;
};
-
+
# https://ihp.digitallyinduced.com/Guide/database-migrations.html#skipping-old-migrations
minimumRevision = mkOption {
type = types.int;
default = 0;
};
-
+
+ # https://ihp.digitallyinduced.com/Guide/database-migrations.html#ihp-migrations-dir
+ ihpMigrationDir = mkOption {
+ type = types.str;
+ default = "Application/Migration/";
+ };
+
+
ihpEnv = mkOption {
type = types.str;
default = "Production";
};
-
+
appPort = mkOption {
type = types.int;
default = 8000;
};
-
+
requestLoggerIPAddrSource = mkOption {
type = types.str;
default = "FromHeader";
};
-
+
sessionSecret = mkOption {
type = types.str;
};
-
+
additionalEnvVars = mkOption {
type = types.attrs;
default = {};
@@ -79,7 +86,7 @@ with lib;
type = types.package;
default = if config.services.ihp.optimized then self.packages."${pkgs.system}".optimized-prod-server else self.packages."${pkgs.system}".default;
};
-
+
optimized = mkOption {
type = types.bool;
default = false;
@@ -91,4 +98,3 @@ with lib;
};
};
}
-
diff --git a/NixSupport/nixosModules/services/migrate.nix b/NixSupport/nixosModules/services/migrate.nix
index a5f6d9ad5..ecd5d7cbe 100644
--- a/NixSupport/nixosModules/services/migrate.nix
+++ b/NixSupport/nixosModules/services/migrate.nix
@@ -22,6 +22,7 @@ in
environment = {
DATABASE_URL = cfg.databaseUrl;
MINIMUM_REVISION = "${toString cfg.minimumRevision}";
+ IHP_MIGRATION_DIR = cfg.ihpMigrationDir;
};
};
-}
\ No newline at end of file
+}
diff --git a/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs b/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs
index 41f1d0d81..940c903ed 100644
--- a/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs
+++ b/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs
@@ -61,7 +61,7 @@ instance Controller MigrationsController where
let errorMessage = case fromException exception of
Just (exception :: EnhancedSqlError) -> cs exception.sqlError.sqlErrorMsg
Nothing -> tshow exception
-
+
setErrorMessage errorMessage
redirectTo MigrationsAction
Right _ -> do
@@ -79,14 +79,14 @@ instance Controller MigrationsController where
action UpdateMigrationAction { migrationId } = do
migration <- findMigrationByRevision migrationId
let sqlStatements = param "sqlStatements"
-
- Text.writeFile (cs $ SchemaMigration.migrationPath migration) sqlStatements
+ migrationFilePath <- SchemaMigration.migrationPath migration
+ Text.writeFile (cs migrationFilePath) sqlStatements
redirectTo MigrationsAction
action DeleteMigrationAction { migrationId } = do
migration <- findMigrationByRevision migrationId
- let path = cs $ SchemaMigration.migrationPath migration
+ path <- cs <$> SchemaMigration.migrationPath migration
Directory.removeFile path
@@ -101,7 +101,7 @@ instance Controller MigrationsController where
let errorMessage = case fromException exception of
Just (exception :: EnhancedSqlError) -> cs exception.sqlError.sqlErrorMsg
Nothing -> tshow exception
-
+
setErrorMessage errorMessage
redirectTo MigrationsAction
Right _ -> do
@@ -109,7 +109,9 @@ instance Controller MigrationsController where
redirectTo MigrationsAction
readSqlStatements :: SchemaMigration.Migration -> IO Text
-readSqlStatements migration = Text.readFile (cs $ SchemaMigration.migrationPath migration)
+readSqlStatements migration = do
+ migrationFilePath <- (SchemaMigration.migrationPath migration)
+ pure Text.readFile (migrationFilePath)
findRecentMigrations :: IO [SchemaMigration.Migration]
findRecentMigrations = take 20 . reverse <$> SchemaMigration.findAllMigrations
From 7a4c95f94a9138b99c9a8bdaac953cdca8f72025 Mon Sep 17 00:00:00 2001
From: Marc Scholten
Date: Tue, 19 Nov 2024 12:55:18 -0800
Subject: [PATCH 3/5] fixed findAllMigrations still trying to access
Application/Migration
---
IHP/SchemaMigration.hs | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/IHP/SchemaMigration.hs b/IHP/SchemaMigration.hs
index 48f5ffab3..22ac3d645 100644
--- a/IHP/SchemaMigration.hs
+++ b/IHP/SchemaMigration.hs
@@ -99,7 +99,8 @@ findMigratedRevisions = map (\[revision] -> revision) <$> sqlQuery "SELECT revis
-- The result is sorted so that the oldest revision is first.
findAllMigrations :: IO [Migration]
findAllMigrations = do
- directoryFiles <- Directory.listDirectory "Application/Migration"
+ migrationDir <- detectMigrationDir
+ directoryFiles <- Directory.listDirectory (cs migrationDir)
directoryFiles
|> map cs
|> filter (\path -> ".sql" `isSuffixOf` path)
@@ -128,5 +129,10 @@ pathToMigration fileName = case revision of
migrationPath :: Migration -> IO Text
migrationPath Migration { migrationFile } = do
- migrationDir <- envOrDefault "IHP_MIGRATION_DIR" "Application/Migration/"
+ migrationDir <- detectMigrationDir
pure (migrationDir <> migrationFile)
+
+detectMigrationDir :: IO Text
+detectMigrationDir =
+ envOrDefault "IHP_MIGRATION_DIR" "Application/Migration/"
+
From 5174b90852019ed1ff44601b82de86bcb4c588a9 Mon Sep 17 00:00:00 2001
From: Marc Scholten
Date: Tue, 19 Nov 2024 13:02:48 -0800
Subject: [PATCH 4/5] fixed error in readSqlStatements
---
ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs b/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs
index 940c903ed..99074aa65 100644
--- a/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs
+++ b/ihp-ide/IHP/IDE/SchemaDesigner/Controller/Migrations.hs
@@ -111,7 +111,7 @@ instance Controller MigrationsController where
readSqlStatements :: SchemaMigration.Migration -> IO Text
readSqlStatements migration = do
migrationFilePath <- (SchemaMigration.migrationPath migration)
- pure Text.readFile (migrationFilePath)
+ Text.readFile (cs migrationFilePath)
findRecentMigrations :: IO [SchemaMigration.Migration]
findRecentMigrations = take 20 . reverse <$> SchemaMigration.findAllMigrations
From 611b881d29cdb4be32887c356332510d06951273 Mon Sep 17 00:00:00 2001
From: Marc Scholten
Date: Tue, 19 Nov 2024 16:47:00 -0800
Subject: [PATCH 5/5] DataSync: fixed connections closed when it's still needed
---
lib/IHP/DataSync/ihp-datasync.js | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/lib/IHP/DataSync/ihp-datasync.js b/lib/IHP/DataSync/ihp-datasync.js
index 345834fb2..d568e6727 100644
--- a/lib/IHP/DataSync/ihp-datasync.js
+++ b/lib/IHP/DataSync/ihp-datasync.js
@@ -352,6 +352,11 @@ class DataSubscription {
return;
}
+ // Set isClosed early as we need to prevent a second close() from triggering another DeleteDataSubscription message
+ // also we don't want to receive any further messages, and onMessage will not process if isClosed == true
+ this.isClosed = true;
+ this.onClose();
+
const dataSyncController = DataSyncController.getInstance();
const { subscriptionId } = await dataSyncController.sendMessage({ tag: 'DeleteDataSubscription', subscriptionId: this.subscriptionId });
@@ -360,10 +365,7 @@ class DataSubscription {
dataSyncController.removeEventListener('reconnect', this.onDataSyncReconnect);
dataSyncController.dataSubscriptions.splice(dataSyncController.dataSubscriptions.indexOf(this), 1);
- this.isClosed = true;
this.isConnected = false;
-
- this.onClose();
}
onDataSyncClosed() {
@@ -425,7 +427,9 @@ class DataSubscription {
return () => {
this.subscribers.splice(this.subscribers.indexOf(callback), 1);
- this.closeIfNotUsed();
+ // We delay the close as react could be re-rendering a component
+ // we garbage collect this connecetion once it's clearly not used anymore
+ setTimeout(this.closeIfNotUsed.bind(this), 1000);
}
}