Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,14 @@ SERVER_PORT=4000 # optional, server port (alias: PORT, PUBLIC_SERVER_URL.port) (

# Secrets
PUBLIC_X_ANON_KEY=...
X_API_KEY=...
X_API_KEY=...

DATABASE_URL=file:local.db

BETTER_AUTH_SECRET=...
BETTER_AUTH_URL=http://localhost:4000

GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=

PUBLIC_WEB_URL=http://localhost:3000
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ dist

*storybook.log
storybook-static

local.db
177 changes: 176 additions & 1 deletion bun.lock

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions packages/server/app.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import { cors } from "@elysiajs/cors";
import { Elysia } from "elysia";
import { auth } from "./lib/auth.ts";
import { usersRouter } from "./router/users.sample.ts";

const betterAuth = new Elysia({ name: "better-auth" })
.mount(auth.handler)
.macro({
auth: {
async resolve({ status, request: { headers } }) {
const session = await auth.api.getSession({
headers,
});
if (!session) return status(401);

return {
user: session.user,
session: session.session,
};
},
},
});

export const app = new Elysia({
prefix: "/api",
})
.use(betterAuth)
.use(cors())
.group("/users", (app) => app.use(usersRouter));

Expand Down
5 changes: 5 additions & 0 deletions packages/server/db/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { drizzle } from "drizzle-orm/libsql";

export const db = drizzle({
connection: { url: process.env.DATABASE_URL ?? "file:local.db" },
});
66 changes: 66 additions & 0 deletions packages/server/db/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";

// auth
export const user = sqliteTable("users", {
id: text("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
emailVerified: integer("email_verified", { mode: "boolean" })
.$defaultFn(() => false)
.notNull(),
image: text("image"),
createdAt: integer("created_at", { mode: "timestamp" })
.$defaultFn(() => /* @__PURE__ */ new Date())
.notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" })
.$defaultFn(() => /* @__PURE__ */ new Date())
.notNull(),
});

export const session = sqliteTable("sessions", {
id: text("id").primaryKey(),
expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
token: text("token").notNull().unique(),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
});

export const account = sqliteTable("accounts", {
id: text("id").primaryKey(),
accountId: text("account_id").notNull(),
providerId: text("provider_id").notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
idToken: text("id_token"),
accessTokenExpiresAt: integer("access_token_expires_at", {
mode: "timestamp",
}),
refreshTokenExpiresAt: integer("refresh_token_expires_at", {
mode: "timestamp",
}),
scope: text("scope"),
password: text("password"),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
});

// export const verification = sqliteTable("verifications", {
// id: text("id").primaryKey(),
// identifier: text("identifier").notNull(),
// value: text("value").notNull(),
// expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(),
// createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(
// () => /* @__PURE__ */ new Date(),
// ),
// updatedAt: integer("updated_at", { mode: "timestamp" }).$defaultFn(
// () => /* @__PURE__ */ new Date(),
// ),
// });
10 changes: 10 additions & 0 deletions packages/server/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from "drizzle-kit";

export default defineConfig({
out: "./drizzle",
schema: "./db/schema.ts",
dialect: "sqlite",
dbCredentials: {
url: process.env.DATABASE_URL ?? "file:local.db",
},
});
18 changes: 18 additions & 0 deletions packages/server/lib/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "../db/index.ts";
import * as schema from "../db/schema.ts";

export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "sqlite",
schema: schema,
}),
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
},
},
trustedOrigins: [process.env.PUBLIC_WEB_URL ?? "http://localhost:3000"],
});
7 changes: 6 additions & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@
"dev": "bun --env-file=../../.env run --watch ./serve.ts"
},
"devDependencies": {
"@types/bun": "^1.2.18"
"@types/bun": "^1.2.18",
"drizzle-kit": "^0.31.4",
"tsx": "^4.20.3"
},
"peerDependencies": {
"typescript": "^5.8.3"
},
"dependencies": {
"@elysiajs/cors": "^1.3.3",
"@libsql/client": "^0.15.10",
"@sinclair/typebox": "^0.34.37",
"better-auth": "^1.3.1",
"drizzle-orm": "^0.44.3",
"elysia": "^1.3.5"
}
}
4 changes: 4 additions & 0 deletions packages/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import Home from "./pages/Home.tsx";
import HowToUse from "./pages/HowToUse.tsx";
import NotFound from "./pages/NotFound.tsx";
import Notion from "./pages/Notion.tsx";
import Profile from "./pages/Profile.tsx";
import SignIn from "./pages/SignIn.tsx";
import UserManagement from "./sample/UserManagement.tsx";

/**
Expand Down Expand Up @@ -65,6 +67,8 @@ export default function App() {
<Route path="/how-to-use" element={<HowToUse />} />
<Route path="/notion" element={<Notion />} />
<Route path="/sample" element={<UserManagement />} />
<Route path="/sign-in" element={<SignIn />} />
<Route path="/profile" element={<Profile />} />
<Route path="*" element={<NotFound />} />
</Routes>
<Footer />
Expand Down
5 changes: 5 additions & 0 deletions packages/web/src/lib/auth-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
baseURL: import.meta.env.PUBLIC_SERVER_URL,
});
5 changes: 4 additions & 1 deletion packages/web/src/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type React from "react";
import { Link } from "react-router-dom";
import Logo from "/syllabus_icon.svg";
// import { useUser } from "@/app/UserContext";

Expand Down Expand Up @@ -55,7 +56,9 @@ export default function Home(): React.ReactElement {

{/* はじめるボタン */}
<div className="flex items-center justify-center">
<div className="btn btn-primary w-36 mt-24">はじめる</div>
<Link to="/sign-in" className="btn btn-primary w-36 mt-24">
サインインページへ
</Link>
</div>
</div>
</div>
Expand Down
11 changes: 11 additions & 0 deletions packages/web/src/pages/Profile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { authClient } from "@/lib/auth-client";

export default function Profile() {
const { data: session } = authClient.useSession();
const username = session?.user.name;
return (
<div>
{username ? <div>Hello {username}</div> : <div>Who are you?</div>}
</div>
);
}
23 changes: 23 additions & 0 deletions packages/web/src/pages/SignIn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { authClient } from "@/lib/auth-client";

export default function SignIn() {
const handleClick = async () => {
try {
await authClient.signIn.social({
provider: "google",
// Use an absolute path for the callbackURL to prevent redirecting to the server.
callbackURL: `${window.location.origin}/profile`,
});
} catch (error) {
console.error("error", error);
}
};

return (
<div>
<button type="button" onClick={handleClick} className="btn btn-primary">
sign in with google
</button>
</div>
);
}