@contentgrowth/content-auth 0.0.1
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/README.md +255 -0
- package/dist/backend/index.d.ts +1077 -0
- package/dist/backend/index.js +15 -0
- package/dist/chunk-526JE62U.js +142 -0
- package/dist/chunk-NKDKDBM2.js +17 -0
- package/dist/chunk-R5U7XKVJ.js +16 -0
- package/dist/chunk-VX6RJ5XJ.js +372 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +105 -0
- package/dist/frontend/client.d.ts +3161 -0
- package/dist/frontend/client.js +9 -0
- package/dist/frontend/index.d.ts +38 -0
- package/dist/frontend/index.js +19 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +31 -0
- package/dist/styles.css +264 -0
- package/package.json +72 -0
- package/schema/auth.sql +115 -0
- package/schema/schema.template.ts +167 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Hono,
|
|
3
|
+
authMiddleware,
|
|
4
|
+
createAuth,
|
|
5
|
+
createAuthApp,
|
|
6
|
+
schema_exports
|
|
7
|
+
} from "../chunk-526JE62U.js";
|
|
8
|
+
import "../chunk-R5U7XKVJ.js";
|
|
9
|
+
export {
|
|
10
|
+
Hono,
|
|
11
|
+
authMiddleware,
|
|
12
|
+
createAuth,
|
|
13
|
+
createAuthApp,
|
|
14
|
+
schema_exports as schema
|
|
15
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-R5U7XKVJ.js";
|
|
4
|
+
|
|
5
|
+
// src/backend/index.ts
|
|
6
|
+
import { betterAuth } from "better-auth";
|
|
7
|
+
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
8
|
+
import { drizzle } from "drizzle-orm/d1";
|
|
9
|
+
import { Hono } from "hono";
|
|
10
|
+
export * from "better-auth";
|
|
11
|
+
|
|
12
|
+
// src/backend/schema.ts
|
|
13
|
+
var schema_exports = {};
|
|
14
|
+
__export(schema_exports, {
|
|
15
|
+
accounts: () => accounts,
|
|
16
|
+
invitations: () => invitations,
|
|
17
|
+
members: () => members,
|
|
18
|
+
organizations: () => organizations,
|
|
19
|
+
sessions: () => sessions,
|
|
20
|
+
users: () => users,
|
|
21
|
+
verifications: () => verifications
|
|
22
|
+
});
|
|
23
|
+
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
|
24
|
+
var users = sqliteTable("users", {
|
|
25
|
+
id: text("id").primaryKey(),
|
|
26
|
+
name: text("name").notNull(),
|
|
27
|
+
email: text("email").notNull().unique(),
|
|
28
|
+
emailVerified: integer("emailVerified", { mode: "boolean" }).notNull(),
|
|
29
|
+
image: text("image"),
|
|
30
|
+
createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
|
|
31
|
+
updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull()
|
|
32
|
+
});
|
|
33
|
+
var sessions = sqliteTable("sessions", {
|
|
34
|
+
id: text("id").primaryKey(),
|
|
35
|
+
expiresAt: integer("expiresAt", { mode: "timestamp" }).notNull(),
|
|
36
|
+
token: text("token").notNull().unique(),
|
|
37
|
+
createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
|
|
38
|
+
updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull(),
|
|
39
|
+
ipAddress: text("ipAddress"),
|
|
40
|
+
userAgent: text("userAgent"),
|
|
41
|
+
userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
42
|
+
activeOrganizationId: text("activeOrganizationId")
|
|
43
|
+
});
|
|
44
|
+
var accounts = sqliteTable("accounts", {
|
|
45
|
+
id: text("id").primaryKey(),
|
|
46
|
+
accountId: text("accountId").notNull(),
|
|
47
|
+
providerId: text("providerId").notNull(),
|
|
48
|
+
userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
49
|
+
accessToken: text("accessToken"),
|
|
50
|
+
refreshToken: text("refreshToken"),
|
|
51
|
+
idToken: text("idToken"),
|
|
52
|
+
accessTokenExpiresAt: integer("accessTokenExpiresAt", { mode: "timestamp" }),
|
|
53
|
+
refreshTokenExpiresAt: integer("refreshTokenExpiresAt", { mode: "timestamp" }),
|
|
54
|
+
scope: text("scope"),
|
|
55
|
+
password: text("password"),
|
|
56
|
+
createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
|
|
57
|
+
updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull()
|
|
58
|
+
});
|
|
59
|
+
var verifications = sqliteTable("verifications", {
|
|
60
|
+
id: text("id").primaryKey(),
|
|
61
|
+
identifier: text("identifier").notNull(),
|
|
62
|
+
value: text("value").notNull(),
|
|
63
|
+
expiresAt: integer("expiresAt", { mode: "timestamp" }).notNull(),
|
|
64
|
+
createdAt: integer("createdAt", { mode: "timestamp" }),
|
|
65
|
+
updatedAt: integer("updatedAt", { mode: "timestamp" })
|
|
66
|
+
});
|
|
67
|
+
var organizations = sqliteTable("organizations", {
|
|
68
|
+
id: text("id").primaryKey(),
|
|
69
|
+
name: text("name").notNull(),
|
|
70
|
+
slug: text("slug").unique(),
|
|
71
|
+
logo: text("logo"),
|
|
72
|
+
createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
|
|
73
|
+
metadata: text("metadata")
|
|
74
|
+
});
|
|
75
|
+
var members = sqliteTable("members", {
|
|
76
|
+
id: text("id").primaryKey(),
|
|
77
|
+
organizationId: text("organizationId").notNull().references(() => organizations.id, { onDelete: "cascade" }),
|
|
78
|
+
userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
79
|
+
role: text("role").notNull(),
|
|
80
|
+
createdAt: integer("createdAt", { mode: "timestamp" }).notNull()
|
|
81
|
+
});
|
|
82
|
+
var invitations = sqliteTable("invitations", {
|
|
83
|
+
id: text("id").primaryKey(),
|
|
84
|
+
organizationId: text("organizationId").notNull().references(() => organizations.id, { onDelete: "cascade" }),
|
|
85
|
+
email: text("email").notNull(),
|
|
86
|
+
role: text("role"),
|
|
87
|
+
status: text("status").notNull(),
|
|
88
|
+
expiresAt: integer("expiresAt", { mode: "timestamp" }).notNull(),
|
|
89
|
+
inviterId: text("inviterId").notNull().references(() => users.id, { onDelete: "cascade" })
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// src/backend/index.ts
|
|
93
|
+
var createAuth = (config) => {
|
|
94
|
+
let db;
|
|
95
|
+
let provider = config.provider || "sqlite";
|
|
96
|
+
if (config.database && typeof config.database.prepare === "function") {
|
|
97
|
+
db = drizzle(config.database, { schema: schema_exports });
|
|
98
|
+
} else {
|
|
99
|
+
db = config.database;
|
|
100
|
+
}
|
|
101
|
+
const { database, secret, baseUrl, provider: _, ...rest } = config;
|
|
102
|
+
let adapterOptions = {
|
|
103
|
+
provider,
|
|
104
|
+
schema: {
|
|
105
|
+
user: users,
|
|
106
|
+
session: sessions,
|
|
107
|
+
account: accounts,
|
|
108
|
+
verification: verifications,
|
|
109
|
+
organization: organizations,
|
|
110
|
+
member: members,
|
|
111
|
+
invitation: invitations
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const auth = betterAuth({
|
|
115
|
+
database: drizzleAdapter(db, adapterOptions),
|
|
116
|
+
secret,
|
|
117
|
+
baseURL: baseUrl,
|
|
118
|
+
...rest
|
|
119
|
+
});
|
|
120
|
+
return auth;
|
|
121
|
+
};
|
|
122
|
+
var authMiddleware = (auth) => {
|
|
123
|
+
return async (c) => {
|
|
124
|
+
return auth.handler(c.req.raw);
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
var createAuthApp = (config) => {
|
|
128
|
+
const auth = createAuth(config);
|
|
129
|
+
const app = new Hono();
|
|
130
|
+
app.all("/api/auth/*", async (c) => {
|
|
131
|
+
return auth.handler(c.req.raw);
|
|
132
|
+
});
|
|
133
|
+
return { app, auth };
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
schema_exports,
|
|
138
|
+
Hono,
|
|
139
|
+
createAuth,
|
|
140
|
+
authMiddleware,
|
|
141
|
+
createAuthApp
|
|
142
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/frontend/client.ts
|
|
2
|
+
import { createAuthClient } from "better-auth/react";
|
|
3
|
+
import { organizationClient } from "better-auth/client/plugins";
|
|
4
|
+
var createClient = (baseUrl) => {
|
|
5
|
+
return createAuthClient({
|
|
6
|
+
baseURL: baseUrl,
|
|
7
|
+
plugins: [
|
|
8
|
+
organizationClient()
|
|
9
|
+
]
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var authClient = createClient();
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
createClient,
|
|
16
|
+
authClient
|
|
17
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
__require,
|
|
15
|
+
__export
|
|
16
|
+
};
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import {
|
|
2
|
+
authClient
|
|
3
|
+
} from "./chunk-NKDKDBM2.js";
|
|
4
|
+
|
|
5
|
+
// src/frontend/components/AuthForm.tsx
|
|
6
|
+
import React, { useState } from "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
var AuthForm = ({
|
|
9
|
+
client = authClient,
|
|
10
|
+
onSuccess,
|
|
11
|
+
className,
|
|
12
|
+
socialProviders = [],
|
|
13
|
+
socialLayout = "row",
|
|
14
|
+
title,
|
|
15
|
+
width = "default",
|
|
16
|
+
layout = "default",
|
|
17
|
+
socialPosition = "top",
|
|
18
|
+
view,
|
|
19
|
+
onSwitchMode
|
|
20
|
+
}) => {
|
|
21
|
+
const [isLogin, setIsLogin] = useState(view !== "signup");
|
|
22
|
+
const [email, setEmail] = useState("");
|
|
23
|
+
const [password, setPassword] = useState("");
|
|
24
|
+
const [name, setName] = useState("");
|
|
25
|
+
const [loading, setLoading] = useState(false);
|
|
26
|
+
const [error, setError] = useState(null);
|
|
27
|
+
const [mounted, setMounted] = useState(false);
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
setMounted(true);
|
|
30
|
+
setIsLogin(view !== "signup");
|
|
31
|
+
}, [view]);
|
|
32
|
+
const handleSubmit = async (e) => {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
setLoading(true);
|
|
35
|
+
setError(null);
|
|
36
|
+
try {
|
|
37
|
+
if (isLogin) {
|
|
38
|
+
const { error: error2 } = await client.signIn.email({
|
|
39
|
+
email,
|
|
40
|
+
password
|
|
41
|
+
});
|
|
42
|
+
if (error2) throw error2;
|
|
43
|
+
} else {
|
|
44
|
+
const { error: error2 } = await client.signUp.email({
|
|
45
|
+
email,
|
|
46
|
+
password,
|
|
47
|
+
name
|
|
48
|
+
});
|
|
49
|
+
if (error2) throw error2;
|
|
50
|
+
}
|
|
51
|
+
onSuccess?.();
|
|
52
|
+
} catch (err) {
|
|
53
|
+
setError(err.message || "An error occurred");
|
|
54
|
+
} finally {
|
|
55
|
+
setLoading(false);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const handleSocialLogin = async (provider) => {
|
|
59
|
+
setLoading(true);
|
|
60
|
+
try {
|
|
61
|
+
await client.signIn.social({
|
|
62
|
+
provider,
|
|
63
|
+
callbackURL: window.location.href
|
|
64
|
+
});
|
|
65
|
+
} catch (err) {
|
|
66
|
+
setError(err.message || `Failed to sign in with ${provider}`);
|
|
67
|
+
setLoading(false);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const socialClass = socialLayout === "column" ? "ca-social-column" : "ca-social-grid";
|
|
71
|
+
let widthClass = "";
|
|
72
|
+
if (width === "compact") widthClass = "ca-width-compact";
|
|
73
|
+
else if (width === "wide") widthClass = "ca-width-wide";
|
|
74
|
+
else widthClass = "ca-width-default";
|
|
75
|
+
const containerClass = `ca-container ${layout === "split" ? "ca-layout-split" : ""} ${widthClass} ${className || ""}`;
|
|
76
|
+
const renderSocials = () => socialProviders.length > 0 && /* @__PURE__ */ jsx("div", { className: socialClass, children: socialProviders.map((provider) => /* @__PURE__ */ jsxs(
|
|
77
|
+
"button",
|
|
78
|
+
{
|
|
79
|
+
type: "button",
|
|
80
|
+
className: `ca-button ca-button-social ca-button-${provider}`,
|
|
81
|
+
onClick: () => handleSocialLogin(provider),
|
|
82
|
+
disabled: loading,
|
|
83
|
+
children: [
|
|
84
|
+
provider === "google" && /* @__PURE__ */ jsx("svg", { className: "ca-icon", viewBox: "0 0 24 24", width: "20", height: "20", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsxs("g", { transform: "matrix(1, 0, 0, 1, 27.009001, -39.238998)", children: [
|
|
85
|
+
/* @__PURE__ */ jsx("path", { fill: "#4285F4", d: "M -3.264 51.509 C -3.264 50.719 -3.334 49.969 -3.454 49.239 L -14.754 49.239 L -14.754 53.749 L -8.284 53.749 C -8.574 55.229 -9.424 56.479 -10.684 57.329 L -10.684 60.329 L -6.824 60.329 C -4.564 58.239 -3.264 55.159 -3.264 51.509 Z" }),
|
|
86
|
+
/* @__PURE__ */ jsx("path", { fill: "#34A853", d: "M -14.754 63.239 C -11.514 63.239 -8.804 62.159 -6.824 60.329 L -10.684 57.329 C -11.764 58.049 -13.134 58.489 -14.754 58.489 C -17.884 58.489 -20.534 56.379 -21.484 53.529 L -25.464 53.529 L -25.464 56.619 C -23.494 60.539 -19.444 63.239 -14.754 63.239 Z" }),
|
|
87
|
+
/* @__PURE__ */ jsx("path", { fill: "#FBBC05", d: "M -21.484 53.529 C -21.734 52.809 -21.864 52.039 -21.864 51.239 C -21.864 50.439 -21.734 49.669 -21.484 48.949 L -21.484 45.859 L -25.464 45.859 C -26.284 47.479 -26.754 49.299 -26.754 51.239 C -26.754 53.179 -26.284 54.999 -25.464 56.619 L -21.484 53.529 Z" }),
|
|
88
|
+
/* @__PURE__ */ jsx("path", { fill: "#EA4335", d: "M -14.754 43.989 C -12.984 43.989 -11.424 44.599 -10.174 45.789 L -6.744 42.359 C -8.804 40.429 -11.514 39.239 -14.754 39.239 C -19.444 39.239 -23.494 41.939 -25.464 45.859 L -21.484 48.949 C -20.534 46.099 -17.884 43.989 -14.754 43.989 Z" })
|
|
89
|
+
] }) }),
|
|
90
|
+
provider === "github" && /* @__PURE__ */ jsx("svg", { className: "ca-icon", viewBox: "0 0 24 24", width: "20", height: "20", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx("path", { d: "M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.464-1.11-1.464-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.026A9.564 9.564 0 0 1 12 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0 0 22 12c0-5.523-4.477-10-10-10z", fill: "currentColor" }) }),
|
|
91
|
+
/* @__PURE__ */ jsx("span", { className: "ca-btn-text", children: provider === "github" ? "GitHub" : "Google" })
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
provider
|
|
95
|
+
)) });
|
|
96
|
+
const renderForm = () => {
|
|
97
|
+
if (!mounted) {
|
|
98
|
+
return /* @__PURE__ */ jsx("div", { className: "ca-form", children: /* @__PURE__ */ jsxs("div", { className: "ca-input-group", style: { opacity: 0 }, children: [
|
|
99
|
+
/* @__PURE__ */ jsx("label", { className: "ca-label", children: "Loading" }),
|
|
100
|
+
/* @__PURE__ */ jsx("input", { className: "ca-input", disabled: true })
|
|
101
|
+
] }) });
|
|
102
|
+
}
|
|
103
|
+
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "ca-form", children: [
|
|
104
|
+
!isLogin && /* @__PURE__ */ jsxs("div", { className: "ca-input-group", children: [
|
|
105
|
+
/* @__PURE__ */ jsx("label", { className: "ca-label", htmlFor: "name", children: "Name" }),
|
|
106
|
+
/* @__PURE__ */ jsx(
|
|
107
|
+
"input",
|
|
108
|
+
{
|
|
109
|
+
id: "name",
|
|
110
|
+
type: "text",
|
|
111
|
+
className: "ca-input",
|
|
112
|
+
value: name,
|
|
113
|
+
onChange: (e) => setName(e.target.value),
|
|
114
|
+
required: true
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
] }),
|
|
118
|
+
/* @__PURE__ */ jsxs("div", { className: "ca-input-group", children: [
|
|
119
|
+
/* @__PURE__ */ jsx("label", { className: "ca-label", htmlFor: "email", children: "Email" }),
|
|
120
|
+
/* @__PURE__ */ jsx(
|
|
121
|
+
"input",
|
|
122
|
+
{
|
|
123
|
+
id: "email",
|
|
124
|
+
type: "email",
|
|
125
|
+
className: "ca-input",
|
|
126
|
+
value: email,
|
|
127
|
+
onChange: (e) => setEmail(e.target.value),
|
|
128
|
+
required: true
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
] }),
|
|
132
|
+
/* @__PURE__ */ jsxs("div", { className: "ca-input-group", children: [
|
|
133
|
+
/* @__PURE__ */ jsx("label", { className: "ca-label", htmlFor: "password", children: "Password" }),
|
|
134
|
+
/* @__PURE__ */ jsx(
|
|
135
|
+
"input",
|
|
136
|
+
{
|
|
137
|
+
id: "password",
|
|
138
|
+
type: "password",
|
|
139
|
+
className: "ca-input",
|
|
140
|
+
value: password,
|
|
141
|
+
onChange: (e) => setPassword(e.target.value),
|
|
142
|
+
required: true
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
] }),
|
|
146
|
+
error && /* @__PURE__ */ jsx("div", { className: "ca-error", children: error }),
|
|
147
|
+
/* @__PURE__ */ jsx("button", { type: "submit", className: "ca-button", disabled: loading, children: loading ? "Loading..." : isLogin ? "Sign In" : "Sign Up" })
|
|
148
|
+
] });
|
|
149
|
+
};
|
|
150
|
+
const renderFooter = () => /* @__PURE__ */ jsxs("div", { className: "ca-footer", children: [
|
|
151
|
+
isLogin ? "Don't have an account? " : "Already have an account? ",
|
|
152
|
+
/* @__PURE__ */ jsx(
|
|
153
|
+
"button",
|
|
154
|
+
{
|
|
155
|
+
className: "ca-link",
|
|
156
|
+
onClick: () => {
|
|
157
|
+
if (onSwitchMode) {
|
|
158
|
+
onSwitchMode();
|
|
159
|
+
} else {
|
|
160
|
+
setIsLogin(!isLogin);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
type: "button",
|
|
164
|
+
children: isLogin ? "Sign up" : "Sign in"
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
] });
|
|
168
|
+
const titleContent = title ? typeof title === "string" ? /* @__PURE__ */ jsx("h2", { className: "ca-title", children: title }) : title : /* @__PURE__ */ jsx("h2", { className: "ca-title", children: isLogin ? "Welcome Back" : "Create Account" });
|
|
169
|
+
if (layout === "split") {
|
|
170
|
+
return /* @__PURE__ */ jsxs("div", { className: containerClass, children: [
|
|
171
|
+
titleContent,
|
|
172
|
+
/* @__PURE__ */ jsxs("div", { className: "ca-split-body", children: [
|
|
173
|
+
/* @__PURE__ */ jsxs("div", { className: "ca-split-main", children: [
|
|
174
|
+
renderForm(),
|
|
175
|
+
renderFooter()
|
|
176
|
+
] }),
|
|
177
|
+
socialProviders.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
178
|
+
/* @__PURE__ */ jsx("div", { className: "ca-split-divider", children: /* @__PURE__ */ jsx("span", { className: "ca-split-divider-text", children: "Or" }) }),
|
|
179
|
+
/* @__PURE__ */ jsxs("div", { className: "ca-split-social", children: [
|
|
180
|
+
/* @__PURE__ */ jsx("h3", { className: "ca-social-header", children: isLogin ? "Sign In With" : "Sign Up With" }),
|
|
181
|
+
renderSocials()
|
|
182
|
+
] })
|
|
183
|
+
] })
|
|
184
|
+
] })
|
|
185
|
+
] });
|
|
186
|
+
}
|
|
187
|
+
return /* @__PURE__ */ jsxs("div", { className: containerClass, children: [
|
|
188
|
+
titleContent,
|
|
189
|
+
socialPosition === "top" && socialProviders.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
190
|
+
renderSocials(),
|
|
191
|
+
/* @__PURE__ */ jsx("div", { className: "ca-divider", children: /* @__PURE__ */ jsx("span", { className: "ca-divider-text", children: "Or continue with" }) })
|
|
192
|
+
] }),
|
|
193
|
+
renderForm(),
|
|
194
|
+
socialPosition === "bottom" && socialProviders.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
195
|
+
/* @__PURE__ */ jsx("div", { className: "ca-divider", children: /* @__PURE__ */ jsx("span", { className: "ca-divider-text", children: "Or continue with" }) }),
|
|
196
|
+
renderSocials()
|
|
197
|
+
] }),
|
|
198
|
+
renderFooter()
|
|
199
|
+
] });
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// src/frontend/components/Organization.tsx
|
|
203
|
+
import { useState as useState2, useEffect } from "react";
|
|
204
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
205
|
+
var CreateOrganizationForm = ({
|
|
206
|
+
client = authClient,
|
|
207
|
+
className,
|
|
208
|
+
onSuccess,
|
|
209
|
+
onError
|
|
210
|
+
}) => {
|
|
211
|
+
const [name, setName] = useState2("");
|
|
212
|
+
const [slug, setSlug] = useState2("");
|
|
213
|
+
const [loading, setLoading] = useState2(false);
|
|
214
|
+
const handleSubmit = async (e) => {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
setLoading(true);
|
|
217
|
+
try {
|
|
218
|
+
const result = await client.organization.create({
|
|
219
|
+
name,
|
|
220
|
+
slug
|
|
221
|
+
});
|
|
222
|
+
if (result.error) throw result.error;
|
|
223
|
+
setName("");
|
|
224
|
+
setSlug("");
|
|
225
|
+
onSuccess?.(result.data);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
onError?.(err.message || "Failed to create organization");
|
|
228
|
+
} finally {
|
|
229
|
+
setLoading(false);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
return /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: `ca-form ${className || ""}`, children: [
|
|
233
|
+
/* @__PURE__ */ jsx2("h3", { className: "ca-subtitle", children: "Create Organization" }),
|
|
234
|
+
/* @__PURE__ */ jsxs2("div", { className: "ca-input-group", children: [
|
|
235
|
+
/* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Organization Name" }),
|
|
236
|
+
/* @__PURE__ */ jsx2(
|
|
237
|
+
"input",
|
|
238
|
+
{
|
|
239
|
+
type: "text",
|
|
240
|
+
className: "ca-input",
|
|
241
|
+
value: name,
|
|
242
|
+
onChange: (e) => {
|
|
243
|
+
setName(e.target.value);
|
|
244
|
+
if (!slug) setSlug(e.target.value.toLowerCase().replace(/\s+/g, "-"));
|
|
245
|
+
},
|
|
246
|
+
required: true,
|
|
247
|
+
placeholder: "Acme Corp"
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
] }),
|
|
251
|
+
/* @__PURE__ */ jsxs2("div", { className: "ca-input-group", children: [
|
|
252
|
+
/* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Slug" }),
|
|
253
|
+
/* @__PURE__ */ jsx2(
|
|
254
|
+
"input",
|
|
255
|
+
{
|
|
256
|
+
type: "text",
|
|
257
|
+
className: "ca-input",
|
|
258
|
+
value: slug,
|
|
259
|
+
onChange: (e) => setSlug(e.target.value),
|
|
260
|
+
required: true,
|
|
261
|
+
placeholder: "acme-corp"
|
|
262
|
+
}
|
|
263
|
+
)
|
|
264
|
+
] }),
|
|
265
|
+
/* @__PURE__ */ jsx2("button", { type: "submit", className: "ca-button", disabled: loading, children: loading ? "Creating..." : "Create Organization" })
|
|
266
|
+
] });
|
|
267
|
+
};
|
|
268
|
+
var OrganizationSwitcher = ({
|
|
269
|
+
client = authClient,
|
|
270
|
+
className,
|
|
271
|
+
currentOrgId,
|
|
272
|
+
onSuccess
|
|
273
|
+
}) => {
|
|
274
|
+
const [orgs, setOrgs] = useState2([]);
|
|
275
|
+
const [loading, setLoading] = useState2(true);
|
|
276
|
+
useEffect(() => {
|
|
277
|
+
const fetchOrgs = async () => {
|
|
278
|
+
const { data } = await client.organization.list({});
|
|
279
|
+
if (data) setOrgs(data);
|
|
280
|
+
setLoading(false);
|
|
281
|
+
};
|
|
282
|
+
fetchOrgs();
|
|
283
|
+
}, [client]);
|
|
284
|
+
const handleSwitch = async (orgId) => {
|
|
285
|
+
await client.organization.setActive({ organizationId: orgId });
|
|
286
|
+
onSuccess?.(orgId);
|
|
287
|
+
};
|
|
288
|
+
if (loading) return /* @__PURE__ */ jsx2("div", { className: "ca-loading", children: "Loading..." });
|
|
289
|
+
return /* @__PURE__ */ jsxs2("div", { className: `ca-org-switcher ${className || ""}`, children: [
|
|
290
|
+
/* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Select Organization" }),
|
|
291
|
+
/* @__PURE__ */ jsxs2(
|
|
292
|
+
"select",
|
|
293
|
+
{
|
|
294
|
+
className: "ca-select",
|
|
295
|
+
value: currentOrgId || "",
|
|
296
|
+
onChange: (e) => handleSwitch(e.target.value),
|
|
297
|
+
children: [
|
|
298
|
+
/* @__PURE__ */ jsx2("option", { value: "", disabled: true, children: "Select an organization" }),
|
|
299
|
+
orgs.map((org) => /* @__PURE__ */ jsx2("option", { value: org.id, children: org.name }, org.id))
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
)
|
|
303
|
+
] });
|
|
304
|
+
};
|
|
305
|
+
var InviteMemberForm = ({
|
|
306
|
+
client = authClient,
|
|
307
|
+
className,
|
|
308
|
+
onSuccess,
|
|
309
|
+
onError
|
|
310
|
+
}) => {
|
|
311
|
+
const [email, setEmail] = useState2("");
|
|
312
|
+
const [role, setRole] = useState2("member");
|
|
313
|
+
const [loading, setLoading] = useState2(false);
|
|
314
|
+
const handleSubmit = async (e) => {
|
|
315
|
+
e.preventDefault();
|
|
316
|
+
setLoading(true);
|
|
317
|
+
try {
|
|
318
|
+
const result = await client.organization.inviteMember({
|
|
319
|
+
email,
|
|
320
|
+
role
|
|
321
|
+
});
|
|
322
|
+
if (result.error) throw result.error;
|
|
323
|
+
setEmail("");
|
|
324
|
+
onSuccess?.(result.data);
|
|
325
|
+
} catch (err) {
|
|
326
|
+
onError?.(err.message || "Invitation failed");
|
|
327
|
+
} finally {
|
|
328
|
+
setLoading(false);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
return /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: `ca-form ${className || ""}`, children: [
|
|
332
|
+
/* @__PURE__ */ jsx2("h3", { className: "ca-subtitle", children: "Invite Member" }),
|
|
333
|
+
/* @__PURE__ */ jsxs2("div", { className: "ca-input-group", children: [
|
|
334
|
+
/* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Email Address" }),
|
|
335
|
+
/* @__PURE__ */ jsx2(
|
|
336
|
+
"input",
|
|
337
|
+
{
|
|
338
|
+
type: "email",
|
|
339
|
+
className: "ca-input",
|
|
340
|
+
value: email,
|
|
341
|
+
onChange: (e) => setEmail(e.target.value),
|
|
342
|
+
required: true,
|
|
343
|
+
placeholder: "colleague@example.com"
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
] }),
|
|
347
|
+
/* @__PURE__ */ jsxs2("div", { className: "ca-input-group", children: [
|
|
348
|
+
/* @__PURE__ */ jsx2("label", { className: "ca-label", children: "Role" }),
|
|
349
|
+
/* @__PURE__ */ jsxs2(
|
|
350
|
+
"select",
|
|
351
|
+
{
|
|
352
|
+
className: "ca-select",
|
|
353
|
+
value: role,
|
|
354
|
+
onChange: (e) => setRole(e.target.value),
|
|
355
|
+
children: [
|
|
356
|
+
/* @__PURE__ */ jsx2("option", { value: "member", children: "Member" }),
|
|
357
|
+
/* @__PURE__ */ jsx2("option", { value: "admin", children: "Admin" }),
|
|
358
|
+
/* @__PURE__ */ jsx2("option", { value: "owner", children: "Owner" })
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
] }),
|
|
363
|
+
/* @__PURE__ */ jsx2("button", { type: "submit", className: "ca-button", disabled: loading, children: loading ? "Sending Invite..." : "Send Invite" })
|
|
364
|
+
] });
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
export {
|
|
368
|
+
AuthForm,
|
|
369
|
+
CreateOrganizationForm,
|
|
370
|
+
OrganizationSwitcher,
|
|
371
|
+
InviteMemberForm
|
|
372
|
+
};
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
__require
|
|
4
|
+
} from "./chunk-R5U7XKVJ.js";
|
|
5
|
+
|
|
6
|
+
// src/cli.ts
|
|
7
|
+
import { cac } from "cac";
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
10
|
+
import { dirname, join, resolve } from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
var cli = cac("content-auth");
|
|
14
|
+
cli.command("init", "Initialize database schema (SQL) in your project").option("-o, --output <path>", "Output path for schema file", { default: "./migrations/0000_auth.sql" }).option("--force", "Overwrite existing file").action((options) => {
|
|
15
|
+
const outputPath = resolve(process.cwd(), options.output);
|
|
16
|
+
const outputDir = dirname(outputPath);
|
|
17
|
+
if (existsSync(outputPath) && !options.force) {
|
|
18
|
+
console.error(`\u274C File already exists: ${outputPath}`);
|
|
19
|
+
console.error(" Use --force to overwrite, or specify a different output with -o");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const schemaPath = join(__dirname, "..", "schema", "auth.sql");
|
|
24
|
+
if (!existsSync(schemaPath)) {
|
|
25
|
+
console.error(`\u274C Schema template not found at: ${schemaPath}`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
if (!existsSync(outputDir)) {
|
|
29
|
+
mkdirSync(outputDir, { recursive: true });
|
|
30
|
+
console.log(`\u{1F4C1} Created directory: ${outputDir}`);
|
|
31
|
+
}
|
|
32
|
+
const schemaContent = readFileSync(schemaPath, "utf-8");
|
|
33
|
+
writeFileSync(outputPath, schemaContent);
|
|
34
|
+
console.log(`\u2705 SQL schema initialized at: ${outputPath}`);
|
|
35
|
+
console.log("");
|
|
36
|
+
console.log("Next steps:");
|
|
37
|
+
console.log(" 1. Add your application tables at the bottom of the file");
|
|
38
|
+
console.log(" 2. Run migrations:");
|
|
39
|
+
console.log(" wrangler d1 migrations apply DB --local");
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error("\u274C Failed to initialize schema:", error);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
cli.command("init-drizzle", "Initialize Drizzle ORM schema (TypeScript) in your project").option("-o, --output <path>", "Output path for schema file", { default: "./src/db/schema.ts" }).option("--force", "Overwrite existing file").action((options) => {
|
|
46
|
+
const outputPath = resolve(process.cwd(), options.output);
|
|
47
|
+
const outputDir = dirname(outputPath);
|
|
48
|
+
if (existsSync(outputPath) && !options.force) {
|
|
49
|
+
console.error(`\u274C File already exists: ${outputPath}`);
|
|
50
|
+
console.error(" Use --force to overwrite, or specify a different output with -o");
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const templatePath = join(__dirname, "..", "schema", "schema.template.ts");
|
|
55
|
+
if (!existsSync(templatePath)) {
|
|
56
|
+
console.error(`\u274C Schema template not found at: ${templatePath}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
if (!existsSync(outputDir)) {
|
|
60
|
+
mkdirSync(outputDir, { recursive: true });
|
|
61
|
+
console.log(`\u{1F4C1} Created directory: ${outputDir}`);
|
|
62
|
+
}
|
|
63
|
+
const templateContent = readFileSync(templatePath, "utf-8");
|
|
64
|
+
writeFileSync(outputPath, templateContent);
|
|
65
|
+
console.log(`\u2705 Drizzle schema initialized at: ${outputPath}`);
|
|
66
|
+
console.log("");
|
|
67
|
+
console.log("Next steps:");
|
|
68
|
+
console.log(" 1. Add your application tables at the bottom of the file");
|
|
69
|
+
console.log(" 2. Create drizzle.config.ts in your project root");
|
|
70
|
+
console.log(" 3. Generate migrations: npm run db:generate");
|
|
71
|
+
console.log(" 4. Apply migrations: npm run dev");
|
|
72
|
+
console.log("");
|
|
73
|
+
console.log("Recommended package.json scripts:");
|
|
74
|
+
console.log(' "db:generate": "drizzle-kit generate"');
|
|
75
|
+
console.log(' "db:check": "drizzle-kit check"');
|
|
76
|
+
console.log(' "db:migrate:local": "wrangler d1 migrations apply DB --local"');
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("\u274C Failed to initialize schema:", error);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
cli.command("migrate", "Run database migrations (deprecated - use wrangler directly)").option("--db <db>", "Database name (for D1)", { default: "DB" }).action((options) => {
|
|
83
|
+
console.log("Running migrations...");
|
|
84
|
+
try {
|
|
85
|
+
console.log("Generating schema...");
|
|
86
|
+
execSync("npx @better-auth/cli generate", { stdio: "inherit" });
|
|
87
|
+
console.log("Migration generation complete. Please check for generated SQL files.");
|
|
88
|
+
console.log("To apply to D1, run: npx wrangler d1 execute <DATABASE_NAME> --local --file=<MIGRATION_FILE>");
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error("Migration failed:", error);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
cli.command("secret", "Generate a secure secret key for Better Auth").action(() => {
|
|
95
|
+
const { randomBytes } = __require("crypto");
|
|
96
|
+
const secret = randomBytes(32).toString("hex");
|
|
97
|
+
console.log("\nGenerated Secret Key:");
|
|
98
|
+
console.log(secret);
|
|
99
|
+
console.log("\nAdd this to your .dev.vars or environment variables:");
|
|
100
|
+
console.log(`BETTER_AUTH_SECRET="${secret}"
|
|
101
|
+
`);
|
|
102
|
+
});
|
|
103
|
+
cli.version("0.0.1");
|
|
104
|
+
cli.help();
|
|
105
|
+
cli.parse();
|