From fd01a2a8d20522e55a24d4422528d53b57b1e1e2 Mon Sep 17 00:00:00 2001 From: RuiOkazaki Date: Sun, 25 Aug 2024 01:42:44 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20d1,=20drizzle=E3=81=AE=E5=8B=95?= =?UTF-8?q?=E4=BD=9C=E7=A2=BA=E8=AA=8D=E3=80=81scripts=E3=82=92=E4=BD=BF?= =?UTF-8?q?=E3=81=84=E3=82=84=E3=81=99=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E6=95=B4=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/db/migrations/0000_fast_the_twelve.sql | 5 - app/db/migrations/meta/0000_snapshot.json | 47 ------- app/db/schema.server.ts | 7 - app/libs/drizzle/clear.sql | 2 + app/libs/drizzle/client.server.ts | 6 + .../migrations/0000_sharp_piledriver.sql | 18 +++ .../migrations/meta/0000_snapshot.json | 129 ++++++++++++++++++ .../drizzle}/migrations/meta/_journal.json | 4 +- app/libs/drizzle/schema.ts | 22 +++ app/routes/resources.tsx | 64 --------- bun.lockb | Bin 374317 -> 379210 bytes drizzle.config.ts | 8 ++ package.json | 20 +-- worker-configuration.d.ts | 6 +- wrangler.toml | 2 +- 15 files changed, 204 insertions(+), 136 deletions(-) delete mode 100644 app/db/migrations/0000_fast_the_twelve.sql delete mode 100644 app/db/migrations/meta/0000_snapshot.json delete mode 100644 app/db/schema.server.ts create mode 100644 app/libs/drizzle/clear.sql create mode 100644 app/libs/drizzle/client.server.ts create mode 100644 app/libs/drizzle/migrations/0000_sharp_piledriver.sql create mode 100644 app/libs/drizzle/migrations/meta/0000_snapshot.json rename app/{db => libs/drizzle}/migrations/meta/_journal.json (67%) create mode 100644 app/libs/drizzle/schema.ts delete mode 100644 app/routes/resources.tsx create mode 100644 drizzle.config.ts diff --git a/app/db/migrations/0000_fast_the_twelve.sql b/app/db/migrations/0000_fast_the_twelve.sql deleted file mode 100644 index 7370a77..0000000 --- a/app/db/migrations/0000_fast_the_twelve.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE `resources` ( - `id` integer PRIMARY KEY NOT NULL, - `title` text NOT NULL, - `href` text NOT NULL -); diff --git a/app/db/migrations/meta/0000_snapshot.json b/app/db/migrations/meta/0000_snapshot.json deleted file mode 100644 index e9ff836..0000000 --- a/app/db/migrations/meta/0000_snapshot.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "version": "6", - "dialect": "sqlite", - "id": "c3c22972-b5ac-4b05-9ae7-8ada87f5e5a2", - "prevId": "00000000-0000-0000-0000-000000000000", - "tables": { - "resources": { - "name": "resources", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "href": { - "name": "href", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - } - }, - "enums": {}, - "_meta": { - "schemas": {}, - "tables": {}, - "columns": {} - }, - "internal": { - "indexes": {} - } -} \ No newline at end of file diff --git a/app/db/schema.server.ts b/app/db/schema.server.ts deleted file mode 100644 index cbfe04d..0000000 --- a/app/db/schema.server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; - -export const resources = sqliteTable("resources", { - id: integer("id").primaryKey(), - title: text("title").notNull(), - href: text("href").notNull(), -}); diff --git a/app/libs/drizzle/clear.sql b/app/libs/drizzle/clear.sql new file mode 100644 index 0000000..21d746d --- /dev/null +++ b/app/libs/drizzle/clear.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS posts; +DROP TABLE IF EXISTS users; diff --git a/app/libs/drizzle/client.server.ts b/app/libs/drizzle/client.server.ts new file mode 100644 index 0000000..f7ed1b5 --- /dev/null +++ b/app/libs/drizzle/client.server.ts @@ -0,0 +1,6 @@ +import { drizzle } from "drizzle-orm/d1"; + +export const getDBClient = (d1: D1Database) => { + const db = drizzle(d1, { logger: import.meta.env.DEV }); + return db; +}; diff --git a/app/libs/drizzle/migrations/0000_sharp_piledriver.sql b/app/libs/drizzle/migrations/0000_sharp_piledriver.sql new file mode 100644 index 0000000..37e8355 --- /dev/null +++ b/app/libs/drizzle/migrations/0000_sharp_piledriver.sql @@ -0,0 +1,18 @@ +CREATE TABLE `posts` ( + `id` integer PRIMARY KEY NOT NULL, + `body` text, + `user_id` integer, + `created_at` text DEFAULT (current_timestamp) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action +); +--> statement-breakpoint +CREATE TABLE `users` ( + `id` integer PRIMARY KEY NOT NULL, + `provider` text NOT NULL, + `provider_id` text NOT NULL, + `name` text NOT NULL, + `icon` text, + `created_at` text DEFAULT (current_timestamp) NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX `users_provider_id_unique` ON `users` (`provider_id`); \ No newline at end of file diff --git a/app/libs/drizzle/migrations/meta/0000_snapshot.json b/app/libs/drizzle/migrations/meta/0000_snapshot.json new file mode 100644 index 0000000..98d9a70 --- /dev/null +++ b/app/libs/drizzle/migrations/meta/0000_snapshot.json @@ -0,0 +1,129 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "a0c45071-1d9d-4fff-b96d-52932ed1a195", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "posts": { + "name": "posts", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + } + }, + "indexes": {}, + "foreignKeys": { + "posts_user_id_users_id_fk": { + "name": "posts_user_id_users_id_fk", + "tableFrom": "posts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + } + }, + "indexes": { + "users_provider_id_unique": { + "name": "users_provider_id_unique", + "columns": [ + "provider_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/app/db/migrations/meta/_journal.json b/app/libs/drizzle/migrations/meta/_journal.json similarity index 67% rename from app/db/migrations/meta/_journal.json rename to app/libs/drizzle/migrations/meta/_journal.json index a2b92af..1b5b00f 100644 --- a/app/db/migrations/meta/_journal.json +++ b/app/libs/drizzle/migrations/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "6", - "when": 1724486082546, - "tag": "0000_fast_the_twelve", + "when": 1724496526361, + "tag": "0000_sharp_piledriver", "breakpoints": true } ] diff --git a/app/libs/drizzle/schema.ts b/app/libs/drizzle/schema.ts new file mode 100644 index 0000000..82e4eda --- /dev/null +++ b/app/libs/drizzle/schema.ts @@ -0,0 +1,22 @@ +import { sql } from "drizzle-orm"; +import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; + +export const users = sqliteTable("users", { + id: integer("id").primaryKey(), + provider: text("provider").notNull(), + providerId: text("provider_id").notNull().unique(), + name: text("name").notNull(), + icon: text("icon"), + createdAt: text("created_at") + .notNull() + .default(sql`(current_timestamp)`), +}); + +export const posts = sqliteTable("posts", { + id: integer("id").primaryKey(), + body: text("body"), + userId: integer("user_id").references(() => users.id), + createdAt: text("created_at") + .notNull() + .default(sql`(current_timestamp)`), +}); diff --git a/app/routes/resources.tsx b/app/routes/resources.tsx deleted file mode 100644 index b65ad79..0000000 --- a/app/routes/resources.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import type { LoaderFunctionArgs, ActionFunctionArgs } from "@remix-run/node"; -import { Form, json, useLoaderData } from "@remix-run/react"; -import { drizzle } from "drizzle-orm/d1"; -import { resources } from "~/db/schema.server"; - -export async function action({ request, context }: ActionFunctionArgs) { - const env = context.cloudflare.env as Env; - const formData = await request.formData(); - const title = formData.get("title") as string; - const href = formData.get("href") as string; - const db = drizzle(env.DB); - - await db.insert(resources).values({ title, href }).execute(); - - return json({ message: "Resource added" }, { status: 201 }); -} - -export async function loader({ context }: LoaderFunctionArgs) { - const env = context.cloudflare.env as Env; - const db = drizzle(env.DB); - const resourceList = await db - .select({ - id: resources.id, - title: resources.title, - href: resources.href, - }) - .from(resources) - .orderBy(resources.id); - - return json({ - resourceList, - }); -} - -export default function ResourcesPage() { - const { resourceList } = useLoaderData(); - return ( -
-

Welcome to Remix (with Drizzle, Vite and Cloudflare D1)

-
    - {resourceList.map((resource) => ( -
  1. - - {resource.title} - -
  2. - ))} -
-
-
- -
-
- -
- -
-
- ); -} diff --git a/bun.lockb b/bun.lockb index 4d74fc7abccb0edcceac54992f76a889a9f62f01..1c837213a499322e02e78ca50fb89e64a36b6ce1 100755 GIT binary patch delta 79283 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

$Ho$sV2Ww#stcF#v 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 Qeg3spAgR_ycaJ>(2NjWzKL7v# diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..c71e02b --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,8 @@ +import type { Config } from "drizzle-kit"; + +export default { + schema: "./app/libs/drizzle/schema.ts", + out: "./app/libs/drizzle/migrations", + driver: "d1-http", + dialect: "sqlite", +} satisfies Config; diff --git a/package.json b/package.json index 4fb4e29..869c91a 100644 --- a/package.json +++ b/package.json @@ -5,15 +5,17 @@ "type": "module", "scripts": { "build": "remix vite:build", - "deploy": "bun run build && wrangler pages deploy", "dev": "remix vite:dev", - "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .", "start": "wrangler pages dev ./build/client", - "typecheck": "tsc", - "typegen": "wrangler types", - "preview": "bun run build && wrangler pages dev", - "cf-typegen": "wrangler types", - "db:update": "drizzle-kit generate --out ./app/db/migrations --schema ./app/db/schema.server.ts --dialect sqlite" + "lint:eslint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .", + "lint:typecheck": "tsc --noEmit", + "cloudflare:deploy": "bun run build && wrangler pages deploy", + "cloudflare:type:generate": "wrangler types", + "db:sql:generate": "rm -rf ./app/libs/drizzle/migrations && drizzle-kit generate", + "db:migrate:local": "wrangler d1 migrations apply dartsroutine-db --local", + "db:migrate:remote": "wrangler d1 migrations apply dartsroutine-db --remote", + "db:clear:local": "wrangler d1 execute dartsroutine-db --local --file=./app/libs/drizzle/clear.sql", + "db:clear:remote": "wrangler d1 execute dartsroutine-db --remote --file=./app/libs/drizzle/clear.sql" }, "dependencies": { "@remix-run/cloudflare": "^2.11.1", @@ -44,9 +46,9 @@ "typescript": "^5.1.6", "vite": "^5.1.0", "vite-tsconfig-paths": "^4.2.1", - "wrangler": "3.57.1" + "wrangler": "^3.57.1" }, "engines": { "node": ">=20.0.0" } -} \ No newline at end of file +} diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 4112818..a572d3f 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,6 +1,10 @@ -// Generated by Wrangler on Sat Aug 24 2024 16:59:23 GMT+0900 (日本標準時) +// Generated by Wrangler on Sat Aug 24 2024 17:51:19 GMT+0900 (日本標準時) // by running `wrangler types` interface Env { + AUTH_SECRET: string; + GOOGLE_CALLBACK_BASE_URL: string; + GOOGLE_CLIENT_ID: string; + GOOGLE_CLIENT_SECRET: string; DB: D1Database; } diff --git a/wrangler.toml b/wrangler.toml index c7c19dc..0a39786 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -7,7 +7,7 @@ pages_build_output_dir = "./build/client" binding = "DB" # i.e. available in your Worker on env.DB database_name = "dartsroutine-db" database_id = "3f34ce78-842f-47b5-98b6-cd30edae58ae" -migrations_dir = "./app/db/migrations" +migrations_dir = "./app/libs/drizzle/migrations" # Automatically place your workloads in an optimal location to minimize latency. # If you are running back-end logic in a Pages Function, running it closer to your back-end infrastructure From 4a04031783664c39a36b8e8ac533e368200073d0 Mon Sep 17 00:00:00 2001 From: RuiOkazaki Date: Sun, 25 Aug 2024 01:50:43 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20OAuth=E3=81=A7google=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E3=82=A4=E3=83=B3=E3=81=8C=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dev.vars.template | 4 ++ .gitignore | 2 +- app/routes/_index.tsx | 66 +++++++++++++++---------- app/routes/auth.google.callback.tsx | 10 ++++ app/routes/auth.google.tsx | 7 +++ app/routes/logout.tsx | 7 +++ app/services/auth.server.ts | 76 +++++++++++++++++++++++++++++ package.json | 5 +- 8 files changed, 150 insertions(+), 27 deletions(-) create mode 100644 .dev.vars.template create mode 100644 app/routes/auth.google.callback.tsx create mode 100644 app/routes/auth.google.tsx create mode 100644 app/routes/logout.tsx create mode 100644 app/services/auth.server.ts diff --git a/.dev.vars.template b/.dev.vars.template new file mode 100644 index 0000000..7216b6f --- /dev/null +++ b/.dev.vars.template @@ -0,0 +1,4 @@ +AUTH_SECRET = "" +GOOGLE_CALLBACK_BASE_URL = "http://localhost:5173" +GOOGLE_CLIENT_ID = "" +GOOGLE_CLIENT_SECRET = "" diff --git a/.gitignore b/.gitignore index 4643549..e93331c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,6 @@ node_modules /.cache /build .env -.dev.vars .wrangler +.dev.vars diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 5c68e88..b5470f9 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,41 +1,57 @@ -import type { MetaFunction } from "@remix-run/cloudflare"; +import type { MetaFunction, LoaderFunctionArgs } from "@remix-run/cloudflare"; +import { json } from "@remix-run/cloudflare"; +import { Form, useLoaderData } from "@remix-run/react"; +import { desc, eq } from "drizzle-orm"; +import { getDBClient } from "~/libs/drizzle/client.server"; +import { posts } from "~/libs/drizzle/schema"; +import { getAuthenticator } from "~/services/auth.server"; export const meta: MetaFunction = () => { return [ { title: "New Remix App" }, { name: "description", - content: "Welcome to Remix on Cloudflare!", + content: "Welcome to Remix! Using Vite and Cloudflare!", }, ]; }; +export const loader = async ({ context, request }: LoaderFunctionArgs) => { + const authenticator = getAuthenticator(context); + const user = await authenticator.isAuthenticated(request); + if (user) { + const db = getDBClient(context.cloudflare.env.DB); + const userPosts = await db + .select() + .from(posts) + .where(eq(posts.userId, user.id)) + .orderBy(desc(posts.id)); + return json({ user, posts: userPosts }); + } + return json({ user }); +}; + export default function Index() { + const data = useLoaderData(); return ( -
-

Welcome to Remix on Cloudflare

-
+ Login with Google + + + + {data.user ? ( +

ログインしています

+ ) : ( +

ログインしていません

+ )} +
); } diff --git a/app/routes/auth.google.callback.tsx b/app/routes/auth.google.callback.tsx new file mode 100644 index 0000000..f5a78d6 --- /dev/null +++ b/app/routes/auth.google.callback.tsx @@ -0,0 +1,10 @@ +import type { LoaderFunctionArgs } from "@remix-run/cloudflare"; +import { getAuthenticator } from "~/services/auth.server"; + +export const loader = ({ context, request }: LoaderFunctionArgs) => { + const authenticator = getAuthenticator(context); + return authenticator.authenticate("google", request, { + successRedirect: "/", + failureRedirect: "/", + }); +}; diff --git a/app/routes/auth.google.tsx b/app/routes/auth.google.tsx new file mode 100644 index 0000000..119f268 --- /dev/null +++ b/app/routes/auth.google.tsx @@ -0,0 +1,7 @@ +import type { ActionFunctionArgs } from "@remix-run/cloudflare"; +import { getAuthenticator } from "~/services/auth.server"; + +export const action = ({ context, request }: ActionFunctionArgs) => { + const authenticator = getAuthenticator(context); + return authenticator.authenticate("google", request); +}; diff --git a/app/routes/logout.tsx b/app/routes/logout.tsx new file mode 100644 index 0000000..067c4cb --- /dev/null +++ b/app/routes/logout.tsx @@ -0,0 +1,7 @@ +import type { ActionFunctionArgs } from "@remix-run/cloudflare"; +import { getAuthenticator } from "~/services/auth.server"; + +export const action = async ({ context, request }: ActionFunctionArgs) => { + const authenticator = getAuthenticator(context); + await authenticator.logout(request, { redirectTo: "/" }); +}; diff --git a/app/services/auth.server.ts b/app/services/auth.server.ts new file mode 100644 index 0000000..70b9eff --- /dev/null +++ b/app/services/auth.server.ts @@ -0,0 +1,76 @@ +import { Authenticator } from "remix-auth"; +import type { AppLoadContext } from "@remix-run/cloudflare"; + +import { GoogleStrategy } from "remix-auth-google"; +import { eq } from "drizzle-orm"; +import { users } from "~/libs/drizzle/schema"; +import { getDBClient } from "~/libs/drizzle/client.server"; + +export type User = { + name: string; + id: number; +}; + +let createCookieSessionStorage: typeof import("@remix-run/cloudflare").createCookieSessionStorage; + +if (import.meta.env.DEV) { + import("@remix-run/node").then((module) => { + createCookieSessionStorage = module.createCookieSessionStorage; + }); +} else { + import("@remix-run/cloudflare").then((module) => { + createCookieSessionStorage = module.createCookieSessionStorage; + }); +} + +let _authenticatedUser: Authenticator | null = null; + +export function getAuthenticator(context: AppLoadContext) { + if (_authenticatedUser === null) { + if (!createCookieSessionStorage) { + throw new Error("createCookieSessionStorage is not initialized"); + } + const sessionStorage = createCookieSessionStorage({ + cookie: { + name: "_session", + sameSite: "lax", + path: "/", + httpOnly: true, + secrets: [context.cloudflare.env.AUTH_SECRET], + secure: import.meta.env.PROD, + }, + }); + _authenticatedUser = new Authenticator(sessionStorage); + const googleStrategy = new GoogleStrategy( + { + clientID: context.cloudflare.env.GOOGLE_CLIENT_ID, + clientSecret: context.cloudflare.env.GOOGLE_CLIENT_SECRET, + callbackURL: `${context.cloudflare.env.GOOGLE_CALLBACK_BASE_URL}/auth/google/callback`, + }, + async ({ profile }) => { + const db = getDBClient(context.cloudflare.env.DB); + const exitsUser = await db + .select() + .from(users) + .where(eq(users.providerId, profile.id)) + .limit(1); + if (exitsUser.length === 0) { + const createUser = await db + .insert(users) + .values({ + provider: profile.provider, + providerId: profile.id, + name: profile.displayName, + icon: profile.photos[0].value, + }) + .returning() + .get(); + return { id: createUser.id, name: createUser.name }; + } + return { id: exitsUser[0].id, name: exitsUser[0].name }; + } + ); + _authenticatedUser.use(googleStrategy); + } + return _authenticatedUser; +} diff --git a/package.json b/package.json index 869c91a..8798aa9 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,10 @@ "drizzle-orm": "^0.33.0", "isbot": "^4.1.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "remix-auth": "^3.7.0", + "remix-auth-form": "^1.5.0", + "remix-auth-google": "^2.0.0" }, "devDependencies": { "@cloudflare/workers-types": "^4.20240821.1", From a81664a3a19614ceceec422d35b0a69335355ada Mon Sep 17 00:00:00 2001 From: RuiOkazaki Date: Sun, 25 Aug 2024 01:51:43 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20Post=E3=82=92=E4=BD=9C=E6=88=90?= =?UTF-8?q?=E3=83=BB=E5=89=8A=E9=99=A4=E8=A1=8C=E3=81=88=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/_index.tsx | 111 +++++++++++++++++++++++++++++++++--- app/routes/posts.create.tsx | 22 +++++++ app/routes/posts.delete.tsx | 25 ++++++++ 3 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 app/routes/posts.create.tsx create mode 100644 app/routes/posts.delete.tsx diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index b5470f9..71f0f23 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,7 +1,8 @@ import type { MetaFunction, LoaderFunctionArgs } from "@remix-run/cloudflare"; import { json } from "@remix-run/cloudflare"; -import { Form, useLoaderData } from "@remix-run/react"; +import { Form, useLoaderData, useNavigation } from "@remix-run/react"; import { desc, eq } from "drizzle-orm"; +import { useRef, useEffect } from "react"; import { getDBClient } from "~/libs/drizzle/client.server"; import { posts } from "~/libs/drizzle/schema"; import { getAuthenticator } from "~/services/auth.server"; @@ -33,6 +34,108 @@ export const loader = async ({ context, request }: LoaderFunctionArgs) => { export default function Index() { const data = useLoaderData(); + + const navigation = useNavigation(); + const isAdding = navigation.state === "submitting"; + const formRef = useRef(null); + const inputRef = useRef(null); + + useEffect(() => { + if (!isAdding) { + formRef.current?.reset(); + inputRef.current?.focus(); + } + }, [isAdding]); + + if (data.user) { + return ( +
+
+
Hi, {data.user.name}
+
+ +
+
+
+
+ +
+ +
+
+ + + + + + + + + + + {data.posts.map((post) => ( + + + + + + + ))} + +
+ ID + + Body + + Created At + + Edit +
+ {post.id} + + {post.body} + + {post.createdAt} + +
+ + +
+
+
+
+ ); + } return (

Home

@@ -45,12 +148,6 @@ export default function Index() { Login with Google - - {data.user ? ( -

ログインしています

- ) : ( -

ログインしていません

- )}
); diff --git a/app/routes/posts.create.tsx b/app/routes/posts.create.tsx new file mode 100644 index 0000000..b90bf3d --- /dev/null +++ b/app/routes/posts.create.tsx @@ -0,0 +1,22 @@ +import { type ActionFunctionArgs, redirect } from "@remix-run/cloudflare"; +import { getDBClient } from "~/libs/drizzle/client.server"; +import { posts } from "~/libs/drizzle/schema"; +import { getAuthenticator } from "~/services/auth.server"; + +export const action = async ({ context, request }: ActionFunctionArgs) => { + const authenticator = getAuthenticator(context); + const user = await authenticator.isAuthenticated(request); + if (user) { + const db = getDBClient(context.cloudflare.env.DB); + const formData = await request.formData(); + const postBody = formData.get("post-body")?.toString(); + // validation + if (postBody === undefined || postBody.length === 0) { + return new Response("Post body is empty", { status: 500 }); + } + await db + .insert(posts) + .values({ body: postBody?.toString(), userId: user.id }); + } + return redirect("/"); +}; diff --git a/app/routes/posts.delete.tsx b/app/routes/posts.delete.tsx new file mode 100644 index 0000000..db1355b --- /dev/null +++ b/app/routes/posts.delete.tsx @@ -0,0 +1,25 @@ +import { type ActionFunctionArgs, redirect } from "@remix-run/cloudflare"; +import { and, eq } from "drizzle-orm"; +import { getDBClient } from "~/libs/drizzle/client.server"; +import { posts } from "~/libs/drizzle/schema"; +import { getAuthenticator } from "~/services/auth.server"; + +export const action = async ({ context, request }: ActionFunctionArgs) => { + const authenticator = getAuthenticator(context); + const user = await authenticator.isAuthenticated(request); + if (user) { + const db = getDBClient(context.cloudflare.env.DB); + const formData = await request.formData(); + const postId = formData.get("post-id")?.toString(); + // validation + if (postId === undefined || Number.isNaN(Number.parseInt(postId))) { + return new Response("Post ID is invalid", { status: 500 }); + } + await db + .delete(posts) + .where( + and(eq(posts.id, Number.parseInt(postId)), eq(posts.userId, user.id)) + ); + } + return redirect("/"); +}; From af0914ca985418fa397900e890d10577ab35de3e Mon Sep 17 00:00:00 2001 From: RuiOkazaki Date: Sun, 25 Aug 2024 01:52:03 +0900 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20html=E3=81=AElang=E3=82=92ja?= =?UTF-8?q?=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/root.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/root.tsx b/app/root.tsx index 3d3d733..0c0d3eb 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -9,7 +9,7 @@ import "./tailwind.css"; export function Layout({ children }: { children: React.ReactNode }) { return ( - +