From 365a1e2efa13a6decc90cccfb4215fdb51ea3182 Mon Sep 17 00:00:00 2001
From: Marc Scholten
Date: Mon, 18 Nov 2024 06:38:04 -0800
Subject: [PATCH 01/13] fix ses not sending to cc and bcc recipients
---
IHP/Mail.hs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/IHP/Mail.hs b/IHP/Mail.hs
index d1393b30d..ce175023c 100644
--- a/IHP/Mail.hs
+++ b/IHP/Mail.hs
@@ -54,7 +54,7 @@ sendWithMailServer SES { .. } mail = do
manager <- Network.HTTP.Client.newManager Network.HTTP.Client.TLS.tlsManagerSettings
let ses = Mailer.SES {
Mailer.sesFrom = cs $ addressEmail (mailFrom mail),
- Mailer.sesTo = map (cs . addressEmail) (mailTo mail),
+ Mailer.sesTo = map (cs . addressEmail) ((mailTo mail) <> (mailCc mail) <> (mailBcc mail)),
Mailer.sesAccessKey = accessKey,
Mailer.sesSecretKey = secretKey,
Mailer.sesSessionToken = Nothing,
From 9b0c8b06583b478bf4b7a19d9e2397ce6734a974 Mon Sep 17 00:00:00 2001
From: Kursat Aktas
Date: Mon, 18 Nov 2024 19:53:02 +0300
Subject: [PATCH 02/13] 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 5684d1a214c2bd1c34fc314f7a4bd3265dd8b4c8 Mon Sep 17 00:00:00 2001
From: Marc Scholten
Date: Tue, 19 Nov 2024 10:18:15 -0800
Subject: [PATCH 03/13] nixos: simplify migrate service by using
IHP_MIGRATION_DIR
---
NixSupport/nixosModules/services/migrate.nix | 31 +++++++-------------
1 file changed, 10 insertions(+), 21 deletions(-)
diff --git a/NixSupport/nixosModules/services/migrate.nix b/NixSupport/nixosModules/services/migrate.nix
index a5f6d9ad5..f1097193c 100644
--- a/NixSupport/nixosModules/services/migrate.nix
+++ b/NixSupport/nixosModules/services/migrate.nix
@@ -2,26 +2,15 @@
let cfg = config.services.ihp;
in
{
- systemd.services.migrate =
- let migrateApp = pkgs.stdenv.mkDerivation {
- name = "migrate-app";
- src = cfg.migrations;
- buildPhase = ''
- mkdir -p $out/Application/Migration
- find "$src" -mindepth 1 -type f -exec cp {} $out/Application/Migration \;
- '';
- };
- in {
- serviceConfig = {
- Type = "oneshot";
- };
- script = ''
- cd ${migrateApp}
- ${ihp.apps."${pkgs.system}".migrate.program}
- '';
- environment = {
- DATABASE_URL = cfg.databaseUrl;
- MINIMUM_REVISION = "${toString cfg.minimumRevision}";
- };
+ systemd.services.migrate = {
+ serviceConfig = {
+ Type = "oneshot";
+ ExecStart = ihp.apps."${pkgs.system}".migrate.program;
+ };
+ environment = {
+ DATABASE_URL = cfg.databaseUrl;
+ MINIMUM_REVISION = "${toString cfg.minimumRevision}";
+ IHP_MIGRATION_DIR = cfg.migrations;
+ };
};
}
\ No newline at end of file
From f59b36f448cc0ee80684df56f4b7a90be56b0df2 Mon Sep 17 00:00:00 2001
From: Montmorency
Date: Tue, 19 Nov 2024 20:52:11 +0000
Subject: [PATCH 04/13] 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 05/13] 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 06/13] 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 07/13] 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);
}
}
From 0ac5d136a565303d00609f1536b1b5e6a656d858 Mon Sep 17 00:00:00 2001
From: Varun Rajput
Date: Tue, 3 Dec 2024 11:47:53 +0530
Subject: [PATCH 08/13] docs: add scheduled job execution with runAt field
(#2017)
Add documentation for scheduling future job execution using the runAt field.
Reorganize job execution docs to clearly distinguish between immediate
and scheduled runs. Note polling interval for scheduled jobs.
---
Guide/jobs.markdown | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/Guide/jobs.markdown b/Guide/jobs.markdown
index 511ebc595..e67009311 100644
--- a/Guide/jobs.markdown
+++ b/Guide/jobs.markdown
@@ -44,11 +44,25 @@ instance Job EmailCustomersJob where
### Running the job
-IHP watches the job table in the database for any new records and automatically runs the job asynchronously when a new job is added. So to run a job, simply create a new record:
+IHP watches the job table in the database for any new records and automatically runs the job asynchronously when a new job is added. There are two ways to run a job:
+
+1. Run immediately (as soon as a job worker is available):
```haskell
newRecord @EmailCustomersJob |> create
```
+2. Schedule for future execution:
+
+```haskell
+import Data.Time.Clock (addUTCTime, getCurrentTime, nominalDay)
+
+now <- getCurrentTime
+newRecord @EmailCustomersJob
+ |> set #runAt (addUTCTime nominalDay now) -- Schedule 24 hours in the future
+ |> create
+```
+
+The `runAt` field determines when the job should be executed. If not set, the job runs immediately. When set, IHP polls for scheduled jobs approximately every minute and executes any jobs whose `runAt` time has passed.
This can be done in a controller action or in a script as will be shown below.
From 9bc16f6ff1c85a2dab1040dedc8a9fce2c56b26c Mon Sep 17 00:00:00 2001
From: Marc Scholten
Date: Tue, 3 Dec 2024 18:36:23 +0100
Subject: [PATCH 09/13] add useCount to DataSync for efficient count queries
---
IHP/DataSync/ControllerImpl.hs | 43 +++++++++++++++++++
IHP/DataSync/Types.hs | 3 ++
.../IHP/DataSync/TypeScript/Compiler.hs | 2 +
ihp-datasync-typescript/Test/Spec.hs | 2 +
lib/IHP/DataSync/react.js | 41 +++++++++++++++++-
5 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/IHP/DataSync/ControllerImpl.hs b/IHP/DataSync/ControllerImpl.hs
index 17fd66217..37577d9e2 100644
--- a/IHP/DataSync/ControllerImpl.hs
+++ b/IHP/DataSync/ControllerImpl.hs
@@ -187,6 +187,49 @@ buildMessageHandler ensureRLSEnabled installTableChangeTriggers sendJSON handleC
MVar.takeMVar close
+ handleMessage CreateCountSubscription { query, requestId } = do
+ ensureBelowSubscriptionsLimit
+
+ tableNameRLS <- ensureRLSEnabled query.table
+
+ subscriptionId <- UUID.nextRandom
+
+ -- Allocate the close handle as early as possible
+ -- to make DeleteDataSubscription calls succeed even when the CountSubscription is
+ -- not fully set up yet
+ close <- MVar.newEmptyMVar
+ atomicModifyIORef'' ?state (\state -> state |> modify #subscriptions (HashMap.insert subscriptionId close))
+
+ let (theQuery, theParams) = compileQueryWithRenamer (renamer query.table) query
+
+ let countQuery = "SELECT COUNT(*) FROM (" <> theQuery <> ") AS _inner"
+
+ let
+ unpackResult :: [(Only Int)] -> Int
+ unpackResult [(Only value)] = value
+ unpackResult otherwise = error "DataSync.unpackResult: Expected INT, but got something else"
+
+ count <- unpackResult <$> sqlQueryWithRLS countQuery theParams
+ countRef <- newIORef count
+
+ installTableChangeTriggers tableNameRLS
+
+ let
+ callback :: ChangeNotifications.ChangeNotification -> IO ()
+ callback _ = do
+ newCount <- unpackResult <$> sqlQueryWithRLS countQuery theParams
+ lastCount <- readIORef countRef
+
+ when (newCount /= count) (sendJSON DidChangeCount { subscriptionId, count = newCount })
+
+ let subscribe = PGListener.subscribeJSON (ChangeNotifications.channelName tableNameRLS) callback pgListener
+ let unsubscribe subscription = PGListener.unsubscribe subscription pgListener
+
+ Exception.bracket subscribe unsubscribe \channelSubscription -> do
+ sendJSON DidCreateCountSubscription { subscriptionId, requestId, count }
+
+ MVar.takeMVar close
+
handleMessage DeleteDataSubscription { requestId, subscriptionId } = do
DataSyncReady { subscriptions } <- getState
case HashMap.lookup subscriptionId subscriptions of
diff --git a/IHP/DataSync/Types.hs b/IHP/DataSync/Types.hs
index d93be685f..ed4240d7e 100644
--- a/IHP/DataSync/Types.hs
+++ b/IHP/DataSync/Types.hs
@@ -10,6 +10,7 @@ import Control.Concurrent.MVar as MVar
data DataSyncMessage
= DataSyncQuery { query :: !DynamicSQLQuery, requestId :: !Int, transactionId :: !(Maybe UUID) }
| CreateDataSubscription { query :: !DynamicSQLQuery, requestId :: !Int }
+ | CreateCountSubscription { query :: !DynamicSQLQuery, requestId :: !Int }
| DeleteDataSubscription { subscriptionId :: !UUID, requestId :: !Int }
| CreateRecordMessage { table :: !Text, record :: !(HashMap Text Value), requestId :: !Int, transactionId :: !(Maybe UUID) }
| CreateRecordsMessage { table :: !Text, records :: ![HashMap Text Value], requestId :: !Int, transactionId :: !(Maybe UUID) }
@@ -31,10 +32,12 @@ data DataSyncResponse
| DataSyncError { requestId :: !Int, errorMessage :: !Text }
| FailedToDecodeMessageError { errorMessage :: !Text }
| DidCreateDataSubscription { requestId :: !Int, subscriptionId :: !UUID, result :: ![[Field]] }
+ | DidCreateCountSubscription { requestId :: !Int, subscriptionId :: !UUID, count :: !Int }
| DidDeleteDataSubscription { requestId :: !Int, subscriptionId :: !UUID }
| DidInsert { subscriptionId :: !UUID, record :: ![Field] }
| DidUpdate { subscriptionId :: !UUID, id :: UUID, changeSet :: !Value }
| DidDelete { subscriptionId :: !UUID, id :: !UUID }
+ | DidChangeCount { subscriptionId :: !UUID, count :: !Int }
| DidCreateRecord { requestId :: !Int, record :: ![Field] } -- ^ Response to 'CreateRecordMessage'
| DidCreateRecords { requestId :: !Int, records :: ![[Field]] } -- ^ Response to 'CreateRecordsMessage'
| DidUpdateRecord { requestId :: !Int, record :: ![Field] } -- ^ Response to 'UpdateRecordMessage'
diff --git a/ihp-datasync-typescript/IHP/DataSync/TypeScript/Compiler.hs b/ihp-datasync-typescript/IHP/DataSync/TypeScript/Compiler.hs
index c59539967..1656e37e2 100644
--- a/ihp-datasync-typescript/IHP/DataSync/TypeScript/Compiler.hs
+++ b/ihp-datasync-typescript/IHP/DataSync/TypeScript/Compiler.hs
@@ -259,6 +259,8 @@ declare module 'ihp-datasync/react' {
*/
function useQuery(queryBuilder: QueryBuilder, options?: DataSubscriptionOptions): Array | null;
+ function useCount(queryBuilder: QueryBuilder): number | null;
+
/**
* A version of `useQuery` when you only want to fetch a single record.
*
diff --git a/ihp-datasync-typescript/Test/Spec.hs b/ihp-datasync-typescript/Test/Spec.hs
index e9061dcb2..c11af8acf 100644
--- a/ihp-datasync-typescript/Test/Spec.hs
+++ b/ihp-datasync-typescript/Test/Spec.hs
@@ -349,6 +349,8 @@ tests = do
*/
function useQuery(queryBuilder: QueryBuilder, options?: DataSubscriptionOptions): Array | null;
+ function useCount(queryBuilder: QueryBuilder): number | null;
+
/**
* A version of `useQuery` when you only want to fetch a single record.
*
diff --git a/lib/IHP/DataSync/react.js b/lib/IHP/DataSync/react.js
index ba008e110..311f338c8 100644
--- a/lib/IHP/DataSync/react.js
+++ b/lib/IHP/DataSync/react.js
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useContext, useSyncExternalStore } from 'react';
+import React, { useState, useEffect, useContext, useSyncExternalStore, useRef, useMemo } from 'react';
import { DataSubscription, DataSyncController } from './ihp-datasync.js';
// Most IHP apps never use this context because they use session cookies for auth.
@@ -99,4 +99,43 @@ export class DataSubscriptionStore {
return subscription;
}
}
+}
+
+export function useCount(queryBuilder) {
+ const count = useRef(null);
+ const getSnapshot = useMemo(() => () => count.current, []);
+ const subscribe = useMemo(() => (onStoreChange) => {
+ const controller = DataSyncController.getInstance();
+ var isActive = true;
+ var subscriptionId = null;
+ const onMessage = (message) => {
+ if (message.tag === 'DidChangeCount' && message.subscriptionId === subscriptionId) {
+ count.current = message.count;
+ onStoreChange();
+ }
+ };
+ controller.sendMessage({ tag: 'CreateCountSubscription', query: queryBuilder.query })
+ .then((response) => {
+ if (isActive) {
+ subscriptionId = response.subscriptionId;
+ count.current = response.count;
+ onStoreChange();
+
+ controller.addEventListener('message', onMessage);
+ } else {
+ controller.sendMessage({ tag: 'DeleteDataSubscription', subscriptionId: response.subscriptionId });
+ }
+ })
+
+ return () => {
+ isActive = false;
+
+ if (subscriptionId) {
+ controller.sendMessage({ tag: 'DeleteDataSubscription', subscriptionId });
+ }
+ controller.removeEventListener('message', onMessage);
+ }
+ }, [JSON.stringify(queryBuilder.query)]);
+
+ return useSyncExternalStore(subscribe, getSnapshot);
}
\ No newline at end of file
From ad90fc222159bb07235c3f66afd39e8422ee1049 Mon Sep 17 00:00:00 2001
From: Marc Scholten
Date: Sat, 7 Dec 2024 11:43:16 +0100
Subject: [PATCH 10/13] DataSync: fix strings in arrays are double escaped
---
IHP/DataSync/REST/Controller.hs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/IHP/DataSync/REST/Controller.hs b/IHP/DataSync/REST/Controller.hs
index 86b052fe1..7eb12b1b9 100644
--- a/IHP/DataSync/REST/Controller.hs
+++ b/IHP/DataSync/REST/Controller.hs
@@ -203,7 +203,7 @@ aesonValueToPostgresValue (Number value) = case Scientific.floatingOrInteger val
Left (floating :: Double) -> PG.toField floating
Right (integer :: Integer) -> PG.toField integer
aesonValueToPostgresValue Data.Aeson.Null = PG.toField PG.Null
-aesonValueToPostgresValue (Data.Aeson.Array values) = PG.toField (PG.PGArray (Vector.toList values))
+aesonValueToPostgresValue (Data.Aeson.Array values) = PG.toField (PG.PGArray (map aesonValueToPostgresValue (Vector.toList values)))
aesonValueToPostgresValue object@(Object values) =
let
tryDecodeAsPoint :: Maybe Point
From 4bddf80c63933ff2f1c79620de7be37996d18554 Mon Sep 17 00:00:00 2001
From: Varun Rajput
Date: Sun, 8 Dec 2024 17:42:24 +0530
Subject: [PATCH 11/13] Show On Delete constraint in Schema Designer
---
ihp-ide/IHP/IDE/SchemaDesigner/View/Layout.hs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ihp-ide/IHP/IDE/SchemaDesigner/View/Layout.hs b/ihp-ide/IHP/IDE/SchemaDesigner/View/Layout.hs
index 2105f17cc..fe79ad6c3 100644
--- a/ihp-ide/IHP/IDE/SchemaDesigner/View/Layout.hs
+++ b/ihp-ide/IHP/IDE/SchemaDesigner/View/Layout.hs
@@ -404,7 +404,7 @@ renderColumn Column { name, columnType, defaultValue, notNull, isUnique } id tab
Just value -> [hsx|default: {compileExpression value} |]
Nothing -> mempty
renderForeignKey = case findForeignKey statements tableName name of
- Just addConstraint@AddConstraint { constraint = ForeignKeyConstraint { name = Just constraintName, referenceTable } } -> [hsx|FOREIGN KEY: {referenceTable}|]
+ Just addConstraint@AddConstraint { constraint = ForeignKeyConstraint { name = Just constraintName, referenceTable, onDelete = onDeleteConstraint } } -> [hsx|FOREIGN KEY: {referenceTable} (On Delete: {tshow onDeleteConstraint})|]
_ -> mempty
foreignKeyOption = case findForeignKey statements tableName name of
Just addConstraint@AddConstraint { constraint = ForeignKeyConstraint { name = Just constraintName, referenceTable } } ->
From 89b762d2f8acdac694832158cd931e9ae8539323 Mon Sep 17 00:00:00 2001
From: Matthew Carroll <60335800+mmc102@users.noreply.github.com>
Date: Wed, 11 Dec 2024 04:59:43 -0500
Subject: [PATCH 12/13] update two small grammatical errors in the docs (#2019)
---
Guide/database.markdown | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Guide/database.markdown b/Guide/database.markdown
index ed1c145c6..f12ed6ebc 100644
--- a/Guide/database.markdown
+++ b/Guide/database.markdown
@@ -20,7 +20,7 @@ When the development server is running, you can connect to it via `postgresql://
When the development server is running, you can use your favorite UI tool (e.g. [TablePlus](https://tableplus.com/)) that allows connecting to Postgres. To do that you would need the following credentials:
-Database Host: This is the application root + "/build/db". Use this command on terminal form the root of you app and copy the output:
+Database Host: This is the application root + "/build/db". Use this command on terminal from the root of your app and copy the output:
```
echo `pwd`/build/db
```
From ed20efe60b3b8d7d738744d6c7da8ada0d667e51 Mon Sep 17 00:00:00 2001
From: Marc Scholten
Date: Fri, 13 Dec 2024 16:44:32 +0100
Subject: [PATCH 13/13] Replace IHP.FileStorage.MimeTypes with Network.Mime
---
IHP/FileStorage/ControllerFunctions.hs | 4 +-
IHP/FileStorage/MimeTypes.hs | 1030 ------------------------
Test/FileStorage/MimeTypesSpec.hs | 20 -
Test/Main.hs | 2 -
ihp.cabal | 2 +-
5 files changed, 3 insertions(+), 1055 deletions(-)
delete mode 100644 IHP/FileStorage/MimeTypes.hs
delete mode 100644 Test/FileStorage/MimeTypesSpec.hs
diff --git a/IHP/FileStorage/ControllerFunctions.hs b/IHP/FileStorage/ControllerFunctions.hs
index 6eb4cdd56..28a8daed1 100644
--- a/IHP/FileStorage/ControllerFunctions.hs
+++ b/IHP/FileStorage/ControllerFunctions.hs
@@ -40,7 +40,7 @@ import qualified System.Directory as Directory
import qualified Control.Exception as Exception
import qualified Network.Wreq as Wreq
import Control.Lens hiding ((|>), set)
-import IHP.FileStorage.MimeTypes
+import qualified Network.Mime as Mime
-- | Uploads a file to a directory in the storage
--
@@ -179,7 +179,7 @@ storeFileFromUrl url options = do
--
storeFileFromPath :: (?context :: context, ConfigProvider context) => Text -> StoreFileOptions -> IO StoredFile
storeFileFromPath path options = do
- let fileContentType = path |> guessMimeType |> cs
+ let fileContentType = Mime.defaultMimeLookup (cs path)
fileContent <- LBS.readFile (cs path)
let file = Wai.FileInfo
diff --git a/IHP/FileStorage/MimeTypes.hs b/IHP/FileStorage/MimeTypes.hs
deleted file mode 100644
index 9a4c3d117..000000000
--- a/IHP/FileStorage/MimeTypes.hs
+++ /dev/null
@@ -1,1030 +0,0 @@
-{-|
-Module: IHP.FileStorage.MimeTypes
-Description: Mime Type helpers
-Copyright: (c) digitally induced GmbH, 2021
--}
-module IHP.FileStorage.MimeTypes
-( guessMimeType
-, extensionToMimeTypeMapping
-) where
-
-import IHP.Prelude
-import qualified Data.Text as Text
-
--- | Returns the mime type based on a file extension
---
--- >>> guessMimeType "test.jpg"
--- "image/jpeg"
---
--- Returns @application/octet-stream@ if no extension is given or it's unknown:
---
--- >>> guessMimeType "unknown"
--- "application/octet-stream"
---
-guessMimeType :: Text -> Text
-guessMimeType file =
- file
- |> Text.breakOnEnd "."
- |> \case
- (_, "") -> defaultMimeType
- (_, extension) -> guessMimeTypeByExtension extension
- where
- defaultMimeType = "application/octet-stream"
- guessMimeTypeByExtension extension =
- extensionToMimeTypeMapping
- |> lookup extension
- |> fromMaybe defaultMimeType
-
--- | List of mime types
---
--- Generated using:
---
--- >>> wget -qO- http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types | egrep -v ^# | awk '{ for (i=2; i<=NF; i++) {print "(\""$i"\", \""$1"\")"}}' | sort
-extensionToMimeTypeMapping =
- [ ("123", "application/vnd.lotus-1-2-3")
- , ("3dml", "text/vnd.in3d.3dml")
- , ("3ds", "image/x-3ds")
- , ("3g2", "video/3gpp2")
- , ("3gp", "video/3gpp")
- , ("7z", "application/x-7z-compressed")
- , ("aab", "application/x-authorware-bin")
- , ("aac", "audio/x-aac")
- , ("aam", "application/x-authorware-map")
- , ("aas", "application/x-authorware-seg")
- , ("abw", "application/x-abiword")
- , ("ac", "application/pkix-attr-cert")
- , ("acc", "application/vnd.americandynamics.acc")
- , ("ace", "application/x-ace-compressed")
- , ("acu", "application/vnd.acucobol")
- , ("acutc", "application/vnd.acucorp")
- , ("adp", "audio/adpcm")
- , ("aep", "application/vnd.audiograph")
- , ("afm", "application/x-font-type1")
- , ("afp", "application/vnd.ibm.modcap")
- , ("ahead", "application/vnd.ahead.space")
- , ("ai", "application/postscript")
- , ("aif", "audio/x-aiff")
- , ("aifc", "audio/x-aiff")
- , ("aiff", "audio/x-aiff")
- , ("air", "application/vnd.adobe.air-application-installer-package+zip")
- , ("ait", "application/vnd.dvb.ait")
- , ("ami", "application/vnd.amiga.ami")
- , ("apk", "application/vnd.android.package-archive")
- , ("appcache", "text/cache-manifest")
- , ("application", "application/x-ms-application")
- , ("apr", "application/vnd.lotus-approach")
- , ("arc", "application/x-freearc")
- , ("asc", "application/pgp-signature")
- , ("asf", "video/x-ms-asf")
- , ("asm", "text/x-asm")
- , ("aso", "application/vnd.accpac.simply.aso")
- , ("asx", "video/x-ms-asf")
- , ("atc", "application/vnd.acucorp")
- , ("atom", "application/atom+xml")
- , ("atomcat", "application/atomcat+xml")
- , ("atomsvc", "application/atomsvc+xml")
- , ("atx", "application/vnd.antix.game-component")
- , ("au", "audio/basic")
- , ("avi", "video/x-msvideo")
- , ("aw", "application/applixware")
- , ("azf", "application/vnd.airzip.filesecure.azf")
- , ("azs", "application/vnd.airzip.filesecure.azs")
- , ("azw", "application/vnd.amazon.ebook")
- , ("bat", "application/x-msdownload")
- , ("bcpio", "application/x-bcpio")
- , ("bdf", "application/x-font-bdf")
- , ("bdm", "application/vnd.syncml.dm+wbxml")
- , ("bed", "application/vnd.realvnc.bed")
- , ("bh2", "application/vnd.fujitsu.oasysprs")
- , ("bin", "application/octet-stream")
- , ("blb", "application/x-blorb")
- , ("blorb", "application/x-blorb")
- , ("bmi", "application/vnd.bmi")
- , ("bmp", "image/bmp")
- , ("book", "application/vnd.framemaker")
- , ("box", "application/vnd.previewsystems.box")
- , ("boz", "application/x-bzip2")
- , ("bpk", "application/octet-stream")
- , ("btif", "image/prs.btif")
- , ("bz", "application/x-bzip")
- , ("bz2", "application/x-bzip2")
- , ("c", "text/x-c")
- , ("c11amc", "application/vnd.cluetrust.cartomobile-config")
- , ("c11amz", "application/vnd.cluetrust.cartomobile-config-pkg")
- , ("c4d", "application/vnd.clonk.c4group")
- , ("c4f", "application/vnd.clonk.c4group")
- , ("c4g", "application/vnd.clonk.c4group")
- , ("c4p", "application/vnd.clonk.c4group")
- , ("c4u", "application/vnd.clonk.c4group")
- , ("cab", "application/vnd.ms-cab-compressed")
- , ("caf", "audio/x-caf")
- , ("cap", "application/vnd.tcpdump.pcap")
- , ("car", "application/vnd.curl.car")
- , ("cat", "application/vnd.ms-pki.seccat")
- , ("cb7", "application/x-cbr")
- , ("cba", "application/x-cbr")
- , ("cbr", "application/x-cbr")
- , ("cbt", "application/x-cbr")
- , ("cbz", "application/x-cbr")
- , ("cc", "text/x-c")
- , ("cct", "application/x-director")
- , ("ccxml", "application/ccxml+xml")
- , ("cdbcmsg", "application/vnd.contact.cmsg")
- , ("cdf", "application/x-netcdf")
- , ("cdkey", "application/vnd.mediastation.cdkey")
- , ("cdmia", "application/cdmi-capability")
- , ("cdmic", "application/cdmi-container")
- , ("cdmid", "application/cdmi-domain")
- , ("cdmio", "application/cdmi-object")
- , ("cdmiq", "application/cdmi-queue")
- , ("cdx", "chemical/x-cdx")
- , ("cdxml", "application/vnd.chemdraw+xml")
- , ("cdy", "application/vnd.cinderella")
- , ("cer", "application/pkix-cert")
- , ("cfs", "application/x-cfs-compressed")
- , ("cgm", "image/cgm")
- , ("chat", "application/x-chat")
- , ("chm", "application/vnd.ms-htmlhelp")
- , ("chrt", "application/vnd.kde.kchart")
- , ("cif", "chemical/x-cif")
- , ("cii", "application/vnd.anser-web-certificate-issue-initiation")
- , ("cil", "application/vnd.ms-artgalry")
- , ("cla", "application/vnd.claymore")
- , ("class", "application/java-vm")
- , ("clkk", "application/vnd.crick.clicker.keyboard")
- , ("clkp", "application/vnd.crick.clicker.palette")
- , ("clkt", "application/vnd.crick.clicker.template")
- , ("clkw", "application/vnd.crick.clicker.wordbank")
- , ("clkx", "application/vnd.crick.clicker")
- , ("clp", "application/x-msclip")
- , ("cmc", "application/vnd.cosmocaller")
- , ("cmdf", "chemical/x-cmdf")
- , ("cml", "chemical/x-cml")
- , ("cmp", "application/vnd.yellowriver-custom-menu")
- , ("cmx", "image/x-cmx")
- , ("cod", "application/vnd.rim.cod")
- , ("com", "application/x-msdownload")
- , ("conf", "text/plain")
- , ("cpio", "application/x-cpio")
- , ("cpp", "text/x-c")
- , ("cpt", "application/mac-compactpro")
- , ("crd", "application/x-mscardfile")
- , ("crl", "application/pkix-crl")
- , ("crt", "application/x-x509-ca-cert")
- , ("cryptonote", "application/vnd.rig.cryptonote")
- , ("csh", "application/x-csh")
- , ("csml", "chemical/x-csml")
- , ("csp", "application/vnd.commonspace")
- , ("css", "text/css")
- , ("cst", "application/x-director")
- , ("csv", "text/csv")
- , ("cu", "application/cu-seeme")
- , ("curl", "text/vnd.curl")
- , ("cww", "application/prs.cww")
- , ("cxt", "application/x-director")
- , ("cxx", "text/x-c")
- , ("dae", "model/vnd.collada+xml")
- , ("daf", "application/vnd.mobius.daf")
- , ("dart", "application/vnd.dart")
- , ("dataless", "application/vnd.fdsn.seed")
- , ("davmount", "application/davmount+xml")
- , ("dbk", "application/docbook+xml")
- , ("dcr", "application/x-director")
- , ("dcurl", "text/vnd.curl.dcurl")
- , ("dd2", "application/vnd.oma.dd2+xml")
- , ("ddd", "application/vnd.fujixerox.ddd")
- , ("deb", "application/x-debian-package")
- , ("def", "text/plain")
- , ("deploy", "application/octet-stream")
- , ("der", "application/x-x509-ca-cert")
- , ("dfac", "application/vnd.dreamfactory")
- , ("dgc", "application/x-dgc-compressed")
- , ("dic", "text/x-c")
- , ("dir", "application/x-director")
- , ("dis", "application/vnd.mobius.dis")
- , ("dist", "application/octet-stream")
- , ("distz", "application/octet-stream")
- , ("djv", "image/vnd.djvu")
- , ("djvu", "image/vnd.djvu")
- , ("dll", "application/x-msdownload")
- , ("dmg", "application/x-apple-diskimage")
- , ("dmp", "application/vnd.tcpdump.pcap")
- , ("dms", "application/octet-stream")
- , ("dna", "application/vnd.dna")
- , ("doc", "application/msword")
- , ("docm", "application/vnd.ms-word.document.macroenabled.12")
- , ("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
- , ("dot", "application/msword")
- , ("dotm", "application/vnd.ms-word.template.macroenabled.12")
- , ("dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template")
- , ("dp", "application/vnd.osgi.dp")
- , ("dpg", "application/vnd.dpgraph")
- , ("dra", "audio/vnd.dra")
- , ("dsc", "text/prs.lines.tag")
- , ("dssc", "application/dssc+der")
- , ("dtb", "application/x-dtbook+xml")
- , ("dtd", "application/xml-dtd")
- , ("dts", "audio/vnd.dts")
- , ("dtshd", "audio/vnd.dts.hd")
- , ("dump", "application/octet-stream")
- , ("dvb", "video/vnd.dvb.file")
- , ("dvi", "application/x-dvi")
- , ("dwf", "model/vnd.dwf")
- , ("dwg", "image/vnd.dwg")
- , ("dxf", "image/vnd.dxf")
- , ("dxp", "application/vnd.spotfire.dxp")
- , ("dxr", "application/x-director")
- , ("ecelp4800", "audio/vnd.nuera.ecelp4800")
- , ("ecelp7470", "audio/vnd.nuera.ecelp7470")
- , ("ecelp9600", "audio/vnd.nuera.ecelp9600")
- , ("ecma", "application/ecmascript")
- , ("edm", "application/vnd.novadigm.edm")
- , ("edx", "application/vnd.novadigm.edx")
- , ("efif", "application/vnd.picsel")
- , ("ei6", "application/vnd.pg.osasli")
- , ("elc", "application/octet-stream")
- , ("emf", "application/x-msmetafile")
- , ("eml", "message/rfc822")
- , ("emma", "application/emma+xml")
- , ("emz", "application/x-msmetafile")
- , ("eol", "audio/vnd.digital-winds")
- , ("eot", "application/vnd.ms-fontobject")
- , ("eps", "application/postscript")
- , ("epub", "application/epub+zip")
- , ("es3", "application/vnd.eszigno3+xml")
- , ("esa", "application/vnd.osgi.subsystem")
- , ("esf", "application/vnd.epson.esf")
- , ("et3", "application/vnd.eszigno3+xml")
- , ("etx", "text/x-setext")
- , ("eva", "application/x-eva")
- , ("evy", "application/x-envoy")
- , ("exe", "application/x-msdownload")
- , ("exi", "application/exi")
- , ("ext", "application/vnd.novadigm.ext")
- , ("ez", "application/andrew-inset")
- , ("ez2", "application/vnd.ezpix-album")
- , ("ez3", "application/vnd.ezpix-package")
- , ("f", "text/x-fortran")
- , ("f4v", "video/x-f4v")
- , ("f77", "text/x-fortran")
- , ("f90", "text/x-fortran")
- , ("fbs", "image/vnd.fastbidsheet")
- , ("fcdt", "application/vnd.adobe.formscentral.fcdt")
- , ("fcs", "application/vnd.isac.fcs")
- , ("fdf", "application/vnd.fdf")
- , ("fe_launch", "application/vnd.denovo.fcselayout-link")
- , ("fg5", "application/vnd.fujitsu.oasysgp")
- , ("fgd", "application/x-director")
- , ("fh", "image/x-freehand")
- , ("fh4", "image/x-freehand")
- , ("fh5", "image/x-freehand")
- , ("fh7", "image/x-freehand")
- , ("fhc", "image/x-freehand")
- , ("fig", "application/x-xfig")
- , ("flac", "audio/x-flac")
- , ("fli", "video/x-fli")
- , ("flo", "application/vnd.micrografx.flo")
- , ("flv", "video/x-flv")
- , ("flw", "application/vnd.kde.kivio")
- , ("flx", "text/vnd.fmi.flexstor")
- , ("fly", "text/vnd.fly")
- , ("fm", "application/vnd.framemaker")
- , ("fnc", "application/vnd.frogans.fnc")
- , ("for", "text/x-fortran")
- , ("fpx", "image/vnd.fpx")
- , ("frame", "application/vnd.framemaker")
- , ("fsc", "application/vnd.fsc.weblaunch")
- , ("fst", "image/vnd.fst")
- , ("ftc", "application/vnd.fluxtime.clip")
- , ("fti", "application/vnd.anser-web-funds-transfer-initiation")
- , ("fvt", "video/vnd.fvt")
- , ("fxp", "application/vnd.adobe.fxp")
- , ("fxpl", "application/vnd.adobe.fxp")
- , ("fzs", "application/vnd.fuzzysheet")
- , ("g2w", "application/vnd.geoplan")
- , ("g3", "image/g3fax")
- , ("g3w", "application/vnd.geospace")
- , ("gac", "application/vnd.groove-account")
- , ("gam", "application/x-tads")
- , ("gbr", "application/rpki-ghostbusters")
- , ("gca", "application/x-gca-compressed")
- , ("gdl", "model/vnd.gdl")
- , ("geo", "application/vnd.dynageo")
- , ("gex", "application/vnd.geometry-explorer")
- , ("ggb", "application/vnd.geogebra.file")
- , ("ggt", "application/vnd.geogebra.tool")
- , ("ghf", "application/vnd.groove-help")
- , ("gif", "image/gif")
- , ("gim", "application/vnd.groove-identity-message")
- , ("gml", "application/gml+xml")
- , ("gmx", "application/vnd.gmx")
- , ("gnumeric", "application/x-gnumeric")
- , ("gph", "application/vnd.flographit")
- , ("gpx", "application/gpx+xml")
- , ("gqf", "application/vnd.grafeq")
- , ("gqs", "application/vnd.grafeq")
- , ("gram", "application/srgs")
- , ("gramps", "application/x-gramps-xml")
- , ("gre", "application/vnd.geometry-explorer")
- , ("grv", "application/vnd.groove-injector")
- , ("grxml", "application/srgs+xml")
- , ("gsf", "application/x-font-ghostscript")
- , ("gtar", "application/x-gtar")
- , ("gtm", "application/vnd.groove-tool-message")
- , ("gtw", "model/vnd.gtw")
- , ("gv", "text/vnd.graphviz")
- , ("gxf", "application/gxf")
- , ("gxt", "application/vnd.geonext")
- , ("h", "text/x-c")
- , ("h261", "video/h261")
- , ("h263", "video/h263")
- , ("h264", "video/h264")
- , ("hal", "application/vnd.hal+xml")
- , ("hbci", "application/vnd.hbci")
- , ("hdf", "application/x-hdf")
- , ("hh", "text/x-c")
- , ("hlp", "application/winhlp")
- , ("hpgl", "application/vnd.hp-hpgl")
- , ("hpid", "application/vnd.hp-hpid")
- , ("hps", "application/vnd.hp-hps")
- , ("hqx", "application/mac-binhex40")
- , ("htke", "application/vnd.kenameaapp")
- , ("htm", "text/html")
- , ("html", "text/html")
- , ("hvd", "application/vnd.yamaha.hv-dic")
- , ("hvp", "application/vnd.yamaha.hv-voice")
- , ("hvs", "application/vnd.yamaha.hv-script")
- , ("i2g", "application/vnd.intergeo")
- , ("icc", "application/vnd.iccprofile")
- , ("ice", "x-conference/x-cooltalk")
- , ("icm", "application/vnd.iccprofile")
- , ("ico", "image/x-icon")
- , ("ics", "text/calendar")
- , ("ief", "image/ief")
- , ("ifb", "text/calendar")
- , ("ifm", "application/vnd.shana.informed.formdata")
- , ("iges", "model/iges")
- , ("igl", "application/vnd.igloader")
- , ("igm", "application/vnd.insors.igm")
- , ("igs", "model/iges")
- , ("igx", "application/vnd.micrografx.igx")
- , ("iif", "application/vnd.shana.informed.interchange")
- , ("imp", "application/vnd.accpac.simply.imp")
- , ("ims", "application/vnd.ms-ims")
- , ("in", "text/plain")
- , ("ink", "application/inkml+xml")
- , ("inkml", "application/inkml+xml")
- , ("install", "application/x-install-instructions")
- , ("iota", "application/vnd.astraea-software.iota")
- , ("ipfix", "application/ipfix")
- , ("ipk", "application/vnd.shana.informed.package")
- , ("irm", "application/vnd.ibm.rights-management")
- , ("irp", "application/vnd.irepository.package+xml")
- , ("iso", "application/x-iso9660-image")
- , ("itp", "application/vnd.shana.informed.formtemplate")
- , ("ivp", "application/vnd.immervision-ivp")
- , ("ivu", "application/vnd.immervision-ivu")
- , ("jad", "text/vnd.sun.j2me.app-descriptor")
- , ("jam", "application/vnd.jam")
- , ("jar", "application/java-archive")
- , ("java", "text/x-java-source")
- , ("jisp", "application/vnd.jisp")
- , ("jlt", "application/vnd.hp-jlyt")
- , ("jnlp", "application/x-java-jnlp-file")
- , ("joda", "application/vnd.joost.joda-archive")
- , ("jpe", "image/jpeg")
- , ("jpeg", "image/jpeg")
- , ("jpg", "image/jpeg")
- , ("jpgm", "video/jpm")
- , ("jpgv", "video/jpeg")
- , ("jpm", "video/jpm")
- , ("js", "application/javascript")
- , ("json", "application/json")
- , ("jsonml", "application/jsonml+json")
- , ("kar", "audio/midi")
- , ("karbon", "application/vnd.kde.karbon")
- , ("kfo", "application/vnd.kde.kformula")
- , ("kia", "application/vnd.kidspiration")
- , ("kml", "application/vnd.google-earth.kml+xml")
- , ("kmz", "application/vnd.google-earth.kmz")
- , ("kne", "application/vnd.kinar")
- , ("knp", "application/vnd.kinar")
- , ("kon", "application/vnd.kde.kontour")
- , ("kpr", "application/vnd.kde.kpresenter")
- , ("kpt", "application/vnd.kde.kpresenter")
- , ("kpxx", "application/vnd.ds-keypoint")
- , ("ksp", "application/vnd.kde.kspread")
- , ("ktr", "application/vnd.kahootz")
- , ("ktx", "image/ktx")
- , ("ktz", "application/vnd.kahootz")
- , ("kwd", "application/vnd.kde.kword")
- , ("kwt", "application/vnd.kde.kword")
- , ("lasxml", "application/vnd.las.las+xml")
- , ("latex", "application/x-latex")
- , ("lbd", "application/vnd.llamagraphics.life-balance.desktop")
- , ("lbe", "application/vnd.llamagraphics.life-balance.exchange+xml")
- , ("les", "application/vnd.hhe.lesson-player")
- , ("lha", "application/x-lzh-compressed")
- , ("link66", "application/vnd.route66.link66+xml")
- , ("list", "text/plain")
- , ("list3820", "application/vnd.ibm.modcap")
- , ("listafp", "application/vnd.ibm.modcap")
- , ("lnk", "application/x-ms-shortcut")
- , ("log", "text/plain")
- , ("lostxml", "application/lost+xml")
- , ("lrf", "application/octet-stream")
- , ("lrm", "application/vnd.ms-lrm")
- , ("ltf", "application/vnd.frogans.ltf")
- , ("lvp", "audio/vnd.lucent.voice")
- , ("lwp", "application/vnd.lotus-wordpro")
- , ("lzh", "application/x-lzh-compressed")
- , ("m13", "application/x-msmediaview")
- , ("m14", "application/x-msmediaview")
- , ("m1v", "video/mpeg")
- , ("m21", "application/mp21")
- , ("m2a", "audio/mpeg")
- , ("m2v", "video/mpeg")
- , ("m3a", "audio/mpeg")
- , ("m3u", "audio/x-mpegurl")
- , ("m3u8", "application/vnd.apple.mpegurl")
- , ("m4a", "audio/mp4")
- , ("m4u", "video/vnd.mpegurl")
- , ("m4v", "video/x-m4v")
- , ("ma", "application/mathematica")
- , ("mads", "application/mads+xml")
- , ("mag", "application/vnd.ecowin.chart")
- , ("maker", "application/vnd.framemaker")
- , ("man", "text/troff")
- , ("mar", "application/octet-stream")
- , ("mathml", "application/mathml+xml")
- , ("mb", "application/mathematica")
- , ("mbk", "application/vnd.mobius.mbk")
- , ("mbox", "application/mbox")
- , ("mc1", "application/vnd.medcalcdata")
- , ("mcd", "application/vnd.mcd")
- , ("mcurl", "text/vnd.curl.mcurl")
- , ("mdb", "application/x-msaccess")
- , ("mdi", "image/vnd.ms-modi")
- , ("me", "text/troff")
- , ("mesh", "model/mesh")
- , ("meta4", "application/metalink4+xml")
- , ("metalink", "application/metalink+xml")
- , ("mets", "application/mets+xml")
- , ("mfm", "application/vnd.mfmp")
- , ("mft", "application/rpki-manifest")
- , ("mgp", "application/vnd.osgeo.mapguide.package")
- , ("mgz", "application/vnd.proteus.magazine")
- , ("mid", "audio/midi")
- , ("midi", "audio/midi")
- , ("mie", "application/x-mie")
- , ("mif", "application/vnd.mif")
- , ("mime", "message/rfc822")
- , ("mj2", "video/mj2")
- , ("mjp2", "video/mj2")
- , ("mk3d", "video/x-matroska")
- , ("mka", "audio/x-matroska")
- , ("mks", "video/x-matroska")
- , ("mkv", "video/x-matroska")
- , ("mlp", "application/vnd.dolby.mlp")
- , ("mmd", "application/vnd.chipnuts.karaoke-mmd")
- , ("mmf", "application/vnd.smaf")
- , ("mmr", "image/vnd.fujixerox.edmics-mmr")
- , ("mng", "video/x-mng")
- , ("mny", "application/x-msmoney")
- , ("mobi", "application/x-mobipocket-ebook")
- , ("mods", "application/mods+xml")
- , ("mov", "video/quicktime")
- , ("movie", "video/x-sgi-movie")
- , ("mp2", "audio/mpeg")
- , ("mp21", "application/mp21")
- , ("mp2a", "audio/mpeg")
- , ("mp3", "audio/mpeg")
- , ("mp4", "video/mp4")
- , ("mp4a", "audio/mp4")
- , ("mp4s", "application/mp4")
- , ("mp4v", "video/mp4")
- , ("mpc", "application/vnd.mophun.certificate")
- , ("mpe", "video/mpeg")
- , ("mpeg", "video/mpeg")
- , ("mpg", "video/mpeg")
- , ("mpg4", "video/mp4")
- , ("mpga", "audio/mpeg")
- , ("mpkg", "application/vnd.apple.installer+xml")
- , ("mpm", "application/vnd.blueice.multipass")
- , ("mpn", "application/vnd.mophun.application")
- , ("mpp", "application/vnd.ms-project")
- , ("mpt", "application/vnd.ms-project")
- , ("mpy", "application/vnd.ibm.minipay")
- , ("mqy", "application/vnd.mobius.mqy")
- , ("mrc", "application/marc")
- , ("mrcx", "application/marcxml+xml")
- , ("ms", "text/troff")
- , ("mscml", "application/mediaservercontrol+xml")
- , ("mseed", "application/vnd.fdsn.mseed")
- , ("mseq", "application/vnd.mseq")
- , ("msf", "application/vnd.epson.msf")
- , ("msh", "model/mesh")
- , ("msi", "application/x-msdownload")
- , ("msl", "application/vnd.mobius.msl")
- , ("msty", "application/vnd.muvee.style")
- , ("mts", "model/vnd.mts")
- , ("mus", "application/vnd.musician")
- , ("musicxml", "application/vnd.recordare.musicxml+xml")
- , ("mvb", "application/x-msmediaview")
- , ("mwf", "application/vnd.mfer")
- , ("mxf", "application/mxf")
- , ("mxl", "application/vnd.recordare.musicxml")
- , ("mxml", "application/xv+xml")
- , ("mxs", "application/vnd.triscape.mxs")
- , ("mxu", "video/vnd.mpegurl")
- , ("n-gage", "application/vnd.nokia.n-gage.symbian.install")
- , ("n3", "text/n3")
- , ("nb", "application/mathematica")
- , ("nbp", "application/vnd.wolfram.player")
- , ("nc", "application/x-netcdf")
- , ("ncx", "application/x-dtbncx+xml")
- , ("nfo", "text/x-nfo")
- , ("ngdat", "application/vnd.nokia.n-gage.data")
- , ("nitf", "application/vnd.nitf")
- , ("nlu", "application/vnd.neurolanguage.nlu")
- , ("nml", "application/vnd.enliven")
- , ("nnd", "application/vnd.noblenet-directory")
- , ("nns", "application/vnd.noblenet-sealer")
- , ("nnw", "application/vnd.noblenet-web")
- , ("npx", "image/vnd.net-fpx")
- , ("nsc", "application/x-conference")
- , ("nsf", "application/vnd.lotus-notes")
- , ("ntf", "application/vnd.nitf")
- , ("nzb", "application/x-nzb")
- , ("oa2", "application/vnd.fujitsu.oasys2")
- , ("oa3", "application/vnd.fujitsu.oasys3")
- , ("oas", "application/vnd.fujitsu.oasys")
- , ("obd", "application/x-msbinder")
- , ("obj", "application/x-tgif")
- , ("oda", "application/oda")
- , ("odb", "application/vnd.oasis.opendocument.database")
- , ("odc", "application/vnd.oasis.opendocument.chart")
- , ("odf", "application/vnd.oasis.opendocument.formula")
- , ("odft", "application/vnd.oasis.opendocument.formula-template")
- , ("odg", "application/vnd.oasis.opendocument.graphics")
- , ("odi", "application/vnd.oasis.opendocument.image")
- , ("odm", "application/vnd.oasis.opendocument.text-master")
- , ("odp", "application/vnd.oasis.opendocument.presentation")
- , ("ods", "application/vnd.oasis.opendocument.spreadsheet")
- , ("odt", "application/vnd.oasis.opendocument.text")
- , ("oga", "audio/ogg")
- , ("ogg", "audio/ogg")
- , ("ogv", "video/ogg")
- , ("ogx", "application/ogg")
- , ("omdoc", "application/omdoc+xml")
- , ("onepkg", "application/onenote")
- , ("onetmp", "application/onenote")
- , ("onetoc", "application/onenote")
- , ("onetoc2", "application/onenote")
- , ("opf", "application/oebps-package+xml")
- , ("opml", "text/x-opml")
- , ("oprc", "application/vnd.palm")
- , ("opus", "audio/ogg")
- , ("org", "application/vnd.lotus-organizer")
- , ("osf", "application/vnd.yamaha.openscoreformat")
- , ("osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml")
- , ("otc", "application/vnd.oasis.opendocument.chart-template")
- , ("otf", "font/otf")
- , ("otg", "application/vnd.oasis.opendocument.graphics-template")
- , ("oth", "application/vnd.oasis.opendocument.text-web")
- , ("oti", "application/vnd.oasis.opendocument.image-template")
- , ("otp", "application/vnd.oasis.opendocument.presentation-template")
- , ("ots", "application/vnd.oasis.opendocument.spreadsheet-template")
- , ("ott", "application/vnd.oasis.opendocument.text-template")
- , ("oxps", "application/oxps")
- , ("oxt", "application/vnd.openofficeorg.extension")
- , ("p", "text/x-pascal")
- , ("p10", "application/pkcs10")
- , ("p12", "application/x-pkcs12")
- , ("p7b", "application/x-pkcs7-certificates")
- , ("p7c", "application/pkcs7-mime")
- , ("p7m", "application/pkcs7-mime")
- , ("p7r", "application/x-pkcs7-certreqresp")
- , ("p7s", "application/pkcs7-signature")
- , ("p8", "application/pkcs8")
- , ("pas", "text/x-pascal")
- , ("paw", "application/vnd.pawaafile")
- , ("pbd", "application/vnd.powerbuilder6")
- , ("pbm", "image/x-portable-bitmap")
- , ("pcap", "application/vnd.tcpdump.pcap")
- , ("pcf", "application/x-font-pcf")
- , ("pcl", "application/vnd.hp-pcl")
- , ("pclxl", "application/vnd.hp-pclxl")
- , ("pct", "image/x-pict")
- , ("pcurl", "application/vnd.curl.pcurl")
- , ("pcx", "image/x-pcx")
- , ("pdb", "application/vnd.palm")
- , ("pdf", "application/pdf")
- , ("pfa", "application/x-font-type1")
- , ("pfb", "application/x-font-type1")
- , ("pfm", "application/x-font-type1")
- , ("pfr", "application/font-tdpfr")
- , ("pfx", "application/x-pkcs12")
- , ("pgm", "image/x-portable-graymap")
- , ("pgn", "application/x-chess-pgn")
- , ("pgp", "application/pgp-encrypted")
- , ("pic", "image/x-pict")
- , ("pkg", "application/octet-stream")
- , ("pki", "application/pkixcmp")
- , ("pkipath", "application/pkix-pkipath")
- , ("plb", "application/vnd.3gpp.pic-bw-large")
- , ("plc", "application/vnd.mobius.plc")
- , ("plf", "application/vnd.pocketlearn")
- , ("pls", "application/pls+xml")
- , ("pml", "application/vnd.ctc-posml")
- , ("png", "image/png")
- , ("pnm", "image/x-portable-anymap")
- , ("portpkg", "application/vnd.macports.portpkg")
- , ("pot", "application/vnd.ms-powerpoint")
- , ("potm", "application/vnd.ms-powerpoint.template.macroenabled.12")
- , ("potx", "application/vnd.openxmlformats-officedocument.presentationml.template")
- , ("ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12")
- , ("ppd", "application/vnd.cups-ppd")
- , ("ppm", "image/x-portable-pixmap")
- , ("pps", "application/vnd.ms-powerpoint")
- , ("ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12")
- , ("ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow")
- , ("ppt", "application/vnd.ms-powerpoint")
- , ("pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12")
- , ("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation")
- , ("pqa", "application/vnd.palm")
- , ("prc", "application/x-mobipocket-ebook")
- , ("pre", "application/vnd.lotus-freelance")
- , ("prf", "application/pics-rules")
- , ("ps", "application/postscript")
- , ("psb", "application/vnd.3gpp.pic-bw-small")
- , ("psd", "image/vnd.adobe.photoshop")
- , ("psf", "application/x-font-linux-psf")
- , ("pskcxml", "application/pskc+xml")
- , ("ptid", "application/vnd.pvi.ptid1")
- , ("pub", "application/x-mspublisher")
- , ("pvb", "application/vnd.3gpp.pic-bw-var")
- , ("pwn", "application/vnd.3m.post-it-notes")
- , ("pya", "audio/vnd.ms-playready.media.pya")
- , ("pyv", "video/vnd.ms-playready.media.pyv")
- , ("qam", "application/vnd.epson.quickanime")
- , ("qbo", "application/vnd.intu.qbo")
- , ("qfx", "application/vnd.intu.qfx")
- , ("qps", "application/vnd.publishare-delta-tree")
- , ("qt", "video/quicktime")
- , ("qwd", "application/vnd.quark.quarkxpress")
- , ("qwt", "application/vnd.quark.quarkxpress")
- , ("qxb", "application/vnd.quark.quarkxpress")
- , ("qxd", "application/vnd.quark.quarkxpress")
- , ("qxl", "application/vnd.quark.quarkxpress")
- , ("qxt", "application/vnd.quark.quarkxpress")
- , ("ra", "audio/x-pn-realaudio")
- , ("ram", "audio/x-pn-realaudio")
- , ("rar", "application/x-rar-compressed")
- , ("ras", "image/x-cmu-raster")
- , ("rcprofile", "application/vnd.ipunplugged.rcprofile")
- , ("rdf", "application/rdf+xml")
- , ("rdz", "application/vnd.data-vision.rdz")
- , ("rep", "application/vnd.businessobjects")
- , ("res", "application/x-dtbresource+xml")
- , ("rgb", "image/x-rgb")
- , ("rif", "application/reginfo+xml")
- , ("rip", "audio/vnd.rip")
- , ("ris", "application/x-research-info-systems")
- , ("rl", "application/resource-lists+xml")
- , ("rlc", "image/vnd.fujixerox.edmics-rlc")
- , ("rld", "application/resource-lists-diff+xml")
- , ("rm", "application/vnd.rn-realmedia")
- , ("rmi", "audio/midi")
- , ("rmp", "audio/x-pn-realaudio-plugin")
- , ("rms", "application/vnd.jcp.javame.midlet-rms")
- , ("rmvb", "application/vnd.rn-realmedia-vbr")
- , ("rnc", "application/relax-ng-compact-syntax")
- , ("roa", "application/rpki-roa")
- , ("roff", "text/troff")
- , ("rp9", "application/vnd.cloanto.rp9")
- , ("rpss", "application/vnd.nokia.radio-presets")
- , ("rpst", "application/vnd.nokia.radio-preset")
- , ("rq", "application/sparql-query")
- , ("rs", "application/rls-services+xml")
- , ("rsd", "application/rsd+xml")
- , ("rss", "application/rss+xml")
- , ("rtf", "application/rtf")
- , ("rtx", "text/richtext")
- , ("s", "text/x-asm")
- , ("s3m", "audio/s3m")
- , ("saf", "application/vnd.yamaha.smaf-audio")
- , ("sbml", "application/sbml+xml")
- , ("sc", "application/vnd.ibm.secure-container")
- , ("scd", "application/x-msschedule")
- , ("scm", "application/vnd.lotus-screencam")
- , ("scq", "application/scvp-cv-request")
- , ("scs", "application/scvp-cv-response")
- , ("scurl", "text/vnd.curl.scurl")
- , ("sda", "application/vnd.stardivision.draw")
- , ("sdc", "application/vnd.stardivision.calc")
- , ("sdd", "application/vnd.stardivision.impress")
- , ("sdkd", "application/vnd.solent.sdkm+xml")
- , ("sdkm", "application/vnd.solent.sdkm+xml")
- , ("sdp", "application/sdp")
- , ("sdw", "application/vnd.stardivision.writer")
- , ("see", "application/vnd.seemail")
- , ("seed", "application/vnd.fdsn.seed")
- , ("sema", "application/vnd.sema")
- , ("semd", "application/vnd.semd")
- , ("semf", "application/vnd.semf")
- , ("ser", "application/java-serialized-object")
- , ("setpay", "application/set-payment-initiation")
- , ("setreg", "application/set-registration-initiation")
- , ("sfd-hdstx", "application/vnd.hydrostatix.sof-data")
- , ("sfs", "application/vnd.spotfire.sfs")
- , ("sfv", "text/x-sfv")
- , ("sgi", "image/sgi")
- , ("sgl", "application/vnd.stardivision.writer-global")
- , ("sgm", "text/sgml")
- , ("sgml", "text/sgml")
- , ("sh", "application/x-sh")
- , ("shar", "application/x-shar")
- , ("shf", "application/shf+xml")
- , ("sid", "image/x-mrsid-image")
- , ("sig", "application/pgp-signature")
- , ("sil", "audio/silk")
- , ("silo", "model/mesh")
- , ("sis", "application/vnd.symbian.install")
- , ("sisx", "application/vnd.symbian.install")
- , ("sit", "application/x-stuffit")
- , ("sitx", "application/x-stuffitx")
- , ("skd", "application/vnd.koan")
- , ("skm", "application/vnd.koan")
- , ("skp", "application/vnd.koan")
- , ("skt", "application/vnd.koan")
- , ("sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12")
- , ("sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide")
- , ("slt", "application/vnd.epson.salt")
- , ("sm", "application/vnd.stepmania.stepchart")
- , ("smf", "application/vnd.stardivision.math")
- , ("smi", "application/smil+xml")
- , ("smil", "application/smil+xml")
- , ("smv", "video/x-smv")
- , ("smzip", "application/vnd.stepmania.package")
- , ("snd", "audio/basic")
- , ("snf", "application/x-font-snf")
- , ("so", "application/octet-stream")
- , ("spc", "application/x-pkcs7-certificates")
- , ("spf", "application/vnd.yamaha.smaf-phrase")
- , ("spl", "application/x-futuresplash")
- , ("spot", "text/vnd.in3d.spot")
- , ("spp", "application/scvp-vp-response")
- , ("spq", "application/scvp-vp-request")
- , ("spx", "audio/ogg")
- , ("sql", "application/x-sql")
- , ("src", "application/x-wais-source")
- , ("srt", "application/x-subrip")
- , ("sru", "application/sru+xml")
- , ("srx", "application/sparql-results+xml")
- , ("ssdl", "application/ssdl+xml")
- , ("sse", "application/vnd.kodak-descriptor")
- , ("ssf", "application/vnd.epson.ssf")
- , ("ssml", "application/ssml+xml")
- , ("st", "application/vnd.sailingtracker.track")
- , ("stc", "application/vnd.sun.xml.calc.template")
- , ("std", "application/vnd.sun.xml.draw.template")
- , ("stf", "application/vnd.wt.stf")
- , ("sti", "application/vnd.sun.xml.impress.template")
- , ("stk", "application/hyperstudio")
- , ("stl", "application/vnd.ms-pki.stl")
- , ("str", "application/vnd.pg.format")
- , ("stw", "application/vnd.sun.xml.writer.template")
- , ("sub", "image/vnd.dvb.subtitle")
- , ("sub", "text/vnd.dvb.subtitle")
- , ("sus", "application/vnd.sus-calendar")
- , ("susp", "application/vnd.sus-calendar")
- , ("sv4cpio", "application/x-sv4cpio")
- , ("sv4crc", "application/x-sv4crc")
- , ("svc", "application/vnd.dvb.service")
- , ("svd", "application/vnd.svd")
- , ("svg", "image/svg+xml")
- , ("svgz", "image/svg+xml")
- , ("swa", "application/x-director")
- , ("swf", "application/x-shockwave-flash")
- , ("swi", "application/vnd.aristanetworks.swi")
- , ("sxc", "application/vnd.sun.xml.calc")
- , ("sxd", "application/vnd.sun.xml.draw")
- , ("sxg", "application/vnd.sun.xml.writer.global")
- , ("sxi", "application/vnd.sun.xml.impress")
- , ("sxm", "application/vnd.sun.xml.math")
- , ("sxw", "application/vnd.sun.xml.writer")
- , ("t", "text/troff")
- , ("t3", "application/x-t3vm-image")
- , ("taglet", "application/vnd.mynfc")
- , ("tao", "application/vnd.tao.intent-module-archive")
- , ("tar", "application/x-tar")
- , ("tcap", "application/vnd.3gpp2.tcap")
- , ("tcl", "application/x-tcl")
- , ("teacher", "application/vnd.smart.teacher")
- , ("tei", "application/tei+xml")
- , ("teicorpus", "application/tei+xml")
- , ("tex", "application/x-tex")
- , ("texi", "application/x-texinfo")
- , ("texinfo", "application/x-texinfo")
- , ("text", "text/plain")
- , ("tfi", "application/thraud+xml")
- , ("tfm", "application/x-tex-tfm")
- , ("tga", "image/x-tga")
- , ("thmx", "application/vnd.ms-officetheme")
- , ("tif", "image/tiff")
- , ("tiff", "image/tiff")
- , ("tmo", "application/vnd.tmobile-livetv")
- , ("torrent", "application/x-bittorrent")
- , ("tpl", "application/vnd.groove-tool-template")
- , ("tpt", "application/vnd.trid.tpt")
- , ("tr", "text/troff")
- , ("tra", "application/vnd.trueapp")
- , ("trm", "application/x-msterminal")
- , ("tsd", "application/timestamped-data")
- , ("tsv", "text/tab-separated-values")
- , ("ttc", "font/collection")
- , ("ttf", "font/ttf")
- , ("ttl", "text/turtle")
- , ("twd", "application/vnd.simtech-mindmapper")
- , ("twds", "application/vnd.simtech-mindmapper")
- , ("txd", "application/vnd.genomatix.tuxedo")
- , ("txf", "application/vnd.mobius.txf")
- , ("txt", "text/plain")
- , ("u32", "application/x-authorware-bin")
- , ("udeb", "application/x-debian-package")
- , ("ufd", "application/vnd.ufdl")
- , ("ufdl", "application/vnd.ufdl")
- , ("ulx", "application/x-glulx")
- , ("umj", "application/vnd.umajin")
- , ("unityweb", "application/vnd.unity")
- , ("uoml", "application/vnd.uoml+xml")
- , ("uri", "text/uri-list")
- , ("uris", "text/uri-list")
- , ("urls", "text/uri-list")
- , ("ustar", "application/x-ustar")
- , ("utz", "application/vnd.uiq.theme")
- , ("uu", "text/x-uuencode")
- , ("uva", "audio/vnd.dece.audio")
- , ("uvd", "application/vnd.dece.data")
- , ("uvf", "application/vnd.dece.data")
- , ("uvg", "image/vnd.dece.graphic")
- , ("uvh", "video/vnd.dece.hd")
- , ("uvi", "image/vnd.dece.graphic")
- , ("uvm", "video/vnd.dece.mobile")
- , ("uvp", "video/vnd.dece.pd")
- , ("uvs", "video/vnd.dece.sd")
- , ("uvt", "application/vnd.dece.ttml+xml")
- , ("uvu", "video/vnd.uvvu.mp4")
- , ("uvv", "video/vnd.dece.video")
- , ("uvva", "audio/vnd.dece.audio")
- , ("uvvd", "application/vnd.dece.data")
- , ("uvvf", "application/vnd.dece.data")
- , ("uvvg", "image/vnd.dece.graphic")
- , ("uvvh", "video/vnd.dece.hd")
- , ("uvvi", "image/vnd.dece.graphic")
- , ("uvvm", "video/vnd.dece.mobile")
- , ("uvvp", "video/vnd.dece.pd")
- , ("uvvs", "video/vnd.dece.sd")
- , ("uvvt", "application/vnd.dece.ttml+xml")
- , ("uvvu", "video/vnd.uvvu.mp4")
- , ("uvvv", "video/vnd.dece.video")
- , ("uvvx", "application/vnd.dece.unspecified")
- , ("uvvz", "application/vnd.dece.zip")
- , ("uvx", "application/vnd.dece.unspecified")
- , ("uvz", "application/vnd.dece.zip")
- , ("vcard", "text/vcard")
- , ("vcd", "application/x-cdlink")
- , ("vcf", "text/x-vcard")
- , ("vcg", "application/vnd.groove-vcard")
- , ("vcs", "text/x-vcalendar")
- , ("vcx", "application/vnd.vcx")
- , ("vis", "application/vnd.visionary")
- , ("viv", "video/vnd.vivo")
- , ("vob", "video/x-ms-vob")
- , ("vor", "application/vnd.stardivision.writer")
- , ("vox", "application/x-authorware-bin")
- , ("vrml", "model/vrml")
- , ("vsd", "application/vnd.visio")
- , ("vsf", "application/vnd.vsf")
- , ("vss", "application/vnd.visio")
- , ("vst", "application/vnd.visio")
- , ("vsw", "application/vnd.visio")
- , ("vtu", "model/vnd.vtu")
- , ("vxml", "application/voicexml+xml")
- , ("w3d", "application/x-director")
- , ("wad", "application/x-doom")
- , ("wav", "audio/x-wav")
- , ("wax", "audio/x-ms-wax")
- , ("wbmp", "image/vnd.wap.wbmp")
- , ("wbs", "application/vnd.criticaltools.wbs+xml")
- , ("wbxml", "application/vnd.wap.wbxml")
- , ("wcm", "application/vnd.ms-works")
- , ("wdb", "application/vnd.ms-works")
- , ("wdp", "image/vnd.ms-photo")
- , ("weba", "audio/webm")
- , ("webm", "video/webm")
- , ("webp", "image/webp")
- , ("wg", "application/vnd.pmi.widget")
- , ("wgt", "application/widget")
- , ("wks", "application/vnd.ms-works")
- , ("wm", "video/x-ms-wm")
- , ("wma", "audio/x-ms-wma")
- , ("wmd", "application/x-ms-wmd")
- , ("wmf", "application/x-msmetafile")
- , ("wml", "text/vnd.wap.wml")
- , ("wmlc", "application/vnd.wap.wmlc")
- , ("wmls", "text/vnd.wap.wmlscript")
- , ("wmlsc", "application/vnd.wap.wmlscriptc")
- , ("wmv", "video/x-ms-wmv")
- , ("wmx", "video/x-ms-wmx")
- , ("wmz", "application/x-ms-wmz")
- , ("wmz", "application/x-msmetafile")
- , ("woff", "font/woff")
- , ("woff2", "font/woff2")
- , ("wpd", "application/vnd.wordperfect")
- , ("wpl", "application/vnd.ms-wpl")
- , ("wps", "application/vnd.ms-works")
- , ("wqd", "application/vnd.wqd")
- , ("wri", "application/x-mswrite")
- , ("wrl", "model/vrml")
- , ("wsdl", "application/wsdl+xml")
- , ("wspolicy", "application/wspolicy+xml")
- , ("wtb", "application/vnd.webturbo")
- , ("wvx", "video/x-ms-wvx")
- , ("x32", "application/x-authorware-bin")
- , ("x3d", "model/x3d+xml")
- , ("x3db", "model/x3d+binary")
- , ("x3dbz", "model/x3d+binary")
- , ("x3dv", "model/x3d+vrml")
- , ("x3dvz", "model/x3d+vrml")
- , ("x3dz", "model/x3d+xml")
- , ("xaml", "application/xaml+xml")
- , ("xap", "application/x-silverlight-app")
- , ("xar", "application/vnd.xara")
- , ("xbap", "application/x-ms-xbap")
- , ("xbd", "application/vnd.fujixerox.docuworks.binder")
- , ("xbm", "image/x-xbitmap")
- , ("xdf", "application/xcap-diff+xml")
- , ("xdm", "application/vnd.syncml.dm+xml")
- , ("xdp", "application/vnd.adobe.xdp+xml")
- , ("xdssc", "application/dssc+xml")
- , ("xdw", "application/vnd.fujixerox.docuworks")
- , ("xenc", "application/xenc+xml")
- , ("xer", "application/patch-ops-error+xml")
- , ("xfdf", "application/vnd.adobe.xfdf")
- , ("xfdl", "application/vnd.xfdl")
- , ("xht", "application/xhtml+xml")
- , ("xhtml", "application/xhtml+xml")
- , ("xhvml", "application/xv+xml")
- , ("xif", "image/vnd.xiff")
- , ("xla", "application/vnd.ms-excel")
- , ("xlam", "application/vnd.ms-excel.addin.macroenabled.12")
- , ("xlc", "application/vnd.ms-excel")
- , ("xlf", "application/x-xliff+xml")
- , ("xlm", "application/vnd.ms-excel")
- , ("xls", "application/vnd.ms-excel")
- , ("xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12")
- , ("xlsm", "application/vnd.ms-excel.sheet.macroenabled.12")
- , ("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
- , ("xlt", "application/vnd.ms-excel")
- , ("xltm", "application/vnd.ms-excel.template.macroenabled.12")
- , ("xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template")
- , ("xlw", "application/vnd.ms-excel")
- , ("xm", "audio/xm")
- , ("xml", "application/xml")
- , ("xo", "application/vnd.olpc-sugar")
- , ("xop", "application/xop+xml")
- , ("xpi", "application/x-xpinstall")
- , ("xpl", "application/xproc+xml")
- , ("xpm", "image/x-xpixmap")
- , ("xpr", "application/vnd.is-xpr")
- , ("xps", "application/vnd.ms-xpsdocument")
- , ("xpw", "application/vnd.intercon.formnet")
- , ("xpx", "application/vnd.intercon.formnet")
- , ("xsl", "application/xml")
- , ("xslt", "application/xslt+xml")
- , ("xsm", "application/vnd.syncml+xml")
- , ("xspf", "application/xspf+xml")
- , ("xul", "application/vnd.mozilla.xul+xml")
- , ("xvm", "application/xv+xml")
- , ("xvml", "application/xv+xml")
- , ("xwd", "image/x-xwindowdump")
- , ("xyz", "chemical/x-xyz")
- , ("xz", "application/x-xz")
- , ("yang", "application/yang")
- , ("yin", "application/yin+xml")
- , ("z1", "application/x-zmachine")
- , ("z2", "application/x-zmachine")
- , ("z3", "application/x-zmachine")
- , ("z4", "application/x-zmachine")
- , ("z5", "application/x-zmachine")
- , ("z6", "application/x-zmachine")
- , ("z7", "application/x-zmachine")
- , ("z8", "application/x-zmachine")
- , ("zaz", "application/vnd.zzazz.deck+xml")
- , ("zip", "application/zip")
- , ("zir", "application/vnd.zul")
- , ("zirz", "application/vnd.zul")
- , ("zmm", "application/vnd.handheld-entertainment+xml")
- ]
\ No newline at end of file
diff --git a/Test/FileStorage/MimeTypesSpec.hs b/Test/FileStorage/MimeTypesSpec.hs
deleted file mode 100644
index ac15242d1..000000000
--- a/Test/FileStorage/MimeTypesSpec.hs
+++ /dev/null
@@ -1,20 +0,0 @@
-{-|
-Module: Test.FileStorage.MimeTypesSpec
-Copyright: (c) digitally induced GmbH, 2021
--}
-module Test.FileStorage.MimeTypesSpec where
-
-import Test.Hspec
-import IHP.Prelude
-import IHP.FileStorage.MimeTypes
-
-tests = do
- describe "IHP.FileStorage.MimeTypes" do
- describe "guessMimeType" do
- it "return common mime types" do
- guessMimeType "test.jpg" `shouldBe` "image/jpeg"
- guessMimeType "test.txt" `shouldBe` "text/plain"
- it "returns application/octet-stream for unknown files" do
- guessMimeType "unknown" `shouldBe` "application/octet-stream"
- guessMimeType "test.hztr43ed" `shouldBe` "application/octet-stream"
- guessMimeType "" `shouldBe` "application/octet-stream"
\ No newline at end of file
diff --git a/Test/Main.hs b/Test/Main.hs
index 7a03cfb2c..5e1f48ff8 100644
--- a/Test/Main.hs
+++ b/Test/Main.hs
@@ -45,7 +45,6 @@ import qualified Test.ViewSupportSpec
import qualified Test.ServerSideComponent.HtmlParserSpec
import qualified Test.ServerSideComponent.HtmlDiffSpec
import qualified Test.FileStorage.ControllerFunctionsSpec
-import qualified Test.FileStorage.MimeTypesSpec
import qualified Test.DataSync.DynamicQueryCompiler
import qualified Test.IDE.CodeGeneration.MigrationGenerator
import qualified Test.PGListenerSpec
@@ -80,7 +79,6 @@ main = hspec do
Test.ServerSideComponent.HtmlParserSpec.tests
Test.ServerSideComponent.HtmlDiffSpec.tests
Test.FileStorage.ControllerFunctionsSpec.tests
- Test.FileStorage.MimeTypesSpec.tests
Test.DataSync.DynamicQueryCompiler.tests
Test.IDE.SchemaDesigner.SchemaOperationsSpec.tests
Test.IDE.CodeGeneration.MigrationGenerator.tests
diff --git a/ihp.cabal b/ihp.cabal
index b27059ae8..dc1996be4 100644
--- a/ihp.cabal
+++ b/ihp.cabal
@@ -101,6 +101,7 @@ common shared-properties
, with-utf8
, ihp-hsx
, ihp-postgresql-simple-extra
+ , mime-types
default-extensions:
OverloadedStrings
, NoImplicitPrelude
@@ -261,7 +262,6 @@ library
, IHP.FileStorage.ControllerFunctions
, IHP.FileStorage.Config
, IHP.FileStorage.Preprocessor.ImageMagick
- , IHP.FileStorage.MimeTypes
, IHP.Pagination.ControllerFunctions
, IHP.Pagination.ViewFunctions
, IHP.Pagination.Types