txt: ## dist_zef-demayl-Cro-HTTP-Session-SQLite.md
  # cro-http-session-sqlite
  
  Cro::HTTP::Session::SQLite
  
  # NAME
  
  Cro::HTTP::Session::SQLite - An implementation of Cro persistent sessions using SQLite.
  
  # SYNOPSIS
  
  ```
  use Cro::HTTP::Session::SQLite;
  
  ```
  
  # DESCRIPTION
  
  There are dozens of ways we might do session storage; this module handles the case where:
  
  * The database is being accessed using DBIish.
  * You're fine with the session state being serialized and stored as a JSON in the database.
  
  If these don't meet your needs, it's best to steal the code from this module into your own application and edit it as needed.
  
  # INSTALLATION
  
  ```
  zef install Cro::HTTP::Session::SQLite
  
  ```
  
  # FUNCTIONALITY
  
  * Store the session data in local SQLite3 file
  * Auto create the db file and table if missing
  * Using your own SQLite DBIish connection if required
  * Encrypted data store using OpenSSL::CryptTools AES256
  * Session auto cleanup or on every request
  * Configurable table name and columns
  * Store the user ID and IP address from the session
  * Skip creating a session
  * JSON state storage in a json type column
  * Base64 state storage when encrypted
  * IP cookie binding
  
  # Database default setup
  
  * The table name can be changed
  * Every column is configurable
  * The table will be created if missing
  
  ```
  CREATE TABLE IF NOT EXISTS sessions (
  	id varchar(255), 
  	state json,
  	ip_addr varchar(255),
  	user_id INTEGER,
  	expiration TIMESTAMP,
  	created TIMESTAMP DEFAULT CURRENT_TIMESTAMP 
  );
  CREATE INDEX session_id ON sessions (id);
  CREATE INDEX expired_date ON sessions (expiration);
  
  ```
  
  # Minimal Cro application setup
  
  ```
  class MySession {
      has $.user-id;
  	has $.ip-addr;
  
      method set-logged-in-user($!user-id --> Nil) { }
      method is-logged-in(--> Bool) { $!user-id.defined }
  }
  
  my $application = route {
  
      before Cro::HTTP::Session::SQLite[UserSession].new();
  
      get -> UserSession $user {
          $user.user-id = 123;
  		$user.ip-addr = '127.0.0.1';
          content 'text/plain', "Hello, TEST!";
      }
  }
  
  my Cro::Service $hello = Cro::HTTP::Server.new:
      :host<localhost>, :port(80), :$application;
  
  ```
  
  Here are the keypoints here:
  
  * Creates a ./sessions.db file
  * Cookie expiration time is 30 min
  * Auto clean the sessions table every 30 min
  * Prevent session loading if the user IP address doesn't match the stored one
  
  # Custom setup
  
  ```
  before Cro::HTTP::Session::SQLite[UserSession].new( 
  	expiration => Duration.new(60 * 60), # 60 minutes expiration
  	cookie-name => 'CustomCookieName',
  	db-path => '/home/user/app/sessions.db',
  	restrict-ip-addr => True,
  	autoclean-every-seconds => 60, # Cleanup the sessions table every 60 seconds
  	key => ("0" x 32).encode, - Encryption key
  	iv => ("0" x 16).encode, - Encryption IV
  	skip-cookie => -> Cro::HTTP::Response $response { # Skip the cookie if the request is application/json
  		my $content-type = $response.request.header('content-type');
  		$content-type && $content-type eq 'application/json'
  		}
  	);
  
  ```
  
  Here we have a custom setup:
  
  * Expiration time is **60 minutes**
  * Cookie name is **CustomCookieName**
  * SQLite DB file is **/home/user/app/sessions.db**
  * Cookie is restricted only to the IP address previously stored
  * Expired sessions are deleted from the DB every 60 seconds
  * The data is encrypted using a default KEY and IV and you **must CHANGE IT**
  * Cookie is ignored if the request content type is application/json
  
  For full setup see the constructor below
  
  # Constructor
  
  All fields are optional
  
  > DBDish::SQLite::Connection $.db
  
  DBIish connection. **Default** auto connects
  
  > IO::Path $.db-path
  
  Path to the DB SQLite3 file. **Default** `./sessions.db`
  
  > Str $.cookie-name
  
  Name of the cookie. **Default** CroCookie
  
  > Duration $.expiration
  
  Expiration duration for the cookie. **Default** `Duration.new( 30*60 )`
  
  > Int $.autoclean-every-seconds
  
  Session cleanup every X seconds. **Default** 1800
  
  * -1 Disable session removal
  * 0 Delete the expired sessions on every request
  * 1+ Delete the expired sessions every X seconds
  
  > Str $.sessions-table
  
  Name of the DB table. **Default** sessions
  
  > Str $.id-column
  
  Name of the ID column in the DB. **Default** id
  
  > Str $.state-column
  
  Name of the state column in the DB. **Default** state
  
  > Str $.expiration-column
  
  Name Of the expiration column. **Default** expiration
  
  > Str $.ip-addr-column
  
  Name of the IP address column. **Default** ip\_addr
  
  > Str $.user-id-column
  
  Name of the User ID column. **Default** user\_id
  
  > Str $.created-column
  
  Name of the Created column. **Default** created
  
  > Str $.ip-addr-field
  
  Field name from your Session object, that is used to store the IP address in the table column $.ip-addr-column. **Default** ip-addr
  
  * Nil to disable storing the IP address
  
  > Str $.user-id-field
  
  Field name from your Session object, that is used to store the User ID in the table column $.user-id-column. **Default** user-id
  
  * Nil to disable storing the User ID
  
  > Bool $.restrict-ip-addr
  
  IP cookie bind. Looksup the $.ip-addr-field from your Session object. **Default** *True*
  
  > Callable &.skip-cookie
  
  Custom function that can disable the session. **Default** *Nil*
  
  > Buf() $.key
  
  Encryption key. See [OpenSSL](https://raku.land/github:sergot/OpenSSL#opensslcrypttools) for more information
  
  > Buf() $.iv
  
  IV. See [OpenSSL](https://raku.land/github:sergot/OpenSSL#opensslcrypttools) for more information
  
  # Controlling serialized data
  
  Instead of using the Cro::HTTP::Session::SQLite role directly, create a class that composes it.
  
  ```
  class MySessionStore does Cro::HTTP::Session::SQLite[MySession] {
      method serialize(MySession $s --> Hash) {
          # Replace this with your serialization logic.
          $s.Capture.hash
      }
      
      method deserialize(Str $d --> MySession) {
          # Replace this with your deserialization logic.
          Session.new(|from-json($d))
      }
  }
  
  ```
  
  # AUTHOR
  
  Denis Kanchev
  
  # COPYRIGHT AND LICENSE
  
  Copyright 2025 Denis Kanchev
  
  This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.
  