developing database
This commit is contained in:
parent
09e1420d26
commit
7364813fd2
|
@ -1,8 +1,10 @@
|
|||
node_modules/
|
||||
src/database/prisma/client
|
||||
**/build
|
||||
**/.cache
|
||||
.env
|
||||
*.log
|
||||
*.sqlite
|
||||
package-lock.json
|
||||
.vscode
|
||||
.vscode
|
||||
*.db*
|
|
@ -8,7 +8,8 @@
|
|||
"build": "remix build",
|
||||
"start": "remix-serve ./build/index.js",
|
||||
"lint": "npx eslint --fix .",
|
||||
"typecheck": "tsc"
|
||||
"typecheck": "tsc",
|
||||
"migrate:dev":"npx prisma generate --schema src/database/prisma/schema.prisma"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -43,6 +44,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@nextui-org/react": "^2.2.9",
|
||||
"@prisma/client": "^5.7.1",
|
||||
"@remix-run/node": "^2.4.0",
|
||||
"@remix-run/react": "^2.4.0",
|
||||
"@remix-run/serve": "^2.4.0",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/** @type {import('@remix-run/dev').AppConfig} */
|
||||
export default {
|
||||
ignoredRouteFiles: ["**/.*"],
|
||||
appDirectory: "src/app",
|
||||
ignoredRouteFiles: ['**/.*'],
|
||||
appDirectory: 'src/app',
|
||||
// assetsBuildDirectory: "public/build",
|
||||
// publicPath: "/build/",
|
||||
// serverBuildPath: "build/index.js",
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,98 +1,108 @@
|
|||
import { Link } from "@remix-run/react";
|
||||
import { Link } from '@remix-run/react';
|
||||
import {
|
||||
Navbar,
|
||||
NavbarBrand,
|
||||
NavbarContent,
|
||||
NavbarItem,
|
||||
Navbar,
|
||||
NavbarBrand,
|
||||
NavbarContent,
|
||||
NavbarItem,
|
||||
NavbarMenuToggle,
|
||||
NavbarMenu,
|
||||
NavbarMenuItem,
|
||||
Button
|
||||
} from "@nextui-org/react";
|
||||
import { useState } from "react";
|
||||
|
||||
Button,
|
||||
} from '@nextui-org/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function NavbarCom() {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
const menuItems = [
|
||||
"Profile",
|
||||
"Dashboard",
|
||||
"Activity",
|
||||
"Analytics",
|
||||
"System",
|
||||
"Deployments",
|
||||
"My Settings",
|
||||
"Team Settings",
|
||||
"Help & Feedback",
|
||||
"Log Out",
|
||||
'Profile',
|
||||
'Dashboard',
|
||||
'Activity',
|
||||
'Analytics',
|
||||
'System',
|
||||
'Deployments',
|
||||
'My Settings',
|
||||
'Team Settings',
|
||||
'Help & Feedback',
|
||||
'Log Out',
|
||||
];
|
||||
|
||||
return(
|
||||
return (
|
||||
<Navbar
|
||||
className="h-navbar"
|
||||
isBordered
|
||||
isMenuOpen={isMenuOpen}
|
||||
onMenuOpenChange={setIsMenuOpen}
|
||||
>
|
||||
<NavbarContent className="sm:hidden" justify="start">
|
||||
<NavbarMenuToggle aria-label={isMenuOpen ? "Close menu" : "Open menu"} />
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent className="sm:hidden pr-3" justify="center">
|
||||
<NavbarBrand>
|
||||
<img src="./favicon.ico" />
|
||||
<p className="ml-1 font-bold text-md text-inherit">Powered by EVRAZ</p>
|
||||
</NavbarBrand>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent className="hidden sm:flex gap-4" justify="center">
|
||||
<NavbarBrand>
|
||||
<img src="./favicon.ico" />
|
||||
<p className="ml-1 font-bold text-xl text-inherit">Powered by EVRAZ</p>
|
||||
</NavbarBrand>
|
||||
<NavbarItem className="mx-auto">
|
||||
<Link color="foreground" to="#">
|
||||
Features
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem isActive>
|
||||
<Link to="#" aria-current="page">
|
||||
Customers
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Link color="foreground" to="#">
|
||||
Integrations
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent justify="end">
|
||||
<NavbarItem className="hidden lg:flex">
|
||||
<Link to="#">Login</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Button as={Link} color="primary" to="#" variant="flat">
|
||||
Sign Up
|
||||
</Button>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarMenu>
|
||||
{menuItems.map((item, index) => (
|
||||
<NavbarMenuItem key={`${item}-${index}`}>
|
||||
<Link
|
||||
className="w-full"
|
||||
color={
|
||||
index === 2 ? "warning" : index === menuItems.length - 1 ? "danger" : "foreground"
|
||||
}
|
||||
to="#"
|
||||
>
|
||||
{item}
|
||||
</Link>
|
||||
</NavbarMenuItem>
|
||||
))}
|
||||
</NavbarMenu>
|
||||
</Navbar>
|
||||
)
|
||||
}
|
||||
className="h-navbar"
|
||||
isBordered
|
||||
isMenuOpen={isMenuOpen}
|
||||
onMenuOpenChange={setIsMenuOpen}
|
||||
>
|
||||
<NavbarContent className="sm:hidden" justify="start">
|
||||
<NavbarMenuToggle
|
||||
aria-label={isMenuOpen ? 'Close menu' : 'Open menu'}
|
||||
/>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent className="sm:hidden pr-3" justify="center">
|
||||
<NavbarBrand>
|
||||
<img src="./favicon.ico" />
|
||||
<p className="ml-1 font-bold text-md text-inherit">
|
||||
Powered by EVRAZ
|
||||
</p>
|
||||
</NavbarBrand>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent className="hidden sm:flex gap-4" justify="center">
|
||||
<NavbarBrand>
|
||||
<img src="./favicon.ico" />
|
||||
<p className="ml-1 font-bold text-xl text-inherit">
|
||||
Powered by EVRAZ
|
||||
</p>
|
||||
</NavbarBrand>
|
||||
<NavbarItem className="mx-auto">
|
||||
<Link color="foreground" to="#">
|
||||
Features
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem isActive>
|
||||
<Link to="#" aria-current="page">
|
||||
Customers
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Link color="foreground" to="#">
|
||||
Integrations
|
||||
</Link>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarContent justify="end">
|
||||
<NavbarItem className="hidden lg:flex">
|
||||
<Link to="#">Login</Link>
|
||||
</NavbarItem>
|
||||
<NavbarItem>
|
||||
<Button as={Link} color="primary" to="#" variant="flat">
|
||||
Sign Up
|
||||
</Button>
|
||||
</NavbarItem>
|
||||
</NavbarContent>
|
||||
|
||||
<NavbarMenu>
|
||||
{menuItems.map((item, index) => (
|
||||
<NavbarMenuItem key={`${item}-${index}`}>
|
||||
<Link
|
||||
className="w-full"
|
||||
color={
|
||||
index === 2
|
||||
? 'warning'
|
||||
: index === menuItems.length - 1
|
||||
? 'danger'
|
||||
: 'foreground'
|
||||
}
|
||||
to="#"
|
||||
>
|
||||
{item}
|
||||
</Link>
|
||||
</NavbarMenuItem>
|
||||
))}
|
||||
</NavbarMenu>
|
||||
</Navbar>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,41 +1,44 @@
|
|||
import type { LinksFunction } from "@remix-run/node";
|
||||
import type { LinksFunction } from '@remix-run/node';
|
||||
import {
|
||||
Links,
|
||||
LiveReload,
|
||||
Meta,
|
||||
Outlet,
|
||||
Scripts,
|
||||
ScrollRestoration,
|
||||
} from "@remix-run/react";
|
||||
import rootCss from "./root.css"
|
||||
import { NextUIProvider } from "@nextui-org/react";
|
||||
import NavbarCom from "./coms/navbar";
|
||||
Links,
|
||||
LiveReload,
|
||||
Meta,
|
||||
Outlet,
|
||||
Scripts,
|
||||
ScrollRestoration,
|
||||
} from '@remix-run/react';
|
||||
import rootCss from './root.css';
|
||||
import { NextUIProvider } from '@nextui-org/react';
|
||||
import NavbarCom from './coms/navbar';
|
||||
|
||||
export const links: LinksFunction = () => [
|
||||
{ rel: "stylesheet", href: rootCss},
|
||||
{ rel: "icon", href: "./favicon.ico"}
|
||||
{ rel: 'stylesheet', href: rootCss },
|
||||
{ rel: 'icon', href: './favicon.ico' },
|
||||
];
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body className="h-screen">
|
||||
<NextUIProvider>
|
||||
<NavbarCom />
|
||||
<main className="h-content bg-background text-foreground flex justify-center items-center">
|
||||
<Outlet />
|
||||
</main>
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</NextUIProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1"
|
||||
/>
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body className="h-screen">
|
||||
<NextUIProvider>
|
||||
<NavbarCom />
|
||||
<main className="h-content bg-background text-foreground flex justify-center items-center">
|
||||
<Outlet />
|
||||
</main>
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</NextUIProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
import type { LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { authenticator } from "~/services/auth.server";
|
||||
import type { LoaderFunctionArgs } from '@remix-run/node';
|
||||
import { authenticator } from '~/services/auth.server';
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
return await authenticator.isAuthenticated(request, {
|
||||
failureRedirect: "/login",
|
||||
failureRedirect: '/login',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default function Index() {
|
||||
return(
|
||||
<>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
|
|
|
@ -1,29 +1,42 @@
|
|||
import { Button, Input } from "@nextui-org/react";
|
||||
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { Form } from "@remix-run/react";
|
||||
import { authenticator } from "~/services/auth.server";
|
||||
import { Button, Input } from '@nextui-org/react';
|
||||
import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node';
|
||||
import { Form } from '@remix-run/react';
|
||||
import { authenticator } from '~/services/auth.server';
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
return await authenticator.isAuthenticated(request, {
|
||||
successRedirect: "/",
|
||||
successRedirect: '/',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default function Auth() {
|
||||
return(
|
||||
<Form method="post">
|
||||
<div id="auth" className="m-auto w-[300px] flex flex-col gap-6">
|
||||
<Input name="login" color="primary" label="login" placeholder="Enter your login"/>
|
||||
<Input name="password" type="password" color="primary" label="password" placeholder="Enter your password"/>
|
||||
<Button type="submit" color="primary" > Login</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
)
|
||||
return (
|
||||
<Form method="post">
|
||||
<div id="auth" className="m-auto w-[300px] flex flex-col gap-6">
|
||||
<Input
|
||||
name="login"
|
||||
color="primary"
|
||||
label="login"
|
||||
placeholder="Enter your login"
|
||||
/>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
color="primary"
|
||||
label="password"
|
||||
placeholder="Enter your password"
|
||||
/>
|
||||
<Button type="submit" color="primary">
|
||||
{' '}
|
||||
Login
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
export async function action({ request }: ActionFunctionArgs) {
|
||||
return await authenticator.authenticate("user-auth", request, {
|
||||
successRedirect: "/",
|
||||
failureRedirect: "/login",
|
||||
return await authenticator.authenticate('user-auth', request, {
|
||||
successRedirect: '/',
|
||||
failureRedirect: '/login',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,29 +1,42 @@
|
|||
import { Button, Input } from "@nextui-org/react";
|
||||
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { Form } from "@remix-run/react";
|
||||
import { authenticator } from "~/services/auth.server";
|
||||
import { Button, Input } from '@nextui-org/react';
|
||||
import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node';
|
||||
import { Form } from '@remix-run/react';
|
||||
import { authenticator } from '~/services/auth.server';
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
return await authenticator.isAuthenticated(request, {
|
||||
successRedirect: "/",
|
||||
successRedirect: '/',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default function Login() {
|
||||
return(
|
||||
<Form method="post">
|
||||
<div id="login" className="m-auto w-[300px] flex flex-col gap-6">
|
||||
<Input name="login" color="primary" label="login" placeholder="Enter your login"/>
|
||||
<Input name="password" type="password" color="primary" label="password" placeholder="Enter your password"/>
|
||||
<Button type="submit" color="primary" > Login</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
)
|
||||
return (
|
||||
<Form method="post">
|
||||
<div id="login" className="m-auto w-[300px] flex flex-col gap-6">
|
||||
<Input
|
||||
name="login"
|
||||
color="primary"
|
||||
label="login"
|
||||
placeholder="Enter your login"
|
||||
/>
|
||||
<Input
|
||||
name="password"
|
||||
type="password"
|
||||
color="primary"
|
||||
label="password"
|
||||
placeholder="Enter your password"
|
||||
/>
|
||||
<Button type="submit" color="primary">
|
||||
{' '}
|
||||
Login
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
export async function action({ request }: ActionFunctionArgs) {
|
||||
return await authenticator.authenticate("user-login", request, {
|
||||
successRedirect: "/",
|
||||
failureRedirect: "/login",
|
||||
return await authenticator.authenticate('user-login', request, {
|
||||
successRedirect: '/',
|
||||
failureRedirect: '/login',
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { Authenticator } from "remix-auth";
|
||||
import { sessionStorage } from "~/services/session.server";
|
||||
import { FormStrategy } from "remix-auth-form";
|
||||
import bcrypt from "bcrypt";
|
||||
import { Authenticator } from 'remix-auth';
|
||||
import { sessionStorage } from '~/services/session.server';
|
||||
import { FormStrategy } from 'remix-auth-form';
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
interface User{
|
||||
login: string,
|
||||
password: string,
|
||||
interface User {
|
||||
login: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export let authenticator = new Authenticator<User>(sessionStorage);
|
||||
|
@ -13,33 +13,35 @@ const saltRounds = 9;
|
|||
|
||||
authenticator.use(
|
||||
new FormStrategy(async ({ form, context }) => {
|
||||
let username = <string>form.get("login");
|
||||
let password = <string>form.get("password");
|
||||
let username = <string>form.get('login');
|
||||
let password = <string>form.get('password');
|
||||
|
||||
const user = { login: 'null', password: 'null' };
|
||||
|
||||
const user = {login: "null", password: "null"};
|
||||
|
||||
return user;
|
||||
}), "user-login"
|
||||
);
|
||||
}),
|
||||
'user-login',
|
||||
);
|
||||
|
||||
authenticator.use(
|
||||
new FormStrategy(async ({ form, context }) => {
|
||||
let username = <string>form.get("login");
|
||||
let password = <string>form.get("password");
|
||||
let username = <string>form.get('login');
|
||||
let password = <string>form.get('password');
|
||||
|
||||
let hashedPassword = "";
|
||||
await bcrypt.hash(password, saltRounds, (err, hash) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
let hashedPassword = '';
|
||||
await bcrypt.hash(password, saltRounds, (error, hash) => {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
hashedPassword = hash;
|
||||
});
|
||||
|
||||
|
||||
if (!hashedPassword) {
|
||||
throw "";
|
||||
throw '';
|
||||
}
|
||||
const user = {login: "null", password: "null"};
|
||||
|
||||
return user;
|
||||
}), "user-auth"
|
||||
);
|
||||
const user = { login: 'null', password: 'null' };
|
||||
|
||||
return user;
|
||||
}),
|
||||
'user-auth',
|
||||
);
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { createCookieSessionStorage } from "@remix-run/node";
|
||||
import { createCookieSessionStorage } from '@remix-run/node';
|
||||
|
||||
export let sessionStorage = createCookieSessionStorage({
|
||||
cookie: {
|
||||
name: "_session",
|
||||
sameSite: "lax",
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secrets: ["s3cr3t"],
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
name: '_session',
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
secrets: ['s3cr3t'],
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
},
|
||||
});
|
||||
|
||||
export let { getSession, commitSession, destroySession } = sessionStorage;
|
||||
});
|
||||
|
||||
export let { getSession, commitSession, destroySession } = sessionStorage;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
class Database {}
|
|
@ -1,8 +1,23 @@
|
|||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = env("DATABASE_URL")
|
||||
provider = "sqlite"
|
||||
url = env("DATABASE_URL")
|
||||
|
||||
}
|
||||
|
||||
model Credentials {
|
||||
id Int @id @default(autoincrement())
|
||||
login String @unique
|
||||
password String
|
||||
worker Worker?
|
||||
|
||||
}
|
||||
|
||||
model Worker {
|
||||
id Int @id @default(autoincrement())
|
||||
credential Credentials @relation(fields: [credentialId],references: [id])
|
||||
credentialId Int @unique
|
||||
}
|
||||
|
|
|
@ -1,50 +1,50 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
|
||||
import colors from "tailwindcss/colors";
|
||||
import { nextui } from "@nextui-org/react";
|
||||
import colors from 'tailwindcss/colors';
|
||||
import { nextui } from '@nextui-org/react';
|
||||
|
||||
export const navbarHeight = "65px";
|
||||
export const navbarHeight = '65px';
|
||||
|
||||
export default {
|
||||
content: [
|
||||
"./src/app/**/*.{js,jsx,ts,tsx}",
|
||||
"./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
height: {
|
||||
"navbar": navbarHeight,
|
||||
"content": `calc(100vh - ${navbarHeight})`,
|
||||
}
|
||||
content: [
|
||||
'./src/app/**/*.{js,jsx,ts,tsx}',
|
||||
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
height: {
|
||||
navbar: navbarHeight,
|
||||
content: `calc(100vh - ${navbarHeight})`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
darkMode: "class",
|
||||
plugins: [nextui({
|
||||
defaultTheme: "light",
|
||||
defaultExtendTheme: "light",
|
||||
themes: {
|
||||
light: {
|
||||
colors: {
|
||||
primary: {
|
||||
...colors.orange,
|
||||
DEFAULT: colors.orange[500],
|
||||
},
|
||||
secondary: {
|
||||
...colors.gray,
|
||||
DEFAULT: colors.gray[500],
|
||||
},
|
||||
warning: {
|
||||
...colors.red,
|
||||
foreground: colors.white,
|
||||
DEFAULT: colors.red[500],
|
||||
}
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
colors: {
|
||||
},
|
||||
},
|
||||
}
|
||||
})],
|
||||
}
|
||||
|
||||
darkMode: 'class',
|
||||
plugins: [
|
||||
nextui({
|
||||
defaultTheme: 'light',
|
||||
defaultExtendTheme: 'light',
|
||||
themes: {
|
||||
light: {
|
||||
colors: {
|
||||
primary: {
|
||||
...colors.orange,
|
||||
DEFAULT: colors.orange[500],
|
||||
},
|
||||
secondary: {
|
||||
...colors.gray,
|
||||
DEFAULT: colors.gray[500],
|
||||
},
|
||||
warning: {
|
||||
...colors.red,
|
||||
foreground: colors.white,
|
||||
DEFAULT: colors.red[500],
|
||||
},
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
colors: {},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue