কম্পিউটার টিউটোরিয়াল

Lucia, PlanetScale এবং Upstash Redis দিয়ে SvelteKit-এ নিরাপদ, টাইপ-সেফ অথেন্টিকেশন

আমাদের শেষ গাইড Upstash ব্লগে Bytes নিউজলেটার-এ জায়গা পাওয়ার পর, মনে হয় SvelteKit পার্টি চালিয়ে যেতে হবে।

একজন Svelte সুপার ফ্যান হিসেবে, আমি প্রতিদিন 점점 বেশি মানুষকে বোর্ডে ঢুকতে দেখছি — এবং এটি ভবিষ্যতের জন্য আমাকে অত্যন্ত উদ্বুদ্ধ করে দিচ্ছে।

এই工具গুলোর মধ্যে একটি যা এখনও রাডারের নিচে আছেন তা হলো Lucia।

এই গাইডে আমি আপনাকে দেখাবো Lucia দিয়ে অথেন্টিকেশন কীভাবে সেট আপ করতে হয়। আমরা ডেটাবেসের জন্য PlanetScale এবং সেশন হ্যান্ডল করার জন্য Upstash Redis ব্যবহার করব।

নিচে এই গাইডের চূড়ান্ত লক্ষ্যের একটি স্ক্রিনশট। আপনি উদাহরণ রিপোজিটরি এখানে পাবেন।

Lucia, PlanetScale এবং Upstash Redis দিয়ে SvelteKit-এ নিরাপদ, টাইপ-সেফ অথেন্টিকেশন

এই গাইড SvelteKit-এ হবে, কিন্তু যেহেতু Lucia যেকোনো ফ্রেমওয়ার্ক সাপোর্ট করে, এই গাইডের অধিকাংশ অংশ যেকোনো জনপ্রিয় ফ্রেমওয়ার্কে সহজে প্রয়োগ করা যাবে।

Lucia কী?

সহজ ভাষায়, Lucia হলো একটি TypeScript লাইব্রেরি যা ইউজার এবং সেশন হ্যান্ডলিংকে খুব সহজ করে দেয়। আসলেই, এই লাইব্রেরিটি SvelteKit-র জন্য তৈরি হয়েছিল, কিন্তু এটি অবিরত উন্নত হয়েছে এবং এখন প্রায় সব জনপ্রিয় ফ্রেমওয়ার্কের সাথে ভালো কাজ করে।

Lucia যে কতটুকু অসাধারণ সেটা হলো এটি আপনাকে অথেন্টিকেশনের জটিলতা ম্যানেজ করতে যা দরকার সব দিয়ে সাজিয়ে দেয়, ইউজার এক্সপেরিয়েন্স বিনা কমপ্রোমাইজ করেই। Lucia কে প্রিমিটিভের একটি সেট হিসেবে চিন্তা করুন — আপনি কীভাবে আপনার কোড স্ট্রাকচার করবেন এবং ইউজার এক্সপেরিয়েন্স হ্যান্ডেল করবেন সেটা আপনার উপর। Lucia-এর কিছু কী পার্ট আছে যা বোঝা গুরুত্বপূর্ণ:

মিডলওয়্যার Lucia কে আলাদা ফ্রেমওয়ার্ক এবং রানটাইমের জন্য রিকোয়েস্ট এবং রেসপন্স পড়তে দেয়।

মিডলওয়্যার কনফিগার করার একটি উদাহরণ:

import { lucia } from "lucia";
import { node } from "lucia/middleware";
// import { nextjs } from "lucia/middleware";
// import { h3 } from "lucia/middleware";
export const auth = lucia({
  env: "DEV", // "PROD" যদি HTTPS-এ ডিপ্লয় করা হয়
  middleware: node(),
});

ডেটাবেস অ্যাডাপ্টার Lucia কে ইউজার এবং সেশন সংরক্ষণ ও রিট্রিভ করতে দেয়। একটি অ্যাডাপ্টার প্রদান করে Lucia জানতে পারে এই টাইপগুলো কীভাবে কোয়ারী করতে হয়। ২ টাইপের অ্যাডাপ্টার আছে; রেগুলার অ্যাডাপ্টার, এবং সেশন অ্যাডাপ্টার। এই নির্দিষ্ট গাইডে আমরা PlanetScale-এ হোস্ট করা একটি MySQL ডেটাবেস ব্যবহার করব ইউজার সংরক্ষণ করার জন্য, এবং Upstash-এ হোস্ট করা একটি Redis ইনস্ট্যান্স সেশন হ্যান্ডল করার জন্য।

ডেটাবেস অ্যাডাপ্টার কনফিগার করার একটি উদাহরণ:

import { prisma } from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
import { lucia } from "lucia";
const client = new PrismaClient();
const auth = lucia({
  env: "DEV",
  adapter: prisma(client),
});

এই ব্যাকগ্রাউন্ড ইনফোrmেশন দিয়ে, চলুন সরাসরি শুরু করি।

পূর্বশর্ত

অ্যাপটি চালু করতে এবং ফলো করতে আপনাকে দরকার:

  • SvelteKit-এর মৌলিক বোঝাপড়া। ফর্ম এবং রাউটিং হ্যান্ডলিং প্লাস পয়েন্ট।
  • Drizzle ORM-এর বেসিক পরিচয়।
  • PlanetScale-এ একটি অ্যাকাউন্ট এবং ডেটাবেস।
  • একটি Redis ইনস্ট্যান্সে অ্যাক্সেস, উদাহরণস্বরূপ Upstash Redis।

শুরু করা যাক

দক্ষতার জন্য, আমরা পুরো অ্যাপ্লিকেশন স্ক্র্যাচ থেকে তৈরি করব না।

বদলে, আপনি Upstash উদাহরণ রিপো থেকে sveltekit-lucia-redis ডিরেক্টরি ক্লোন করে ফলো করতে পারেন।

রিপোজিটরি ডাউনলোড করার পর, cd কমান্ড ব্যবহার করে অ্যাপ্লিকেশনের মধ্যে নেভিগেট করুন এবং আপনার পছন্দের প্যাকেজ ম্যানেজার দিয়ে ডিপেন্ডেন্সি ইনস্টল করুন এবং .env.example ডুপ্লিকেট করে .env ভেরিয়েবল সেট করুন।

কী অংশগুলো বোঝা যাক

সর্বমূল্যবান অংশগুলোর একটি কুইক রানডাউন:

  • src/lib/server/auth/index.ts - এখানেই আমরা Lucia কনফিগার করি।
  • src/lib/server/drizzle - Drizzle আমাদের সহজে একটি MySQL স্কিমা তৈরি করতে সাহায্য করে যা আমরা Drizzle Kit ব্যবহার করে PlanetScale-এ পUSH করতে পারি।
  • src/lib/server/planetscale - Upstash ক্লায়েন্ট এক্সপোর্ট করে যা আমরা আমাদের Lucia অ্যাডাপ্টার কনফিগে ইউজার ম্যানেজ করতে ব্যবহার করি।
  • src/lib/server/upstash - Upstash ক্লায়েন্ট এক্সপোর্ট করে যা আমরা আমাদের Lucia অ্যাডাপ্টার কনফিগে সেশন ম্যানেজ করতে ব্যবহার করি।

কোড ব্রেক ডাউন

Lucia কনফিগার করা

প্রথমে যা করতে হবে তা হলো Lucia কনফিগার করা। আমরা src/lib/server/auth/index.ts-এ একটি নতুন ফাইল তৈরি করে এটি করতে পারি।

import { planetscale } from "@lucia-auth/adapter-mysql";
import { dev } from "$app/environment";
import { lucia } from "lucia";
import { sveltekit } from "lucia/middleware";
import { ps } from "../planetscale";
export enum PROVIDER_ID { EMAIL = "email", }
export const auth = lucia({
  adapter: {
    user: planetscale(ps, {
      user: "users",
      key: "keys",
      /** * সেশন Upstash Redis দ্বারা হ্যান্ডল করা হচ্ছে। */
      session: null,
    }),
  },
  middleware: sveltekit(),
  env: dev ? "DEV" : "PROD",
  getUserAttributes: (data) => {
    return { userId: data.id, email: data.email, };
  },
});
export type Auth = typeof auth;

এখানে কী হচ্ছে তার একটি কুইক রানডাউন:

আমরা lucia ফাংশনটি lucia প্যাকেজ থেকে ইমপোর্ট করি Lucia-এর কনফিগারেশন সেট আপ করার জন্য।

প্রথমে আমরা adapter প্রপার্টি কনফিগার করি। এটিই যেখানে আমরা Lucia-কে বলি কীভাবে ইউজার এবং সেশন হ্যান্ডল করতে হয়।

আমরা সেশন প্রপার্টিকে null সেট করি কারণ আমরা সেশন হ্যান্ডল করার জন্য Redis ব্যবহার করতে চাই। আপনি যদি এখানে স্ট্রিং 'session' ব্যবহার করেন, তবে Lucia একই অ্যাডাপ্টার ব্যবহার করবে ইউজার এবং সেশন দুটির জন্য (এই স্ট্রিংগুলো আপনার ডেটাবেসের টেবিলের সাথে মেলে)।

এখন সেশন অ্যাডাপ্টারের চিন্তা ন করুন, আমরা পরে সেটা করব।

middleware প্রপার্টিতে আমরা Lucia-কে জানাতে পারি যে আমরা SvelteKit ব্যবহার করছি। এটি Lucia কে রিকোয়েস্ট এবং রেসপন্স অবজেক্ট পড়তে দেবে।

এক্সপোর্ট করা Auth টাইপটি নোট করুন। এটি আমাদের auth অবজেক্টের টাইপ। সেটআপ SvelteKit locals-এর জন্য এটি দরকার হবে।

Lucia দিয়ে দুর্দান্ত টাইপ ইনফারেন্স পাওয়া

Lucia TypeScript-এ লেখা, তাই আপনি বক্সের বাইরে দুর্দান্ত টাইপ ইনফারেন্স পাওয়া যাবেন। নিশ্চিত করি SvelteKit Auth টাইপ সম্পর্কে জানে যা আমরাhenyl তৈরি করেছি।

আপনার app.d.ts ফাইল খুলুন এবং নিচের কোডটি যোগ করুন:

import type { Auth as LuciaAuth } from "$lib/server/auth";
import type { AuthRequest, Session, User } from "lucia";
declare global {
  namespace App {
    // interface Error {}
    interface Locals {
      auth: AuthRequest;
      session: Session | null;
    }
    interface PageData { user?: User; }
    // interface Platform {}
  }
}
/// <reference types="lucia" />
declare global {
  namespace Lucia {
    type Auth = LuciaAuth;
    type DatabaseUserAttributes = { email: string; };
    type DatabaseSessionAttributes = {};
  }
}
export {};

Auth টাইপকে Lucia নেমস্পেসে যোগ করে, আমরা এখন locals অবজেক্ট থেকে auth অবজেক্ট অ্যাক্সেস করতে পারি আমাদের SvelteKit রুটগুলিতে।

কিন্তু Lucia থেকে ইমপোর্ট করা যেকোনো কিছুর টাইপও এখন সঠিক হবে।

এই টাইপগুলো থাকায়, আমরা hooks.server.ts সেট আপ করতে পারি। এটিই যেখানে আমরা AuthRequest এবং Session অবজেক্ট বাইন্ড করব বর্তমান রিকোয়েস্টের সাথে। এটি সার্ভারে locals এর মাধ্যমে সহজে অ্যাক্সেসযোগ্য করবে।

import type { Handle } from "@sveltejs/kit";
import { sequence } from "@sveltejs/kit/hooks";
import { auth } from "$lib/server";
const auth_handle: Handle = async ({ event, resolve }) => {
  event.locals.auth = auth.handleRequest(event);
  event.locals.session = await event.locals.auth.validate();
  return resolve(event);
};
export const handle = sequence(auth_handle);

আমরা sequence ইমপোর্ট করছি যা একটি হেল্পার ফাংশন যা একাধিক হুক সিকোয়েনশিয়ালি চালাতে দেয়। এটি পরে রুট প্রোটেক্ট করার সময় কাজে আসবে।

ইউজার মডেল তৈরি করা

Lucia কনফিগার হয়ে গেলে, আমরা আমাদের ইউজার মডেল তৈরি করতে পারি।

আমরা Drizzle ORM ব্যবহার করব, যেটি এখন খুব হট এবং হাপেনিং।

Lucia, PlanetScale এবং Upstash Redis দিয়ে SvelteKit-এ নিরাপদ, টাইপ-সেফ অথেন্টিকেশন

এবং তাদের মিমগুলো পয়েন্টে। শুধু এইটা দেখুন।

আপনি চালিয়ে যাওয়ার আগে, আপনাকে PlanetScale-এ একটি ডেটাবেস তৈরি করতে হবে। এবং Drizzle কনফিগ ফাইল সেট আপ করতে হবে। এটি Drizzle CLI কে আপনার ডেটাবেসের সাথে সংযোগ করতে সাহায্য করবে।

import dotenv from "dotenv";
import type { Config } from "drizzle-kit";
dotenv.process();
const username = process.env.DATABASE_USERNAME;
const password = process.env.DATABASE_PASSWORD;
const host = process.env.DATABASE_HOST;
const db = process.env.DATABASE_NAME;
const connectionString = `mysql://${username}:${password}@${host}/${db}?ssl={"rejectUnauthorized":true}`;
export default { schema: "./src/lib/server/drizzle/schema/index.ts", driver: "mysql2", dbCredentials: { connectionString: connectionString, }, } satisfies Config;

যেহেতু আমরা MySQL অ্যাডাপ্টার ব্যবহার করছি, Lucia আমাদের ইউজার মডেলের একটি নির্দিষ্ট স্ট্রাকচার আশা করে। এর সম্পর্কে আরও তথ্য ডক্সে পাবেন।

নিচের কোডটি src/lib/server/drizzle/schema/index.ts-এ রাখুন।

import { relations } from "drizzle-orm";
import { bigint, datetime, index, int, mysqlEnum, mysqlTable, timestamp, unique, varchar, } from "drizzle-orm/mysql-core";
export const users = mysqlTable( "users", { id: varchar("id", { length: 255 }).primaryKey(), createdAt: timestamp("createdAt").defaultNow().onUpdateNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), email: varchar("email", { length: 191 }).notNull(), }, (table) => { return { idIdx: index("users_id_idx").on(table.id), userIdKey: unique("users_id_key").on(table.id), }; }, );
export const keys = mysqlTable( "keys", { id: varchar("id", { length: 255 }).primaryKey(), hashedPassword: varchar("hashed_password", { length: 255 }), userId: varchar("user_id", { length: 255 }).notNull(), }, (table) => { return { userIdIdx: index("keys_user_id_idx").on(table.userId), keyIdKey: unique("keys_id_key").on(table.id), }; }, );

এবং pnpm drizzle-kit push:mysql চালান স্কিমা PlanetScale-এ পুশ করতে।

Voila! এখন আপনার पास একটি ইউজার মডেল আছে যা Lucia ব্যবহার করতে পারে ইউজার ম্যানেজ করার জন্য।

সেশন ম্যানেজমেন্ট সেট আপ করা

ইউজার মডেল থাকায়, আমরা সেশন ম্যানেজমেন্ট সেট আপ করতে পারি।

আমরা Upstash Redis ব্যবহার করব সেশন হ্যান্ডল করার জন্য। আপনি এখানে ফ্রি অ্যাকাউন্ট খুলতে পারেন।

Lucia, PlanetScale এবং Upstash Redis দিয়ে SvelteKit-এ নিরাপদ, টাইপ-সেফ অথেন্টিকেশন

ড্যাশবোর্ডে প্রবেশ করার পর, আপনাকে শুধু একটি নতুন ডেটাবেস তৈরি করতে হবে এবং এনভায়রনমেন্ট ভেরিয়েবল কপি করতে হবে।

Lucia, PlanetScale এবং Upstash Redis দিয়ে SvelteKit-এ নিরাপদ, টাইপ-সেফ অথেন্টিকেশন

এখন এই ভেরিয়েবলগুলো আপনার .env ফাইলে যোগ করুন। এবং নিচের কোডটি src/lib/server/upstash/index.ts-এ যোগ করুন।

import { Redis } from "@upstash/redis";
import { UPSTASH_REDIS_REST_TOKEN, UPSTASH_REDIS_REST_URL, } from "$env/static/private";
export const upstashClient = new Redis({ url: UPSTASH_REDIS_REST_URL, token: UPSTASH_REDIS_REST_TOKEN, });

এখন মনে রাখুন যখন আমরা Lucia কনফিগার করলাম, আমরা session-কে null সেট করেছিলাম। কারণ আমরা সেশন হ্যান্ডল করার জন্য Redis ব্যবহার করতে চাই। এখন আমাদের কনফিগ এর выглядит এইরকম:

import { planetscale } from "@lucia-auth/adapter-mysql";
import { upstash } from "@lucia-auth/adapter-session-redis";
import { dev } from "$app/environment";
import { lucia } from "lucia";
import { sveltekit } from "lucia/middleware";
import { ps } from "../planetscale";
import { upstashClient } from "../upstash";
export enum PROVIDER_ID { EMAIL = "email", }
export const auth = lucia({
  adapter: {
    user: planetscale(ps, { user: "users", key: "keys", session: null, }),
    // Lucia কে Upstash Redis ব্যবহার করতে নির্দেশ করুন সেশন জন্য
    session: upstash(upstashClient),
  },
  middleware: sveltekit(),
  env: dev ? "DEV" : "PROD",
  getUserAttributes: (data) => { return { userId: data.id, email: data.email, }; },
});
export type Auth = typeof auth;

আমাদের সুবিধার জন্য, Lucia-র বক্সের বাইরে Upstash Redis অ্যাডাপ্টার আছে। তাই আমাদের শুধু ইমপোর্ট করতে হবে এবং Upstash ক্লায়েন্ট পাস করতে হবে।

ততটুকু সহজ!

রুট তৈরি করা

Lucia কনফিগার হয়ে গেলে, আমরা আমাদের রুট তৈরি করতে পারি।

src/routes-এ নিচের ফোল্ডারগুলো তৈরি করুন। ফাইলগুলোর চিন্তা করবেন না, আমরা সেগুলো একটু পরপর আলোচনা করব।

  • src/routes/auth
    • src/routes/auth/signin
    • src/routes/auth/signup
  • src/routes/app

টিপ: কিছু "ফিচার" একই ফোল্ডার নাম বা গ্রুপের অধীনে রাখলে রিডাইরেক্ট এবং রুট প্রোটেক্ট করার সময় ম্যানেজ করা সহজ হয়।

সাইন-ইন পেজ তৈরি করা

অবশেষে, আমরা কিছু ফ্রন্ট-এন্ড কাজ করতে পারি! সাইন-ইন পেজ से শুরু করি।

src/routes/auth/signin/+page.svelte-এ একটি নতুন ফাইল তৈরি করুন। আমি স্টাইলিং বাদ দিচ্ছি এখন, এবং ফাংশনালিটিতে ফোকাস করছি — কিন্তু আপনি উদাহরণ রিপোজিটরিতে ফুল কোড পাবেন।

<script lang="ts">
import { enhance } from '$app/forms';
import { Button, Input, Label, PasswordInput } from '$lib/components/common';
import type { ActionData } from './$types';
export let form: ActionData;
let loading = false;
let email = '';
let password = '';
</script>
<form method="POST" use:enhance={() => { loading = true; return async ({ update }) => { loading = false; update(); }; }} >
  {#if form && form.error}
    <div class="p-2 mb-4 text-sm text-center text-red-900 bg-red-200 rounded-sm"> Error: {form.error} </div>
  {/if}
  <div class="grid gap-2.5">
    <div class="grid gap-1">
      <Label for="email">Email</Label>
      <Input bind:value={email} name="email" placeholder="Email" type="email" autoCapitalize="none" autoComplete="email" autoCorrect="off" required={true} />
    </div>
    <div class="grid gap-1">
      <Label for="password">Password</Label>
      <PasswordInput name="password" placeholder="Password" bind:value={password} />
    </div>
    <div class="mt-6 col-span-full">
      <Button type="submit" class="w-full" disabled={loading}>
        {#if loading} Loading... {:else} Sign in {/if}
      </Button>
    </div>
  </div>
</form>

use:enhance অ্যাকশন ফর্মটি প্রোগ্রেসিভলি এনহ্যান্স করবে, এবং আমাদের ফর্ম সাবমিট হলে লোডিং স্টেট দেখাতে দেবে। এর সম্পর্কে এখানে আরো পড়ুন।

বাকি কোড খুব স্বয়ংসম্মত।

সাইন-ইন রিকোয়েস্ট হ্যান্ডল করা

SvelteKit POST রিকোয়েস্ট হ্যান্ডল করা অসাধারণভাবে সহজ করে দেয়। আমাদের শুধু +page.server.ts ফাইলটি +page.svelte ফাইলের মতো একই ডিরেক্টরিতে তৈরি করতে হবে এবং একটি actions অবজেক্ট এক্সপোর্ট করতে হবে কমপক্ষে একটি default প্রপার্টির সাথে।

import { fail, redirect } from "@sveltejs/kit";
import { auth, PROVIDER_ID } from "$lib/server";
import { LuciaError } from "lucia";
import type { Actions, PageServerLoad } from "./$types";
export const actions = { /* our actions here */ };

এই অংশের কী এলিমেন্টগুলো তা দেখি:

আমরা PROVIDER_ID enum এবং auth-কে src/lib/server/auth- থেকে ইমপোর্ট করেছি, যা আমরা আগে তৈরি করেছিলাম। এই auth অবজেক্টে ইউজার এবং সেশন ম্যানেজ করার সব মেথড আছে।

এখন actions অবজেক্টটি দেখি। আমরা রিকোয়েস্ট অবজেক্ট থেকে ফর্ম ডেটা পেতে পারি, এবং কিছু বেসিক হাউসকিপিং করতে পারি।

actions = { default: async ({ request, locals }) => {
  const formData = await request.formData();
  const email = formData.get("email") as string;
  const password = formData.get("password") as string;
  const fields = [email, password];
  if (fields.some(field => !field)) { return fail(400, { error: "All fields are required" }); }

এরপর, আমরা ইউজারকে সাইন-ইন করার চেষ্টা করব। যদি ইউজার না থাকে, বা পাসওয়ার্ড ভুল হয়, Lucia একটি এরর থ্রো করবে। আমরা এই ইভেন্টের জন্য প্রস্তুত করেছি এরর ক্যাচ করে এবং একটি ৪০০ রেসপন্স সহ এরর মেসেজ রিটার্ন করে।

try {
  const user = await auth.useKey(PROVIDER_ID.EMAIL, email.toLowerCase(), password);
  const session = await auth.createSession({ userId: user.userId, attributes: {} });
  locals.auth.setSession(session);
} catch (err) {
  if (err instanceof LuciaError && (err.message === 'AUTH_INVALID_KEY_ID' || err.message === 'AUTH_INVALID_PASSWORD')) {
    return fail(400, { error: 'Incorrect username of password' });
  }
  return fail(400, { error: 'An unknown error occurred' });
}

যদি ইউজার থাকে এবং পাসওয়ার্ড সঠিক হয়, আমরা একটি নতুন সেশন তৈরি করব।

এবং অবশেষে, ইউজারকে ড্যাশবোর্ডে রিডাইরেক্ট করব।

return redirect('/app');

আপনি সম্ভবত বুঝতে পারছেন, Lucia অথেন্টিকেশনের অনেক জটিলতা অ্যাবস্ট্রাক্ট করে দিয়েছে। আমাদের শুধু সঠিক মেথডগুলো কল করতে হয়, এবং Lucia বাকি গুলো হ্যান্ডেল করবে।

Lucia, PlanetScale এবং Upstash Redis দিয়ে SvelteKit-এ নিরাপদ, টাইপ-সেফ অথেন্টিকেশন

পাসওয়ার্ড হ্যাশ করার, সেশন তৈরি করার, বা কুকি ম্যানেজ করার দরকার নেই। Lucia আমাদের জন্য সব করছে। এবং এটি সব টাইপ-সেফ!

সাইন-আপ পেজ তৈরি করা

src/routes/auth/signup/+page.svelte-এ একটি নতুন ফাইল তৈরি করুন।

সাইন-আপ পেজ সাইন-ইন পেজের খুব সিমিলার, তাই এখানে ব্যাখ্যা করার কিছু বেশি নেই। একমাত্র পার্থক্য হলো আমরা পাসওয়ার্ড কনফারমেশন চাইছি।

<script lang="ts">
import { enhance } from '$app/forms';
import { Button, Input, Label, PasswordInput } from '$lib/components/common';
import type { ActionData } from './$types';
export let form: ActionData;
let loading = false;
let email = '';
let password = '';
let passwordConfirmation = '';
</script>
<form method="POST" use:enhance={() => { loading = true; return async ({ update }) => { loading = false; update(); }; }} >
  {#if form && form.error}
    <div class="p-2 mb-4 text-sm text-center text-red-900 bg-red-200 rounded-sm"> Error: {form.error} </div>
  {/if}
  <div class="grid gap-2.5">
    <div class="grid gap-1">
      <Label for="email">Email</Label>
      <Input bind:value={email} name="email" placeholder="Email" type="email" autoCapitalize="none" autoComplete="email" autoCorrect="off" required={true} />
    </div>
    <div class="grid gap-1">
      <Label for="password">Password</Label>
      <PasswordInput name="password" placeholder="Password" bind:value={password} />
    </div>
    <div class="grid gap-1">
      <Label for="passwordConfirmation">Confirm password</Label>
      <PasswordInput name="passwordConfirmation" placeholder="Repeat password" bind:value={passwordConfirmation} />
    </div>
    <div class="mt-6 col-span-full">
      <Button type="submit" class="w-full" disabled={loading}>
        {#if loading} Loading... {:else} Sign in {/if}
      </Button>
    </div>
  </div>
</form>

সাইন-আপ রিকোয়েস্ট হ্যান্ডল করা

src/routes/auth/signup/+page.server.ts-এ একটি নতুন ফাইল তৈরি করুন।

সাইন-আপের জন্য আমাদের সাইন-আপ ফর্মের মত একটি সিমিলার প্যাটার্ন ব্যবহার করতে হবে কিন্তু কিছু পার্থক্য থাকবে ফর্ম থেকে ডেটা রিট্রিভাল, ফিল্ড ভ্যালিডেশন থেকে শুরু করে ইউজার তৈরি করা এবং ইতিমধ্যে বিদ্যমান ইউজার হ্যান্ডল করার পর্যায় পর্যন্ত।

ইমপোর্টগুলো সাইন-ইন পেজের মতোই, তাই আমরা সেটা স্কিপ করব।

আমরা রিকোয়েস্ট অবজেক্ট থেকে ফর্ম ডেটা পাওয়ায় শুরু করব, এবং কিছু বেসিক হাউসকিপিং করব। আমরা চেক করব পাসওয়ার্ডগুলো মিলে কিনা।

const formData = await request.formData();
const email = formData.get('email') as string;
const password = formData.get('password') as string;
const passwordConfirmation = formData.get('passwordConfirmation') as string;
const fields = [email, password, passwordConfirmation];
if (fields.some((field) => !field)) { return fail(400, { error: 'All fields are required' }); }
if (password !== passwordConfirmation) { return fail(400, { error: 'Passwords do not match' }); }

এরপর, আমরা Drizzle ORM ব্যবহার করে ইমেইল দিয়ে ইউজার খুঁজব। যদি ইউজার থাকে, আমরা একটি ৪০০ রেসপন্স সহ এরর মেসেজ রিটার্ন করব।

try {
  const user = await db.query.users.findFirst({ where: eq(schema.users.email, email.toLowerCase()), });
  if (user) { return fail(400, { error: "User with this email already exists" }); }

যদি ইউজার না থাকে, আমরা Lucia ব্যবহার করে একটি নতুন ইউজার তৈরি করব।

const newUser = await auth.createUser({ key: { providerId: PROVIDER_ID.EMAIL, providerUserId: email.toLowerCase(), password: password, }, attributes: { email, }, });

এবং অবশেষে, একটি নতুন সেশন তৈরি করব এবং ইউজারকে ড্যাশবোর্ডে রিডাইরেক্ট করব।

const session = await auth.createSession({ userId: newUser.userId, attributes: {}, });
locals.auth.setSession(session);

Easy peasey lemon squeezy!

বোনাস: সাইন-আউট পেজ তৈরি করা

যেহেতু আমরা এই Arbeit করছি, একটি সাইন-আউট এন্ডপয়েন্টও তৈরি করি। src/routes/auth/signout/+server.ts-এ একটি নতুন ফাইল তৈরি করুন।

এবং নিচের কোডটি যোগ করুন:

import { auth } from "$lib/server";
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ locals }) => {
  const session = await locals.auth.validate();
  if (!session) { return new Response(null, { status: 400, }); }
  // সেশন ইনভ্যালিডেট করুন অথবা অল্টার্নেটিভলি, সব সেশন ডিলিট করুন:
  await auth.invalidateAllUserSessions(session.userId);
  await auth.invalidateSession(session.sessionId);
  // কুকি সরান।
  locals.auth.setSession(null);
  return new Response(null, { status: 200, });
};

আবার Lucia আমাদের পেছনে দাঁড়িয়ে আছে সেশন ইনভ্যালিডেট করা অসাধারণভাবে সহজ করে দিয়েছে। এখন আমাদের শুধু এই এন্ডপয়েন্টটি কল করতে হবে যখন ইউজার সাইন-আউট বাটনে ক্লিক করবে।

<script lang="ts">
import { goto } from '$app/navigation';
import { Button } from '$lib/components/common';
async function handleSignOut() {
  const response = await fetch('/auth/signout', { method: 'POST', headers: { 'Content-Type': 'application/json' } });
  if (response.ok) { goto('/auth/signin', { replaceState: true, invalidateAll: true }); }
}
</script>
<Button on:click={handleSignOut}>Sign out</Button>

এটা হলো র‍্যাপ

আমরা সফলভাবে Lucia, PlanetScale, এবং Upstash Redis দিয়ে টাইপ-সেফ অথেন্টিকেশন তৈরি করেছি। এবং আমরা Lucia যা পার তায় শুধু সতহ ছোঁয়া করেছি।

আবার, আপনি এই গাইডের রিপো এখানে পাবেন। Lucia সম্পর্কে আরও জানতে চাইলে, আমি ডক্স চেক আউট করার পরামর্শ দিচ্ছি।

আগে যাওয়ার আগে আপনাকে Upstash Discord কমিউনিটিতে আড্ডা দিতে হবে — আমরা ভালো টাইম কাটাচ্ছি। এবং আপনি আধিক SvelteKit কন্টেন্ট চাইলে, আমার ব্লগে এখানে পাবেন।

  1. HTML DOM Input Email defaultValue প্রোপার্টি

  2. জাভাস্ক্রিপ্টে স্ক্রিন রেজোলিউশন পাওয়া

  3. C++-এ একটি বাইনারি ট্রি লেভেল-ওয়াইজ সর্টেড কিনা তা যাচাই করা

  4. HTML DOM emphasized অবজেক্ট