feat: Add prisma

This commit is contained in:
yoosangwook 2024-08-07 18:04:07 +09:00
parent e12a923186
commit fcca20d14f
11 changed files with 209 additions and 94 deletions

View File

@ -2,4 +2,6 @@ NEXT_PUBLIC_TEST="테스트변수입니다. development"
NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080" NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080"
DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=sa;password=Qwertqaz!@#45;trustServerCertificate=true" DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;password=Qwertqaz12345;trustServerCertificate=true"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="

View File

@ -3,3 +3,5 @@ NEXT_PUBLIC_TEST="테스트변수입니다. production"
NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080" NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080"
DATABASE_URL="" DATABASE_URL=""
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="

View File

@ -15,6 +15,7 @@
"axios": "^1.7.3", "axios": "^1.7.3",
"fabric": "^5.3.0", "fabric": "^5.3.0",
"framer-motion": "^11.2.13", "framer-motion": "^11.2.13",
"iron-session": "^8.0.2",
"mathjs": "^13.0.2", "mathjs": "^13.0.2",
"mssql": "^11.0.1", "mssql": "^11.0.1",
"next": "14.2.3", "next": "14.2.3",

View File

@ -1,91 +1,9 @@
export default function page() { import Login from '@/components/auth/Login'
export default function LoginPage() {
return ( return (
<> <>
<div className="flex flex-col align-center h-screen"> <Login />
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
<img
alt="Your Company"
src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
className="mx-auto h-10 w-auto"
/>
<h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
Sign in to your account
</h2>
</div>
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form action="#" method="POST" className="space-y-6">
<div>
<label
htmlFor="email"
className="block text-sm font-medium leading-6 text-gray-900"
>
Email address
</label>
<div className="mt-2">
<input
id="email"
name="email"
type="email"
required
autoComplete="email"
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<div className="flex items-center justify-between">
<label
htmlFor="password"
className="block text-sm font-medium leading-6 text-gray-900"
>
Password
</label>
<div className="text-sm">
<a
href="#"
className="font-semibold text-indigo-600 hover:text-indigo-500"
>
Forgot password?
</a>
</div>
</div>
<div className="mt-2">
<input
id="password"
name="password"
type="password"
required
autoComplete="current-password"
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<button
type="submit"
className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Sign in
</button>
</div>
</form>
<p className="mt-10 text-center text-sm text-gray-500">
Not a member?{' '}
<a
href="#"
className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500"
>
Start a 14 day free trial
</a>
</p>
</div>
</div>
</div>
</> </>
) )
} }

View File

@ -1,11 +1,15 @@
import MainPage from '@/components/Main' import MainPage from '@/components/Main'
import { getSession } from '@/lib/authActions'
import { getCurrentLocale } from '@/locales/server' import { getCurrentLocale } from '@/locales/server'
export default function page() { export default async function page() {
const session = await getSession()
const currentLocale = getCurrentLocale() const currentLocale = getCurrentLocale()
const mainPageProps = { const mainPageProps = {
currentLocale, currentLocale,
isLoggedIn: session?.isLoggedIn,
} }
return ( return (

View File

@ -1,10 +1,11 @@
'use client' 'use client'
import { logout } from '@/lib/authActions'
import { useChangeLocale, useI18n } from '@/locales/client' import { useChangeLocale, useI18n } from '@/locales/client'
import { Button, Chip } from '@nextui-org/react' import { Button, Chip } from '@nextui-org/react'
export default function MainPage(props) { export default function MainPage(props) {
const { currentLocale } = props const { currentLocale, isLoggedIn } = props
const t = useI18n() const t = useI18n()
const changeLocale = useChangeLocale() const changeLocale = useChangeLocale()
@ -12,7 +13,11 @@ export default function MainPage(props) {
currentLocale === 'ja' ? changeLocale('ko') : changeLocale('ja') currentLocale === 'ja' ? changeLocale('ko') : changeLocale('ja')
} }
console.log('MainPage', currentLocale) // console.log('MainPage', currentLocale)
const handleLogout = async () => {
await logout()
}
return ( return (
<> <>
@ -22,6 +27,13 @@ export default function MainPage(props) {
<div> <div>
<Button onClick={handleChangeLocale}>Change Locale</Button> <Button onClick={handleChangeLocale}>Change Locale</Button>
</div> </div>
{isLoggedIn && (
<div className="my-4">
<Button color="primary" onClick={handleLogout}>
로그아웃
</Button>
</div>
)}
</> </>
) )
} }

View File

@ -0,0 +1,75 @@
'use client'
import { login } from '@/lib/authActions'
export default function Login() {
return (
<div className="flex flex-col align-center h-screen">
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
<img alt="Your Company" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600" className="mx-auto h-10 w-auto" />
<h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">Sign in to your account</h2>
</div>
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form action={login} method="POST" className="space-y-6">
<div>
<label htmlFor="userId" className="block text-sm font-medium leading-6 text-gray-900">
User ID
</label>
<div className="mt-2">
<input
id="userId"
name="id"
type="text"
required
// autoComplete="email"
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<div className="flex items-center justify-between">
<label htmlFor="password" className="block text-sm font-medium leading-6 text-gray-900">
Password
</label>
<div className="text-sm">
<a href="#" className="font-semibold text-indigo-600 hover:text-indigo-500">
Forgot password?
</a>
</div>
</div>
<div className="mt-2">
<input
id="password"
name="password"
type="password"
required
autoComplete="current-password"
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
<div>
<button
type="submit"
className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Sign in
</button>
</div>
</form>
<p className="mt-10 text-center text-sm text-gray-500">
Not a member?{' '}
<a href="#" className="font-semibold leading-6 text-indigo-600 hover:text-indigo-500">
Start a 14 day free trial
</a>
</p>
</div>
</div>
</div>
)
}

61
src/lib/authActions.js Normal file
View File

@ -0,0 +1,61 @@
'use server'
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation'
import { getIronSession } from 'iron-session'
import { getUserByIdAndPassword } from './user'
import { defaultSession, sessionOptions } from './session'
export async function logout() {
const session = await getSession()
session.destroy()
redirect('/login')
}
export async function getSession() {
let session
session = await getIronSession(cookies(), sessionOptions)
console.log('session:', session)
if (!session.isLoggedIn) {
session.isLoggedIn = defaultSession.isLoggedIn
}
return session
}
export async function login(formData) {
const session = await getSession()
const userId = formData.get('id')
const password = formData.get('password')
console.log('id:', userId)
console.log('password:', password)
// const user = {
// id: 1,
// name: 'jinsoo Kim',
// email: 'jinsoo.kim@example.com',
// }
const loginUser = await getUserByIdAndPassword({ userId, password })
console.log('loginUser:', loginUser)
if (!loginUser) {
throw Error('Wrong Credentials!')
}
// session.id = user.id
// session.email = user.email
session.userId = loginUser.USER_ID
session.saleStoreId = loginUser.SALE_STORE_ID
session.name = loginUser.NAME
session.mail = loginUser.MAIL
session.isLoggedIn = true
console.log('session:', session)
await session.save()
redirect('/')
}

16
src/lib/session.js Normal file
View File

@ -0,0 +1,16 @@
export const defaultSession = {
userId: null,
saleStoreId: null,
name: null,
mail: null,
isLoggedIn: false,
}
export const sessionOptions = {
password: process.env.SESSION_SECRET,
cookieName: 'lama-session',
cookieOptions: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
},
}

View File

@ -4,9 +4,9 @@ const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient() const prisma = new PrismaClient()
export async function createUser({ userId, password }) { export async function getUserByIdAndPassword({ userId, password }) {
return prisma.m_USER.create({ return prisma.m_USER.findFirst({
data: { where: {
USER_ID: userId, USER_ID: userId,
PASSWORD: password, PASSWORD: password,
}, },

View File

@ -2645,6 +2645,11 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0:
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
cookie@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
cross-spawn@^7.0.0: cross-spawn@^7.0.0:
version "7.0.3" version "7.0.3"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
@ -3091,6 +3096,20 @@ invariant@^2.2.4:
dependencies: dependencies:
loose-envify "^1.0.0" loose-envify "^1.0.0"
iron-session@^8.0.2:
version "8.0.2"
resolved "https://registry.yarnpkg.com/iron-session/-/iron-session-8.0.2.tgz#9802e080206a8ba41911b53d29ff7de11161d036"
integrity sha512-p4Yf1moQr6gnCcXu5vCaxVKRKDmR9PZcQDfp7ZOgbsSHUsgaNti6OgDB2BdgxC2aS6V/6Hu4O0wYlj92sbdIJg==
dependencies:
cookie "0.6.0"
iron-webcrypto "1.2.1"
uncrypto "0.1.3"
iron-webcrypto@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz#aa60ff2aa10550630f4c0b11fd2442becdb35a6f"
integrity sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==
is-arrayish@^0.3.1: is-arrayish@^0.3.1:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
@ -4288,6 +4307,11 @@ typed-function@^4.2.1:
resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-4.2.1.tgz#19aa51847aa2dea9ef5e7fb7641c060179a74426" resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-4.2.1.tgz#19aa51847aa2dea9ef5e7fb7641c060179a74426"
integrity sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA== integrity sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==
uncrypto@0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/uncrypto/-/uncrypto-0.1.3.tgz#e1288d609226f2d02d8d69ee861fa20d8348ef2b"
integrity sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==
undici-types@~6.13.0: undici-types@~6.13.0:
version "6.13.0" version "6.13.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5"