@remit/auth-service 0.0.10 → 0.0.12

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@remit/auth-service",
3
- "version": "0.0.10",
4
- "description": "better-auth identity service for the Postgres-parity stack (pg-parity)",
3
+ "version": "0.0.12",
4
+ "description": "better-auth identity service for the self-host stack",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "types": "src/index.ts",
@@ -23,14 +23,9 @@
23
23
  "test:typecheck": "tsgo --noEmit -p tsconfig.json",
24
24
  "test:run": "node --import tsx --experimental-test-coverage --test-coverage-include='src/**' --test-coverage-exclude='src/**/*.test.ts' --test-coverage-exclude='src/**/*.test.tsx' --test-coverage-lines=94 --test 'src/**/*.test.ts'",
25
25
  "test": "npm run test:typecheck && npm run test:run",
26
- "auth:schema:generate": "npx @better-auth/cli generate --config src/auth.gen.ts --output src/schema/auth-schema.ts -y",
27
- "auth:schema:push": "npx drizzle-kit push",
28
26
  "fix": "biome check --write src"
29
27
  },
30
28
  "devDependencies": {
31
- "@better-auth/cli": "^1.4.21",
32
- "drizzle-kit": "^0.31.1",
33
- "embedded-postgres": "^18.4.0-beta.17",
34
29
  "tsx": "*"
35
30
  },
36
31
  "dependencies": {
@@ -38,7 +33,6 @@
38
33
  "better-sqlite3": "^12.11.1",
39
34
  "drizzle-orm": "^0.45.2",
40
35
  "jose": "^6.2.3",
41
- "pg": "^8.0.0",
42
36
  "@types/better-sqlite3": "^7.6.13"
43
37
  },
44
38
  "license": "MIT",
package/src/auth.ts CHANGED
@@ -2,19 +2,14 @@ import type { Auth as BetterAuthInstance } from "better-auth";
2
2
  import { betterAuth } from "better-auth";
3
3
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
4
4
  import { jwt } from "better-auth/plugins";
5
- import { drizzle as drizzlePg } from "drizzle-orm/node-postgres";
6
- import pg from "pg";
7
- import * as authSchema from "./schema/auth-schema.js";
8
5
  import * as authSchemaSqlite from "./schema/auth-schema-sqlite.js";
9
6
 
10
7
  export interface AuthConfig {
11
8
  /**
12
- * The relational backend better-auth runs on, off `DATA_BACKEND` (RFC 036).
13
- * `pg` (the default) reads `connectionString` as a Postgres URL; `sqlite`
14
- * reads it as the path to the shared database file (D3), and drives the
15
- * SQLite identity schema through the drizzle adapter's `sqlite` provider.
9
+ * Path to the shared database file (RFC 036 D3). The identity tables live
10
+ * beside the app tables and are driven through the drizzle adapter's
11
+ * `sqlite` provider.
16
12
  */
17
- provider?: "pg" | "sqlite";
18
13
  connectionString: string;
19
14
  secret: string;
20
15
  baseURL: string;
@@ -27,34 +22,36 @@ export interface AuthConfig {
27
22
  selfSignUpEnabled: boolean;
28
23
  }
29
24
 
30
- // The drizzle adapter better-auth runs against, chosen by backend. better-sqlite3
31
- // and its drizzle driver are imported dynamically so the Postgres path never
32
- // loads the native binding, and the module stays out of any bundle that never
33
- // runs on SQLite. On SQLite the auth connection sets the same WAL/busy_timeout
25
+ // The drizzle adapter better-auth runs against. better-sqlite3 and its drizzle
26
+ // driver are imported dynamically so the module stays out of any bundle that
27
+ // never opens the database. The auth connection sets the same WAL/busy_timeout
34
28
  // coordination every writer uses (RFC 036 D3) — it shares the file with the app
35
29
  // tables through its own connection.
36
30
  const buildAuthAdapter = async (config: AuthConfig) => {
37
- if (config.provider === "sqlite") {
38
- const { default: Database } = await import("better-sqlite3");
39
- const { drizzle } = await import("drizzle-orm/better-sqlite3");
40
- const sqlite = new Database(config.connectionString);
41
- sqlite.pragma("journal_mode = WAL");
42
- sqlite.pragma("busy_timeout = 5000");
43
- sqlite.pragma("synchronous = NORMAL");
44
- sqlite.pragma("foreign_keys = ON");
45
- return drizzleAdapter(drizzle(sqlite, { schema: authSchemaSqlite }), {
46
- provider: "sqlite",
47
- schema: authSchemaSqlite,
48
- });
49
- }
50
-
51
- const pool = new pg.Pool({ connectionString: config.connectionString });
52
- return drizzleAdapter(drizzlePg(pool, { schema: authSchema }), {
53
- provider: "pg",
54
- schema: authSchema,
31
+ const { default: Database } = await import("better-sqlite3");
32
+ const { drizzle } = await import("drizzle-orm/better-sqlite3");
33
+ const sqlite = new Database(config.connectionString);
34
+ sqlite.pragma("journal_mode = WAL");
35
+ sqlite.pragma("busy_timeout = 5000");
36
+ sqlite.pragma("synchronous = NORMAL");
37
+ sqlite.pragma("foreign_keys = ON");
38
+ return drizzleAdapter(drizzle(sqlite, { schema: authSchemaSqlite }), {
39
+ provider: "sqlite",
40
+ schema: authSchemaSqlite,
55
41
  });
56
42
  };
57
43
 
44
+ /**
45
+ * The header carrying the client address, set by the edge proxy from the
46
+ * connection it terminates (deploy/vps/caddy/routes.caddy). It is deliberately
47
+ * not `X-Forwarded-For`: that header arrives with an appended hop per proxy,
48
+ * which resolves to no single client and collapses rate limiting onto one
49
+ * shared bucket for everybody. Nothing else in the chain writes this name, and
50
+ * the edge replaces any value a client sends, so it is single-valued and
51
+ * trustworthy at the point better-auth reads it.
52
+ */
53
+ export const CLIENT_IP_HEADER = "x-remit-client-ip";
54
+
58
55
  const intEnv = (name: string, fallback: number): number => {
59
56
  const raw = process.env[name];
60
57
  if (!raw) return fallback;
@@ -63,7 +60,7 @@ const intEnv = (name: string, fallback: number): number => {
63
60
  };
64
61
 
65
62
  /**
66
- * Build a better-auth instance bound to the pg-parity Postgres.
63
+ * Build a better-auth instance bound to the self-host database file.
67
64
  *
68
65
  * RS256 is deliberate: the edge tier (APISIX, or the dev-server verifier)
69
66
  * follows JWKS key rotation offline, so signing must be asymmetric. The JWKS is
@@ -91,6 +88,12 @@ export const createAuth = async (
91
88
  autoSignIn: true,
92
89
  disableSignUp: !config.selfSignUpEnabled,
93
90
  },
91
+ // Listing one header, and not better-auth's `x-forwarded-for` default,
92
+ // is what keeps the resolved address out of a caller's reach: an
93
+ // `X-Forwarded-For` a client sends is never read at all.
94
+ advanced: {
95
+ ipAddress: { ipAddressHeaders: [CLIENT_IP_HEADER] },
96
+ },
94
97
  rateLimit: {
95
98
  enabled: true,
96
99
  window: intEnv("BETTER_AUTH_RATE_LIMIT_WINDOW", 60),
package/src/config.ts CHANGED
@@ -29,29 +29,22 @@ export const resolveSelfSignUpEnabled = (
29
29
  };
30
30
 
31
31
  export interface DataConnectionConfig {
32
- provider: "pg" | "sqlite";
33
32
  connectionString: string;
34
33
  }
35
34
 
36
35
  /**
37
- * Resolve which relational backend identity data lives on, from the same env
38
- * vars the rest of the self-host stack reads (RFC 036 D5). On
39
- * `DATA_BACKEND=sqlite` the identity tables share the app database file, so
40
- * the locator is `SQLITE_DB_PATH`; otherwise it is the Postgres URL.
36
+ * Resolve where identity data lives, from the same env vars the rest of the
37
+ * self-host stack reads (RFC 036 D5). The identity tables share the app
38
+ * database file, so the locator is `SQLITE_DB_PATH`.
41
39
  */
42
- export const resolveDataConnectionConfig = (): DataConnectionConfig => {
43
- const provider = process.env.DATA_BACKEND === "sqlite" ? "sqlite" : "pg";
44
- const connectionString =
45
- provider === "sqlite"
46
- ? required("SQLITE_DB_PATH", process.env.SQLITE_DB_PATH)
47
- : required("PG_CONNECTION_URL", process.env.PG_CONNECTION_URL);
48
- return { provider, connectionString };
49
- };
40
+ export const resolveDataConnectionConfig = (): DataConnectionConfig => ({
41
+ connectionString: required("SQLITE_DB_PATH", process.env.SQLITE_DB_PATH),
42
+ });
50
43
 
51
44
  /**
52
45
  * Resolve the better-auth config from the environment. Callers reach this only
53
- * on the self-host relational backends (Postgres or SQLite); the Cognito/AWS
54
- * path never touches better-auth.
46
+ * on the self-host relational backend; the Cognito/AWS path never touches
47
+ * better-auth.
55
48
  */
56
49
  export const resolveAuthConfig = (): AuthConfig => {
57
50
  const baseURL = required("BETTER_AUTH_URL", process.env.BETTER_AUTH_URL);
package/src/index.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export { toNodeHandler } from "better-auth/node";
2
- export { type Auth, type AuthConfig, createAuth } from "./auth.js";
2
+ export {
3
+ type Auth,
4
+ type AuthConfig,
5
+ CLIENT_IP_HEADER,
6
+ createAuth,
7
+ } from "./auth.js";
3
8
  export {
4
9
  AUTH_BASE_PATH,
5
10
  AUTH_JWKS_PATH,
@@ -0,0 +1,106 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { after, before, describe, it } from "node:test";
6
+ import type { Auth } from "./auth.js";
7
+ import { CLIENT_IP_HEADER, createAuth } from "./auth.js";
8
+
9
+ const SIGN_IN_MAX = 2;
10
+ const MIGRATIONS = resolve(
11
+ import.meta.dirname,
12
+ "../../../deploy/vps/migrations-sqlite/auth",
13
+ );
14
+
15
+ const createDatabase = async (dir: string, name: string): Promise<string> => {
16
+ const path = join(dir, `${name}.db`);
17
+ const { default: Database } = await import("better-sqlite3");
18
+ const { drizzle } = await import("drizzle-orm/better-sqlite3");
19
+ const { migrate } = await import("drizzle-orm/better-sqlite3/migrator");
20
+ const sqlite = new Database(path);
21
+ migrate(drizzle(sqlite), { migrationsFolder: MIGRATIONS });
22
+ sqlite.close();
23
+ return path;
24
+ };
25
+
26
+ const signIn = (auth: Auth, headers: Record<string, string>) =>
27
+ auth.handler(
28
+ new Request("http://localhost:3000/api/auth/sign-in/email", {
29
+ method: "POST",
30
+ headers: { "content-type": "application/json", ...headers },
31
+ body: JSON.stringify({
32
+ email: "nobody@example.com",
33
+ password: "a-sufficiently-long-password",
34
+ }),
35
+ }),
36
+ );
37
+
38
+ /**
39
+ * Spend one client's sign-in budget, returning the attempt number that was
40
+ * turned away. `null` means the budget never ran out.
41
+ */
42
+ const exhaust = async (
43
+ auth: Auth,
44
+ headers: Record<string, string>,
45
+ ): Promise<number | null> => {
46
+ for (let attempt = 0; attempt <= SIGN_IN_MAX; attempt++) {
47
+ const response = await signIn(auth, headers);
48
+ if (response.status === 429) return attempt;
49
+ }
50
+ return null;
51
+ };
52
+
53
+ describe("sign-in rate limiting is per client", () => {
54
+ const previousMax = process.env.BETTER_AUTH_RATE_LIMIT_SIGN_IN_MAX;
55
+ let dir: string;
56
+
57
+ before(() => {
58
+ process.env.BETTER_AUTH_RATE_LIMIT_SIGN_IN_MAX = String(SIGN_IN_MAX);
59
+ dir = mkdtempSync(join(tmpdir(), "remit-rate-limit-"));
60
+ });
61
+
62
+ after(() => {
63
+ if (previousMax === undefined) {
64
+ delete process.env.BETTER_AUTH_RATE_LIMIT_SIGN_IN_MAX;
65
+ } else {
66
+ process.env.BETTER_AUTH_RATE_LIMIT_SIGN_IN_MAX = previousMax;
67
+ }
68
+ rmSync(dir, { recursive: true, force: true });
69
+ });
70
+
71
+ const build = async (name: string): Promise<Auth> =>
72
+ createAuth({
73
+ connectionString: await createDatabase(dir, name),
74
+ secret: "rate-limit-test-secret-value-32chars-min",
75
+ baseURL: "http://localhost:3000",
76
+ selfSignUpEnabled: true,
77
+ });
78
+
79
+ it("locks out only the client that spent the budget", async () => {
80
+ const auth = await build("per-client");
81
+
82
+ assert.equal(
83
+ await exhaust(auth, { [CLIENT_IP_HEADER]: "203.0.113.10" }),
84
+ SIGN_IN_MAX,
85
+ );
86
+
87
+ const other = await signIn(auth, { [CLIENT_IP_HEADER]: "198.51.100.20" });
88
+ assert.equal(other.status, 401);
89
+ });
90
+
91
+ it("ignores a forwarded address the client supplies itself", async () => {
92
+ const auth = await build("spoofed");
93
+ const client = { [CLIENT_IP_HEADER]: "203.0.113.30" };
94
+
95
+ assert.equal(
96
+ await exhaust(auth, { ...client, "x-forwarded-for": "203.0.113.31" }),
97
+ SIGN_IN_MAX,
98
+ );
99
+
100
+ const spoofed = await signIn(auth, {
101
+ ...client,
102
+ "x-forwarded-for": "198.51.100.40",
103
+ });
104
+ assert.equal(spoofed.status, 429);
105
+ });
106
+ });
@@ -1,9 +1,15 @@
1
- // SQLite twin of auth-schema.ts (RFC 036 D5). Same five better-auth identity
2
- // tables in the SQLite dialect — the shape better-auth's drizzle adapter emits
3
- // with `provider: "sqlite"`: `sqliteTable`, `text` unchanged, `boolean` and
4
- // `timestamp` mapped to `integer` (mode boolean / timestamp). Used only to
5
- // generate the committed sqlite migrations (deploy/vps/migrations-sqlite/auth);
6
- // the Postgres schema stays the source for the pg migrations.
1
+ // The five better-auth identity tables, in the shape better-auth's drizzle
2
+ // adapter emits with `provider: "sqlite"`. Used to generate the committed
3
+ // sqlite migrations (deploy/vps/migrations-sqlite/auth).
4
+ //
5
+ // Hand-maintained, deliberately. The `@better-auth/cli generate` command no
6
+ // longer emits this shape: it writes `mode: "timestamp_ms"` where these columns
7
+ // store seconds, and a SQL `DEFAULT (cast(unixepoch('subsecond') * 1000 as
8
+ // integer))` where these use a JS-side `$defaultFn` and the committed migration
9
+ // has no DEFAULT clause. Taking its output would reinterpret every timestamp on
10
+ // every deployed instance and rewrite all five tables. Match better-auth's
11
+ // runtime expectations by hand when a version bump changes them, and let
12
+ // vps-migrations-drift.sqlite.test.ts catch the migration side.
7
13
  import { relations } from "drizzle-orm";
8
14
  import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
9
15
 
@@ -1,7 +1,5 @@
1
- // SQLite twin of meta-schema.ts. Same shape in the SQLite dialect — used only
2
- // to generate the committed SQLite migrations
3
- // (deploy/vps/migrations-sqlite/meta); the Postgres schema stays the source
4
- // for the pg migrations.
1
+ // The instance-owner singleton, used to generate the committed SQLite
2
+ // migrations (deploy/vps/migrations-sqlite/meta).
5
3
  import { sql } from "drizzle-orm";
6
4
  import { check, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
7
5
 
@@ -29,7 +29,7 @@ describe("resolveSelfSignUpEnabled", () => {
29
29
  });
30
30
 
31
31
  const baseConfig = {
32
- connectionString: "postgresql://remit:remit@localhost:5432/remit_test",
32
+ connectionString: ":memory:",
33
33
  secret: "signup-test-secret-value-32chars-minimum",
34
34
  baseURL: "http://localhost:3000",
35
35
  };
@@ -45,9 +45,8 @@ describe("createAuth self-signup gate", () => {
45
45
  assert.equal(closed.options.emailAndPassword?.disableSignUp, true);
46
46
  });
47
47
 
48
- it("selects the sqlite drizzle provider when configured", async () => {
48
+ it("drives the sqlite drizzle provider", async () => {
49
49
  const auth = await createAuth({
50
- provider: "sqlite",
51
50
  connectionString: ":memory:",
52
51
  secret: baseConfig.secret,
53
52
  baseURL: baseConfig.baseURL,
package/src/verifier.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  // JWT-verification-only slice of this package's barrel. The full barrel
2
- // (`index.ts`) re-exports `createAuth` from `./auth.js`, which constructs a
3
- // `pg.Pool` + `drizzle-orm/node-postgres` at module scope, and `toNodeHandler`
4
- // from `better-auth/node` — neither needed to verify a token, both dragging a
5
- // Postgres client into any Lambda bundle that only checks a bearer token (see
6
- // #1242's FAQ, #1244/#1247). `config.ts`'s `AuthConfig` import from `./auth.js`
7
- // is type-only and erases at build, so this stays free of that chain.
2
+ // (`index.ts`) re-exports `createAuth` from `./auth.js` and `toNodeHandler`
3
+ // from `better-auth/node` — neither needed to verify a token, both dragging the
4
+ // whole better-auth server graph into any Lambda bundle that only checks a
5
+ // bearer token (see #1242's FAQ, #1244/#1247). `config.ts`'s `AuthConfig`
6
+ // import from `./auth.js` is type-only and erases at build, so this stays free
7
+ // of that chain.
8
8
 
9
9
  export {
10
10
  AUTH_BASE_PATH,
package/drizzle.config.ts DELETED
@@ -1,24 +0,0 @@
1
- const url =
2
- process.env.DATABASE_URL ??
3
- process.env.PG_CONNECTION_URL ??
4
- "postgresql://remit:remit@localhost:5432/remit_dev";
5
-
6
- /**
7
- * The better-auth identity tables share the pg-parity database with the entity
8
- * schema (remit-drizzle-service). `tablesFilter` scopes drizzle-kit to only the
9
- * auth tables so a push here never proposes dropping the entity tables it does
10
- * not know about.
11
- */
12
- export default {
13
- dialect: "postgresql",
14
- schema: "./src/schema/auth-schema.ts",
15
- out: "./.drizzle",
16
- dbCredentials: { url },
17
- tablesFilter: [
18
- "auth_user",
19
- "auth_session",
20
- "auth_account",
21
- "auth_verification",
22
- "auth_jwks",
23
- ],
24
- };
package/src/auth.gen.ts DELETED
@@ -1,35 +0,0 @@
1
- import type { Auth as BetterAuthInstance } from "better-auth";
2
- import { betterAuth } from "better-auth";
3
- import { drizzleAdapter } from "better-auth/adapters/drizzle";
4
- import { jwt } from "better-auth/plugins";
5
- import { drizzle } from "drizzle-orm/node-postgres";
6
- import pg from "pg";
7
-
8
- /**
9
- * Schema-generation-only config for `@better-auth/cli generate`. It mirrors the
10
- * plugin set of the real `createAuth` so the emitted Drizzle schema matches what
11
- * runtime expects, but omits the schema import to break the generate-time
12
- * chicken-and-egg (the schema file is this command's output).
13
- */
14
- const pool = new pg.Pool({
15
- connectionString:
16
- process.env.PG_CONNECTION_URL ??
17
- "postgresql://remit:remit@localhost:5432/remit_dev",
18
- });
19
-
20
- export const auth = betterAuth({
21
- secret: "schema-generation-only",
22
- baseURL: "http://localhost",
23
- database: drizzleAdapter(drizzle(pool), { provider: "pg" }),
24
- emailAndPassword: { enabled: true },
25
- user: { modelName: "auth_user" },
26
- session: { modelName: "auth_session" },
27
- account: { modelName: "auth_account" },
28
- verification: { modelName: "auth_verification" },
29
- plugins: [
30
- jwt({
31
- schema: { jwks: { modelName: "auth_jwks" } },
32
- jwks: { keyPairConfig: { alg: "RS256", modulusLength: 2048 } },
33
- }),
34
- ],
35
- }) as unknown as BetterAuthInstance;
@@ -1,101 +0,0 @@
1
- import { relations } from "drizzle-orm";
2
- import { boolean, index, pgTable, text, timestamp } from "drizzle-orm/pg-core";
3
-
4
- export const auth_user = pgTable("auth_user", {
5
- id: text("id").primaryKey(),
6
- name: text("name").notNull(),
7
- email: text("email").notNull().unique(),
8
- emailVerified: boolean("email_verified").default(false).notNull(),
9
- image: text("image"),
10
- createdAt: timestamp("created_at").defaultNow().notNull(),
11
- updatedAt: timestamp("updated_at")
12
- .defaultNow()
13
- .$onUpdate(() => /* @__PURE__ */ new Date())
14
- .notNull(),
15
- });
16
-
17
- export const auth_session = pgTable(
18
- "auth_session",
19
- {
20
- id: text("id").primaryKey(),
21
- expiresAt: timestamp("expires_at").notNull(),
22
- token: text("token").notNull().unique(),
23
- createdAt: timestamp("created_at").defaultNow().notNull(),
24
- updatedAt: timestamp("updated_at")
25
- .$onUpdate(() => /* @__PURE__ */ new Date())
26
- .notNull(),
27
- ipAddress: text("ip_address"),
28
- userAgent: text("user_agent"),
29
- userId: text("user_id")
30
- .notNull()
31
- .references(() => auth_user.id, { onDelete: "cascade" }),
32
- },
33
- (table) => [index("auth_session_userId_idx").on(table.userId)],
34
- );
35
-
36
- export const auth_account = pgTable(
37
- "auth_account",
38
- {
39
- id: text("id").primaryKey(),
40
- accountId: text("account_id").notNull(),
41
- providerId: text("provider_id").notNull(),
42
- userId: text("user_id")
43
- .notNull()
44
- .references(() => auth_user.id, { onDelete: "cascade" }),
45
- accessToken: text("access_token"),
46
- refreshToken: text("refresh_token"),
47
- idToken: text("id_token"),
48
- accessTokenExpiresAt: timestamp("access_token_expires_at"),
49
- refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
50
- scope: text("scope"),
51
- password: text("password"),
52
- createdAt: timestamp("created_at").defaultNow().notNull(),
53
- updatedAt: timestamp("updated_at")
54
- .$onUpdate(() => /* @__PURE__ */ new Date())
55
- .notNull(),
56
- },
57
- (table) => [index("auth_account_userId_idx").on(table.userId)],
58
- );
59
-
60
- export const auth_verification = pgTable(
61
- "auth_verification",
62
- {
63
- id: text("id").primaryKey(),
64
- identifier: text("identifier").notNull(),
65
- value: text("value").notNull(),
66
- expiresAt: timestamp("expires_at").notNull(),
67
- createdAt: timestamp("created_at").defaultNow().notNull(),
68
- updatedAt: timestamp("updated_at")
69
- .defaultNow()
70
- .$onUpdate(() => /* @__PURE__ */ new Date())
71
- .notNull(),
72
- },
73
- (table) => [index("auth_verification_identifier_idx").on(table.identifier)],
74
- );
75
-
76
- export const auth_jwks = pgTable("auth_jwks", {
77
- id: text("id").primaryKey(),
78
- publicKey: text("public_key").notNull(),
79
- privateKey: text("private_key").notNull(),
80
- createdAt: timestamp("created_at").notNull(),
81
- expiresAt: timestamp("expires_at"),
82
- });
83
-
84
- export const auth_userRelations = relations(auth_user, ({ many }) => ({
85
- auth_sessions: many(auth_session),
86
- auth_accounts: many(auth_account),
87
- }));
88
-
89
- export const auth_sessionRelations = relations(auth_session, ({ one }) => ({
90
- auth_user: one(auth_user, {
91
- fields: [auth_session.userId],
92
- references: [auth_user.id],
93
- }),
94
- }));
95
-
96
- export const auth_accountRelations = relations(auth_account, ({ one }) => ({
97
- auth_user: one(auth_user, {
98
- fields: [auth_account.userId],
99
- references: [auth_user.id],
100
- }),
101
- }));
@@ -1,12 +0,0 @@
1
- import { sql } from "drizzle-orm";
2
- import { check, integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
3
-
4
- export const instance_owner = pgTable(
5
- "instance_owner",
6
- {
7
- id: integer("id").primaryKey().default(1),
8
- userId: text("user_id").notNull(),
9
- claimedAt: timestamp("claimed_at").defaultNow().notNull(),
10
- },
11
- (table) => [check("instance_owner_singleton", sql`${table.id} = 1`)],
12
- );