summaryrefslogtreecommitdiff
path: root/Omni/Task/Core.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Task/Core.hs')
-rw-r--r--Omni/Task/Core.hs67
1 files changed, 41 insertions, 26 deletions
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs
index af982d8..5b1551c 100644
--- a/Omni/Task/Core.hs
+++ b/Omni/Task/Core.hs
@@ -12,8 +12,7 @@ import qualified Data.ByteString.Lazy.Char8 as BLC
import qualified Data.List as List
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
-import Data.Time (UTCTime, diffTimeToPicoseconds, getCurrentTime, utctDay, utctDayTime)
-import Data.Time.Calendar (toModifiedJulianDay)
+import Data.Time (UTCTime, getCurrentTime)
import qualified Database.SQLite.Simple as SQL
import qualified Database.SQLite.Simple.FromField as SQL
import qualified Database.SQLite.Simple.Ok as SQLOk
@@ -22,6 +21,7 @@ import GHC.Generics ()
import System.Directory (createDirectoryIfMissing, doesFileExist)
import System.Environment (lookupEnv)
import System.IO.Unsafe (unsafePerformIO)
+import qualified Web.Sqids as Sqids
-- Core data types
data Task = Task
@@ -242,18 +242,48 @@ initTaskDb = do
\ created_at TIMESTAMP NOT NULL, \
\ updated_at TIMESTAMP NOT NULL \
\)"
+ SQL.execute_
+ conn
+ "CREATE TABLE IF NOT EXISTS id_counter (\
+ \ id INTEGER PRIMARY KEY CHECK (id = 1), \
+ \ counter INTEGER NOT NULL DEFAULT 0 \
+ \)"
+ SQL.execute_
+ conn
+ "INSERT OR IGNORE INTO id_counter (id, counter) VALUES (1, 0)"
+
+-- Sqids configuration: lowercase alphabet only, minimum length 8
+sqidsOptions :: Sqids.SqidsOptions
+sqidsOptions =
+ Sqids.defaultSqidsOptions
+ { Sqids.alphabet = "abcdefghijklmnopqrstuvwxyz0123456789",
+ Sqids.minLength = 8,
+ Sqids.blocklist = []
+ }
--- Generate a short ID using base36 encoding of timestamp (lowercase)
+-- Generate a short ID using sqids with sequential counter
generateId :: IO Text
generateId = do
- now <- getCurrentTime
- let day = utctDay now
- dayTime = utctDayTime now
- mjd = toModifiedJulianDay day
- micros = diffTimeToPicoseconds dayTime `div` 1000000
- totalMicros = (mjd * 100000000000) + micros
- encoded = toBase36 totalMicros
- pure <| "t-" <> T.pack encoded
+ counter <- getNextCounter
+ let encoded = case Sqids.runSqids sqidsOptions (Sqids.encode [counter]) of
+ Left _ -> "00000000"
+ Right sqid -> sqid
+ pure <| "t-" <> encoded
+
+-- Get the next counter value (atomically increments)
+getNextCounter :: IO Int
+getNextCounter =
+ withDb <| \conn -> do
+ SQL.execute_
+ conn
+ "CREATE TABLE IF NOT EXISTS id_counter (\
+ \ id INTEGER PRIMARY KEY CHECK (id = 1), \
+ \ counter INTEGER NOT NULL DEFAULT 0 \
+ \)"
+ SQL.execute_ conn "INSERT OR IGNORE INTO id_counter (id, counter) VALUES (1, 0)"
+ SQL.execute_ conn "UPDATE id_counter SET counter = counter + 1 WHERE id = 1"
+ [SQL.Only c] <- SQL.query_ conn "SELECT counter FROM id_counter WHERE id = 1" :: IO [SQL.Only Int]
+ pure c
-- Generate a child ID based on parent ID
generateChildId :: Text -> IO Text
@@ -279,21 +309,6 @@ getSuffix parent childId =
else Nothing
else Nothing
--- Convert number to base36 (0-9, a-z)
-toBase36 :: Integer -> String
-toBase36 0 = "0"
-toBase36 n = reverse <| go n
- where
- alphabet = ['0' .. '9'] ++ ['a' .. 'z']
- go 0 = []
- go x =
- let (q, r) = x `divMod` 36
- idx = fromIntegral r
- char = case drop idx alphabet of
- (c : _) -> c
- [] -> '0'
- in char : go q
-
-- Load all tasks from DB
loadTasks :: IO [Task]
loadTasks =