@contentgrowth/content-auth 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -759,6 +759,12 @@ interface EmailNormalizationConfig {
759
759
  /** Column name in users table. Default: 'normalized_email' */
760
760
  columnName?: string;
761
761
  }
762
+ interface SignInTrackingConfig {
763
+ /** Enable sign-in tracking (lastSignedInAt column). Default: false */
764
+ enabled: boolean;
765
+ /** Column name for last sign-in timestamp. Default: 'lastSignedInAt' */
766
+ columnName?: string;
767
+ }
762
768
  /**
763
769
  * Field attribute for additional fields in custom schema
764
770
  */
@@ -861,12 +867,28 @@ interface AuthConfig {
861
867
  * Requires a 'normalized_email' column in the users table.
862
868
  */
863
869
  emailNormalization?: EmailNormalizationConfig;
870
+ /**
871
+ * Sign-in tracking to record when users last signed in.
872
+ * Useful for engagement analytics and identifying inactive users.
873
+ * Requires a 'lastSignedInAt' column (or custom name) in the users table.
874
+ */
875
+ signInTracking?: SignInTrackingConfig;
864
876
  /**
865
877
  * Custom schema mapping to use existing tables with Better Auth.
866
878
  * Maps Better Auth's default table/column names to your existing database schema.
867
879
  * If not provided, uses default table names (users, sessions, accounts, etc.).
868
880
  */
869
881
  schemaMapping?: SchemaMapping;
882
+ /**
883
+ * Callback triggered when a new user signs up.
884
+ * This includes email signups and OAuth signups (first time).
885
+ */
886
+ onSignup?: (user: any) => Promise<void> | void;
887
+ /**
888
+ * Callback triggered when a user signs in.
889
+ * This triggers on every successful session creation (including signup).
890
+ */
891
+ onSignin?: (user: any) => Promise<void> | void;
870
892
  [key: string]: any;
871
893
  }
872
894
  declare const createAuth: (config: AuthConfig) => better_auth.Auth<any>;
@@ -876,4 +898,4 @@ declare const createAuthApp: (config: AuthConfig) => {
876
898
  auth: better_auth.Auth<any>;
877
899
  };
878
900
 
879
- export { type AuthConfig, type EmailNormalizationConfig, type FieldAttribute, type SchemaMapping, type TableMapping, type TurnstileConfig, authMiddleware, createAuth, createAuthApp, getInvitationLink, getSessionToken, isGmailAddress, normalizeEmail, schema, verifyTurnstile };
901
+ export { type AuthConfig, type EmailNormalizationConfig, type FieldAttribute, type SchemaMapping, type SignInTrackingConfig, type TableMapping, type TurnstileConfig, authMiddleware, createAuth, createAuthApp, getInvitationLink, getSessionToken, isGmailAddress, normalizeEmail, schema, verifyTurnstile };
@@ -9,7 +9,7 @@ import {
9
9
  normalizeEmail,
10
10
  schema_exports,
11
11
  verifyTurnstile
12
- } from "../chunk-52ZT54FA.js";
12
+ } from "../chunk-VZSDOPSD.js";
13
13
  import "../chunk-R5U7XKVJ.js";
14
14
  export {
15
15
  Hono,
@@ -365,6 +365,9 @@ var createAuth = (config) => {
365
365
  emailVerification,
366
366
  turnstile: turnstileConfig,
367
367
  emailNormalization,
368
+ signInTracking,
369
+ onSignup,
370
+ onSignin,
368
371
  schemaMapping,
369
372
  user,
370
373
  session,
@@ -484,15 +487,66 @@ var createAuth = (config) => {
484
487
  after: async (context) => {
485
488
  const path = context.path || "";
486
489
  const user2 = context.user || context.response?.user || context.data?.user || context.context?.returned?.user || context.context?.newSession?.user;
487
- if (emailNormalization?.enabled && rawDb?.prepare) {
488
- if ((path.includes("/sign-up") || path.includes("/callback")) && user2?.id && user2?.email) {
490
+ if (user2) {
491
+ if (emailNormalization?.enabled && rawDb?.prepare) {
492
+ if ((path.includes("/sign-up") || path.includes("/callback")) && user2.id && user2.email) {
493
+ try {
494
+ const normalized = normalizeEmail(user2.email);
495
+ await rawDb.prepare(
496
+ `UPDATE ${userTableName} SET ${normalizedEmailColumn} = ? WHERE ${userIdColumn} = ? AND (${normalizedEmailColumn} IS NULL OR ${normalizedEmailColumn} != ?)`
497
+ ).bind(normalized, user2.id, normalized).run();
498
+ } catch (e) {
499
+ console.error(`[ContentAuth] Failed to set normalized_email: ${e.message}`);
500
+ }
501
+ }
502
+ }
503
+ if (onSignup) {
504
+ let isNewUser = false;
505
+ if (path.includes("/sign-up/email")) {
506
+ isNewUser = true;
507
+ } else if (path.includes("/callback")) {
508
+ if (user2.createdAt) {
509
+ const createdAt = new Date(user2.createdAt).getTime();
510
+ const now = Date.now();
511
+ if (now - createdAt < 1e4) {
512
+ isNewUser = true;
513
+ }
514
+ }
515
+ }
516
+ if (isNewUser) {
517
+ try {
518
+ const result = onSignup(user2);
519
+ if (result instanceof Promise) {
520
+ result.catch((e) => console.error(`[ContentAuth] onSignup hook failed: ${e.message}`));
521
+ }
522
+ } catch (e) {
523
+ console.error(`[ContentAuth] onSignup hook failed: ${e.message}`);
524
+ }
525
+ }
526
+ }
527
+ if (onSignin) {
528
+ if (path.includes("/sign-in") || path.includes("/sign-up") || path.includes("/callback")) {
529
+ try {
530
+ const result = onSignin(user2);
531
+ if (result instanceof Promise) {
532
+ result.catch((e) => console.error(`[ContentAuth] onSignin hook failed: ${e.message}`));
533
+ }
534
+ } catch (e) {
535
+ console.error(`[ContentAuth] onSignin hook failed: ${e.message}`);
536
+ }
537
+ }
538
+ }
539
+ }
540
+ if (signInTracking?.enabled && rawDb?.prepare && user2?.id) {
541
+ if (path.includes("/sign-in") || path.includes("/sign-up") || path.includes("/callback")) {
489
542
  try {
490
- const normalized = normalizeEmail(user2.email);
543
+ const lastSignedInColumn = signInTracking.columnName || "lastSignedInAt";
544
+ const now = Math.floor(Date.now() / 1e3);
491
545
  await rawDb.prepare(
492
- `UPDATE ${userTableName} SET ${normalizedEmailColumn} = ? WHERE ${userIdColumn} = ? AND (${normalizedEmailColumn} IS NULL OR ${normalizedEmailColumn} != ?)`
493
- ).bind(normalized, user2.id, normalized).run();
546
+ `UPDATE ${userTableName} SET ${lastSignedInColumn} = ? WHERE ${userIdColumn} = ?`
547
+ ).bind(now, user2.id).run();
494
548
  } catch (e) {
495
- console.error(`[ContentAuth] Failed to set normalized_email: ${e.message}`);
549
+ console.error(`[ContentAuth] Failed to update lastSignedInAt: ${e.message}`);
496
550
  }
497
551
  }
498
552
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { AuthConfig, EmailNormalizationConfig, FieldAttribute, SchemaMapping, TableMapping, TurnstileConfig, authMiddleware, createAuth, createAuthApp, getInvitationLink, getSessionToken, isGmailAddress, normalizeEmail, schema, verifyTurnstile } from './backend/index.js';
1
+ export { AuthConfig, EmailNormalizationConfig, FieldAttribute, SchemaMapping, SignInTrackingConfig, TableMapping, TurnstileConfig, authMiddleware, createAuth, createAuthApp, getInvitationLink, getSessionToken, isGmailAddress, normalizeEmail, schema, verifyTurnstile } from './backend/index.js';
2
2
  export { AuthForm, CreateOrganizationForm, ForgotPasswordForm, InviteMemberForm, OrganizationSwitcher, PasswordChanger, PasswordChangerProps, ProfileEditor, ProfileEditorProps, ResetPasswordForm } from './frontend/index.js';
3
3
  export { authClient, createClient } from './frontend/client.js';
4
4
  export * from 'better-auth';
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  normalizeEmail,
10
10
  schema_exports,
11
11
  verifyTurnstile
12
- } from "./chunk-52ZT54FA.js";
12
+ } from "./chunk-VZSDOPSD.js";
13
13
  import {
14
14
  AuthForm,
15
15
  CreateOrganizationForm,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentgrowth/content-auth",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Better Auth wrapper with UI components for Cloudflare Workers & Pages. Includes custom schema mapping, Turnstile bot protection, and email normalization.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/schema/auth.sql CHANGED
@@ -29,6 +29,8 @@ CREATE TABLE IF NOT EXISTS users (
29
29
  updatedAt TIMESTAMP NOT NULL
30
30
  -- Optional: For Gmail duplicate prevention (see docs)
31
31
  -- normalized_email TEXT
32
+ -- Optional: Sign-in tracking for engagement analytics
33
+ -- lastSignedInAt TIMESTAMP -- Updated on each sign-in
32
34
  );
33
35
 
34
36
  -- Sessions