changes
This commit is contained in:
parent
a7f24f6229
commit
8c56a9421e
28
.vscode/launch.json
vendored
Normal file
28
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Next.js: debug server-side",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "npm run dev"
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug client-side",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000"
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug full stack",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "npm run dev",
|
||||
"serverReadyAction": {
|
||||
"pattern": "- Local:.+(https?://.+)",
|
||||
"uriFormat": "%s",
|
||||
"action": "debugWithChrome"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"window.title": "${dirty}${activeEditorMedium}${separator}${rootName}${separator}${profileName}${separator}${appName}",
|
||||
"workbench.editor.labelFormat": "medium",
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
serverComponentsExternalPackages: ['sequelize', 'sequelize-typescript', 'bcrypt', '@sequelize/core'],
|
||||
serverComponentsExternalPackages: ['sequelize', 'sequelize-typescript', 'bcrypt', '@sequelize/core', '@/app/lib/api/error'],
|
||||
serverActions: {
|
||||
|
||||
},
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
|
||||
|
||||
type APIErrorMessage = {
|
||||
status: number,
|
||||
responseText: string
|
||||
}
|
||||
class APIError extends Error{
|
||||
declare info:APIErrorMessage
|
||||
constructor(message:APIErrorMessage) {
|
||||
super(JSON.stringify(message))
|
||||
this.info = message;
|
||||
this.name = "AuthError"; // (different names for different built-in error classes)
|
||||
}
|
||||
}
|
||||
|
||||
export { APIError }
|
||||
24
src/app/admin/layout.tsx
Normal file
24
src/app/admin/layout.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import '../globals.css'
|
||||
import { Inter } from 'next/font/google'
|
||||
import StyledJsxRegistry from '../registry';
|
||||
import Providers from '@/providers/providers';
|
||||
import 'bootstrap/dist/css/bootstrap.css';
|
||||
|
||||
const inter = Inter({ subsets: ['latin'], fallback: ['system-ui', 'arial'] })
|
||||
|
||||
export const metadata = {
|
||||
title: 'Create Next App',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}><StyledJsxRegistry><Providers>{children}</Providers></StyledJsxRegistry></body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@ -5,9 +5,9 @@ import { cookies } from "next/headers";
|
||||
|
||||
export default async function Page(){
|
||||
return (
|
||||
<main className="h-screen w-screen flex flex-col p-10 bg-background-500 box-border m-0">
|
||||
<AuthHandler params={null}><AdminPanel><p>this is only shown on the admin panel</p></AdminPanel></AuthHandler>
|
||||
<section>{JSON.stringify(cookies().getAll())}</section>
|
||||
<main className="h-screen w-screen flex flex-col p-0 bg-background-500 box-border m-0">
|
||||
<AuthHandler params={null}><AdminPanel></AdminPanel></AuthHandler>
|
||||
{/* <section>{JSON.stringify(cookies().getAll())}</section> */}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
import { APIError} from "@/api/error"
|
||||
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/api/user"
|
||||
import { APIError} from "@/util/api/error"
|
||||
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
|
||||
import { Auth, User } from "@/model/Models";
|
||||
|
||||
|
||||
|
||||
@ -7,8 +7,8 @@ import { cookies } from "next/headers";
|
||||
import { Auth } from "@/model/Models";
|
||||
|
||||
|
||||
import { APIError} from "@/api/error"
|
||||
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/api/user"
|
||||
import { APIError} from "@/util/api/error"
|
||||
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
|
||||
|
||||
|
||||
async function tryAuth(request:Request){
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use server'
|
||||
|
||||
import { APIError } from "@/api/error";
|
||||
import { APIError } from "@/app/lib/api/error";
|
||||
import { Post } from "@/model/Models";
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use server'
|
||||
|
||||
import { APIError } from "@/api/error";
|
||||
import { APIError, attemptAPIAction } from "@/util/api/error";
|
||||
import { Auth, Post, PostTag, Tag, User } from "@/model/Models";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
@ -25,55 +25,45 @@ async function tryCreatePost(request: Request) {
|
||||
const authObject = JSON.parse(cookieJSON);
|
||||
|
||||
// Fetch User Auth from the database
|
||||
const auth:any = await Auth.findOne({ include: 'user', where: { token: authObject.token } });
|
||||
const auth = await Auth.findOne({
|
||||
include: [
|
||||
{
|
||||
model: User.withScope(['withPerms']),
|
||||
attributes: {
|
||||
exclude: ['username', 'password', 'updatedAt', 'createdAt']
|
||||
}
|
||||
}
|
||||
],
|
||||
where: { token: authObject.token }
|
||||
});
|
||||
|
||||
// Sanity check the auth and associated user
|
||||
if (!auth || !auth.user) throw new APIError({ status: 401, responseText: "Authentication Error" });
|
||||
const user = auth.user;
|
||||
|
||||
// Handle incomplete data or other problems
|
||||
if (!requestBody) throw new APIError({ status: 500, responseText: "Empty request body" });
|
||||
if (!requestBody.title) throw new APIError({ status: 500, responseText: "Missing post title" });
|
||||
if (!requestBody.content) throw new APIError({ status: 500, responseText: "Missing post content" });
|
||||
if (!user.id) throw new APIError({ status: 500, responseText: "Missing user id" });
|
||||
if (!user.perms || !user.perms.isAdmin) throw new APIError({ status: 401, responseText: "Unauthorized" });
|
||||
if (!auth.user.id) throw new APIError({ status: 500, responseText: "Missing user id" });
|
||||
if (!auth.user.perms || !auth.user.perms.isAdmin) throw new APIError({ status: 401, responseText: `Unauthorized ${JSON.stringify(auth.user)}` });
|
||||
|
||||
// Create a new Post in the database
|
||||
const post = await Post.create(
|
||||
{
|
||||
content: requestBody.content,
|
||||
user_id: user.id,
|
||||
user_id: auth.user.id,
|
||||
title: requestBody.title,
|
||||
},{
|
||||
include: {
|
||||
association: Post.associations.user
|
||||
}
|
||||
}).then(post=>post.reload())
|
||||
// // Find the post (Unneeded but nice to check)
|
||||
// const foundPost = await Post.findOne({
|
||||
// include: "user",
|
||||
// where: {
|
||||
// id: post.id
|
||||
// }
|
||||
// })
|
||||
|
||||
// Return the response
|
||||
return new Response(JSON.stringify(post), { status: 200 });
|
||||
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
return await tryCreatePost(request);
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof APIError) {
|
||||
return new Response(e.info.responseText, { status: e.info.status });
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function tryFetchPosts(request: Request) {
|
||||
@ -94,18 +84,9 @@ async function tryFetchPosts(request: Request) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
return await tryFetchPosts(request);
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof APIError) {
|
||||
return new Response(e.info.responseText, { status: e.info.status });
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return await attemptAPIAction(tryFetchPosts,request);
|
||||
}
|
||||
export async function POST(request: Request) {
|
||||
return await attemptAPIAction(tryCreatePost,request);
|
||||
}
|
||||
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
*
|
||||
* ================================================================================================= */
|
||||
|
||||
import { APIError } from "@/api/error";
|
||||
import { UserAuth } from "@/api/user";
|
||||
import { APIError } from "@/util/api/error";
|
||||
import { UserAuth } from "@/util/api/user";
|
||||
import { UserPerms, User, Auth } from "@/model/Models"; // Do not alter "unused" imports, they are required to perform the nescessairy includes on the User model
|
||||
import { hashPassword } from "@/util/Auth";
|
||||
|
||||
|
||||
@ -1,40 +1,45 @@
|
||||
'use server'
|
||||
|
||||
import { constructAPIUrl } from "@/util/Utils"
|
||||
// import { signIn } from '@/auth'
|
||||
import { cookies } from "next/headers"
|
||||
import { parseSetCookie } from "@/util/parseSetCookie";
|
||||
import makeFetchCookie from 'fetch-cookie';
|
||||
import fetchCookie from "fetch-cookie";
|
||||
import { Attributes } from "@sequelize/core";
|
||||
import { User } from "@/model/User";
|
||||
|
||||
type LoginReturn = {
|
||||
cookie?:unknown,
|
||||
errorMessage?:string;
|
||||
}
|
||||
|
||||
async function attemptAPILogin(method:string,b:FormData):Promise<LoginReturn|null>
|
||||
async function attemptAPILogin(method:string,formData:FormData):Promise<LoginReturn|null>
|
||||
{
|
||||
console.log("form data:");
|
||||
console.log(b);
|
||||
if(!b || !b.get('input_username') || !b.get('input_password')) return null;
|
||||
// Check if form data is present with required fields, return null if not
|
||||
if(!formData || !formData.get('input_username') || !formData.get('input_password')) return null;
|
||||
|
||||
// Instantiate header object
|
||||
let headers:Headers = new Headers();
|
||||
|
||||
// Prepare fetchCookie
|
||||
const { CookieJar, Cookie } = fetchCookie.toughCookie;
|
||||
const cj = new CookieJar()
|
||||
const fetchKoek = makeFetchCookie(fetch, cj);
|
||||
const jar = new CookieJar()
|
||||
const fetchWithCookie = makeFetchCookie(fetch, jar);
|
||||
|
||||
// Set Basic Auth
|
||||
headers.set('Authorization', `Basic ${Buffer.from(`${b.get('input_username')}:${b.get('input_password')}`).toString('base64')}`);
|
||||
let res = await fetchKoek(constructAPIUrl("auth"), {
|
||||
headers.set('Authorization', `Basic ${Buffer.from(`${formData.get('input_username')}:${formData.get('input_password')}`).toString('base64')}`);
|
||||
let res = await fetchWithCookie(constructAPIUrl("auth"), {
|
||||
method:'POST',
|
||||
credentials: 'include',
|
||||
headers:headers,
|
||||
});
|
||||
console.log(cj.store.idx['localhost']['/']);
|
||||
// if(res.headers.get('set-coo')) console.log(res.headers['set-cookie'])
|
||||
|
||||
console.log(jar.store.idx['localhost']['/']);
|
||||
|
||||
let koek = res.headers.getSetCookie();
|
||||
// console.log("api koek:" + koek.map((k)=>decodeURIComponent(k)));
|
||||
|
||||
let cookieDict = parseSetCookie(koek);
|
||||
console.log("testing cookiedict")
|
||||
console.log(cookieDict);
|
||||
|
||||
await cookies().set('auth', cookieDict.auth);
|
||||
return {
|
||||
cookie:cookieDict.auth,
|
||||
@ -45,24 +50,17 @@ async function attemptAPILogin(method:string,b:FormData):Promise<LoginReturn|nul
|
||||
|
||||
export async function serverAttemptAuthenticateUser(_currentState: unknown, formData: FormData):Promise<LoginReturn|null>
|
||||
{
|
||||
console.log("action triggered")
|
||||
try {
|
||||
const signInStatus = await attemptAPILogin('credentials', formData)
|
||||
return signInStatus;
|
||||
} catch (error:any) {
|
||||
if (error) {
|
||||
switch (error.type) {
|
||||
case 'CredentialsSignin':
|
||||
return {
|
||||
errorMessage: 'invalidCredentials'
|
||||
}
|
||||
default:
|
||||
return {
|
||||
errorMessage: 'Something went wrong.'
|
||||
case 'CredentialsSignin': return { errorMessage: 'invalidCredentials' };
|
||||
default: return { errorMessage: 'Something went wrong.' };
|
||||
}
|
||||
}
|
||||
}
|
||||
throw error
|
||||
throw Error
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,3 +77,9 @@ export async function serverValidateSessionCookie(koek:string):Promise<boolean>
|
||||
else
|
||||
return false
|
||||
}
|
||||
|
||||
export async function userIsAdmin(user:Partial<Attributes<User>>):Promise<boolean>
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1,23 +1,67 @@
|
||||
'use client'
|
||||
import { ReactNode, useContext } from "react";
|
||||
import { ReactNode, useContext, useState } from "react";
|
||||
import { AuthContext, AuthProps } from "@/providers/providers";
|
||||
import SomeServerSubComponent from "@/components/server/admin/serverContextUserTest";
|
||||
import PostView from "@/components/server/admin/views/PostView";
|
||||
import Sidebar from "./views/sidebar";
|
||||
import ClientPostView from "./views/ClientPostView";
|
||||
import ServerComponentTest from "@/components/server/ServerComponentTest";
|
||||
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode;
|
||||
auth?: AuthProps;
|
||||
}
|
||||
|
||||
|
||||
export type SidebarEntry = {
|
||||
label:string;
|
||||
view:string;
|
||||
}
|
||||
|
||||
|
||||
export const sidebarEntries:Array<SidebarEntry> = [
|
||||
{ label: 'Home', view: 'home'},
|
||||
{ label: 'Post Management', view: 'man-post'},
|
||||
{ label: 'Project Management', view: 'man-proj'},
|
||||
{ label: 'Tag Management', view: 'man-tags'},
|
||||
{ label: 'User Management', view: 'man-user'},
|
||||
]
|
||||
|
||||
function Home(){
|
||||
return <div>home</div>
|
||||
}
|
||||
function PostManager(){
|
||||
return <div>posts</div>
|
||||
}
|
||||
|
||||
|
||||
const viewMap = new Map<string, ReactNode>([
|
||||
['home', <Home></Home>],
|
||||
['man-post', <ClientPostView></ClientPostView>]
|
||||
])
|
||||
|
||||
|
||||
|
||||
export default function AdminPanel(props:Props){
|
||||
const [view, setView] = useState((<Home></Home>) as ReactNode);
|
||||
|
||||
const switchView = function (value:string){
|
||||
setView(viewMap.get(value));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="AdminPanelWrapper">
|
||||
<h1>Super Secret Admin Panel!</h1>
|
||||
<h2>this is where we use the context test:<SomeSubComponent></SomeSubComponent></h2>
|
||||
<SomeServerSubComponent></SomeServerSubComponent>
|
||||
<section>
|
||||
{props.children}
|
||||
</section>
|
||||
<div className="AdminPanelWrapper w-[100%] h-[100%] flex flex-row">
|
||||
{/* <h1>Super Secret Admin Panel!</h1> */}
|
||||
{/* <h2>this is where we use the context test:<SomeSubComponent></SomeSubComponent></h2> */}
|
||||
{/* <SomeServerSubComponent></SomeServerSubComponent> */}
|
||||
{/* <section> */}
|
||||
{/* {props.children} */}
|
||||
{/* <PostView></PostView> */}
|
||||
{/* </section> */}
|
||||
<Sidebar sidebarEntries={sidebarEntries} switchView={switchView}></Sidebar>
|
||||
<div>{view}</div>
|
||||
<ServerComponentTest></ServerComponentTest>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
9
src/components/client/admin/entryEditor.tsx
Normal file
9
src/components/client/admin/entryEditor.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
export default function EntryEditor(){
|
||||
return (
|
||||
<div className='w[100%] h[100%] bg-primary-800'>
|
||||
Entries
|
||||
</div>
|
||||
);
|
||||
}
|
||||
16
src/components/client/admin/views/ClientPostView.tsx
Normal file
16
src/components/client/admin/views/ClientPostView.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
'use client'
|
||||
|
||||
import ServerComponentTest from "@/components/server/ServerComponentTest";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type Props = {
|
||||
children?:ReactNode
|
||||
}
|
||||
|
||||
export default function ClientPostView(props:Props){
|
||||
return (
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
);
|
||||
}
|
||||
90
src/components/client/admin/views/sidebar.css
Normal file
90
src/components/client/admin/views/sidebar.css
Normal file
@ -0,0 +1,90 @@
|
||||
body {
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
html {
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
max-height: 100vh;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.b-example-divider {
|
||||
flex-shrink: 0;
|
||||
width: 1.5rem;
|
||||
height: 100vh;
|
||||
background-color: rgba(0, 0, 0, .1);
|
||||
border: solid rgba(0, 0, 0, .15);
|
||||
border-width: 1px 0;
|
||||
box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
|
||||
}
|
||||
|
||||
.bi {
|
||||
vertical-align: -.125em;
|
||||
pointer-events: none;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.dropdown-toggle { outline: 0; }
|
||||
|
||||
.nav-flush .nav-link {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: .25rem .5rem;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, .65);
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
.btn-toggle:hover,
|
||||
.btn-toggle:focus {
|
||||
color: rgba(0, 0, 0, .85);
|
||||
background-color: #d2f4ea;
|
||||
}
|
||||
|
||||
.btn-toggle::before {
|
||||
width: 1.25em;
|
||||
line-height: 0;
|
||||
content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");
|
||||
transition: transform .35s ease;
|
||||
transform-origin: .5em 50%;
|
||||
}
|
||||
|
||||
.btn-toggle[aria-expanded="true"] {
|
||||
color: rgba(0, 0, 0, .85);
|
||||
}
|
||||
.btn-toggle[aria-expanded="true"]::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.btn-toggle-nav a {
|
||||
display: inline-flex;
|
||||
padding: .1875rem .5rem;
|
||||
margin-top: .125rem;
|
||||
margin-left: 1.25rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn-toggle-nav a:hover,
|
||||
.btn-toggle-nav a:focus {
|
||||
background-color: #d2f4ea;
|
||||
}
|
||||
|
||||
.scrollarea {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.fw-semibold { font-weight: 600; }
|
||||
.lh-tight { line-height: 1.25; }
|
||||
|
||||
30
src/components/client/admin/views/sidebar.tsx
Normal file
30
src/components/client/admin/views/sidebar.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import './sidebar.css'
|
||||
import { Button, NavLink } from 'react-bootstrap';
|
||||
import { SidebarEntry } from '../adminPanel';
|
||||
import React, { ReactNode, useState } from 'react';
|
||||
|
||||
|
||||
|
||||
|
||||
type Props = {
|
||||
children?:ReactNode;
|
||||
sidebarEntries:Array<SidebarEntry>;
|
||||
switchView:any;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default function Sidebar(props:Props){
|
||||
|
||||
return (
|
||||
<div className='w[100%] h[100%] bg-secondary-800'>
|
||||
<ul className='nav nav-pills flex-column mb-auto container-fluid'>
|
||||
{props.sidebarEntries.map((sidebarEntry)=>{
|
||||
return <li className='nav-item'><NavLink className='nav-link bg-purple-400 m-2' onClick={(e)=>{props.switchView(sidebarEntry.view)}}>
|
||||
{sidebarEntry.label}
|
||||
</NavLink></li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
0
src/components/server/ServerComponentTest.tsx
Normal file
0
src/components/server/ServerComponentTest.tsx
Normal file
@ -40,8 +40,8 @@ export default async function AuthHandler(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row">
|
||||
{!(koek && await serverValidateSessionCookie(koek)) ? <LoginForm>{ }</LoginForm> : <ClientAuthHandler authProps={p}><section>{props.children}<div>signed in! :D</div></section></ClientAuthHandler>}
|
||||
<div className="flex flex-row w-[100%] h-[100%]">
|
||||
{!(koek && await serverValidateSessionCookie(koek)) ? <LoginForm>{ }</LoginForm> : <ClientAuthHandler authProps={p}>{props.children}</ClientAuthHandler>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
47
src/components/server/admin/authWrapper.tsx
Normal file
47
src/components/server/admin/authWrapper.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
'use server'
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
import LoginForm from "@/components/client/admin/loginForm";
|
||||
import AdminPanel from "@/components/client/admin/adminPanel";
|
||||
import ClientAuthHandler from "@/components/client/admin/clientAuthHandler";
|
||||
|
||||
import { serverValidateSessionCookie } from "@/app/lib/actions";
|
||||
import { constructAPIUrl } from "@/util/Utils";
|
||||
import { ReactNode } from "react";
|
||||
import { AuthContext, AuthProps } from "@/providers/providers";
|
||||
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode;
|
||||
params?: any;
|
||||
requiredRole?: number;
|
||||
} // We interfacing lads
|
||||
|
||||
|
||||
export default async function AuthHandler(props: Props) {
|
||||
const protoKoek = await cookies().get('auth')?.value;
|
||||
const koek = decodeURIComponent(protoKoek ? protoKoek: "");
|
||||
console.log("koekje:" + koek)
|
||||
let p:AuthProps = {
|
||||
test:"not pog"
|
||||
};
|
||||
if(koek){
|
||||
const kd = JSON.parse(koek);
|
||||
if(kd.id && kd.token && kd.user_id){
|
||||
p = {
|
||||
test:"success!",
|
||||
auth: {
|
||||
id:kd.id,
|
||||
token:kd.token,
|
||||
user_id:kd.user_id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row">
|
||||
{!(koek && await serverValidateSessionCookie(koek)) ? <LoginForm>{ }</LoginForm> : <ClientAuthHandler authProps={p}><section>{props.children}</section></ClientAuthHandler>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
21
src/components/server/admin/views/PostView.tsx
Normal file
21
src/components/server/admin/views/PostView.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
'use server'
|
||||
|
||||
import { ReactNode } from "react"
|
||||
import AuthHandler from "../authHandler"
|
||||
import { Post } from "@/model/Post"
|
||||
import { constructAPIUrl } from "@/util/Utils"
|
||||
|
||||
type Props = {
|
||||
children?:ReactNode
|
||||
}
|
||||
|
||||
export default async function PostView(props:Props){
|
||||
// const posts = await Post.findAll();
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
test
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,11 +1,9 @@
|
||||
import { Association, BelongsToGetAssociationMixin, CreationOptional, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";import { User } from "./User";
|
||||
|
||||
import { SqliteDialect } from '@sequelize/sqlite3';
|
||||
import { Attribute, AutoIncrement, PrimaryKey, Unique } from "@sequelize/core/decorators-legacy";
|
||||
import { Post } from "./Post";
|
||||
|
||||
const sequelize = new Sequelize({
|
||||
dialect: SqliteDialect,
|
||||
storage: 'db.sqlite'
|
||||
});
|
||||
export type ProjectAttributes = {
|
||||
id: number;
|
||||
title: string;
|
||||
@ -20,37 +18,21 @@ export type ProjectCreationAttributes = {
|
||||
user_id:number;
|
||||
}
|
||||
export class Project extends Model<ProjectAttributes, ProjectCreationAttributes> {
|
||||
@Attribute(DataTypes.INTEGER) @PrimaryKey @Unique @AutoIncrement
|
||||
declare id: number;
|
||||
declare user:NonAttribute<User>;
|
||||
declare user_id:ForeignKey<User['id']>;
|
||||
declare getUser: BelongsToGetAssociationMixin<User>;
|
||||
@Attribute(DataTypes.STRING) @Unique
|
||||
declare readableIdentifier: string;
|
||||
@Attribute(DataTypes.STRING)
|
||||
declare name: string;
|
||||
|
||||
declare static associations: {
|
||||
user: Association<User, Project>;
|
||||
posts: Association<Post, Project>;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Project.init(
|
||||
{
|
||||
id: {
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
unique: true,
|
||||
},
|
||||
content: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
title: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
date: {
|
||||
type: DataTypes.DATE
|
||||
},
|
||||
|
||||
},
|
||||
{
|
||||
tableName: 'Projects',
|
||||
sequelize // passing the `sequelize` instance is required
|
||||
}
|
||||
);
|
||||
const sequelize = new Sequelize({
|
||||
dialect: SqliteDialect,
|
||||
models: [Project]
|
||||
})
|
||||
|
||||
@ -10,12 +10,10 @@ export class Tag extends Model<InferAttributes<Tag>, InferCreationAttributes<Tag
|
||||
@Attribute(DataTypes.INTEGER)
|
||||
@Unique
|
||||
declare id: number;
|
||||
|
||||
/** Defined by {@link Post.postTags} */
|
||||
declare taggedPosts?:NonAttribute<Post[]>;
|
||||
declare getPosts: BelongsToManyGetAssociationsMixin<Post>;
|
||||
|
||||
declare user_id:ForeignKey<Tag>;
|
||||
declare getPost: BelongsToManyGetAssociationsMixin<Tag>;
|
||||
declare static associations: {
|
||||
posts: Association<Post, Tag>;
|
||||
};
|
||||
|
||||
@ -55,6 +55,7 @@ export class User extends Model<InferAttributes<User>, InferCreationAttributes<U
|
||||
/** Defined by {@link Post.user} */
|
||||
declare posts?:CreationOptional<Post[]>
|
||||
|
||||
|
||||
declare static associations: {
|
||||
perms: Association<UserPerms,User>;
|
||||
posts: Association<Post,User>
|
||||
|
||||
@ -15,8 +15,19 @@ let p: AuthProps = {
|
||||
test: "lorem",
|
||||
}
|
||||
|
||||
export type AdminViewProps = {
|
||||
view: string;
|
||||
}
|
||||
|
||||
let avp: AdminViewProps = {
|
||||
view: "home",
|
||||
}
|
||||
|
||||
export const AdminViewContext = createContext(avp);
|
||||
export const AuthContext = createContext(p);
|
||||
|
||||
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode;
|
||||
params?: any;
|
||||
@ -24,6 +35,6 @@ interface Props {
|
||||
|
||||
export default function Providers(props:Props){
|
||||
return (
|
||||
<AuthContext.Provider value={{test:"freek"}}>{props.children}</AuthContext.Provider>
|
||||
<AuthContext.Provider value={{test:"freek"}}><AdminViewContext.Provider value={avp}>{props.children}</AdminViewContext.Provider></AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
28
src/util/api/error.ts
Normal file
28
src/util/api/error.ts
Normal file
@ -0,0 +1,28 @@
|
||||
type APIErrorMessage = {
|
||||
status: number,
|
||||
responseText: string
|
||||
}
|
||||
class APIError extends Error{
|
||||
declare info:APIErrorMessage
|
||||
constructor(message:APIErrorMessage) {
|
||||
super(JSON.stringify(message))
|
||||
this.info = message;
|
||||
this.name = "AuthError"; // (different names for different built-in error classes)
|
||||
}
|
||||
}
|
||||
|
||||
const attemptAPIAction = async (action:Function,request:Request) => {
|
||||
try {
|
||||
return await action(request);
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof APIError) {
|
||||
return new Response(e.info.responseText, { status: e.info.status });
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { APIError, attemptAPIAction }
|
||||
@ -2,7 +2,7 @@
|
||||
// import { MAuth } from "@/model/sequelize/Auth";
|
||||
|
||||
import { validatePassword } from "@/util/Auth";
|
||||
import { APIError } from "@/api/error";
|
||||
import { APIError } from "@/util/api/error";
|
||||
import { User } from "@/model/User";
|
||||
|
||||
export function parseBasicAuth(authb64:string):UserAuth
|
||||
Loading…
x
Reference in New Issue
Block a user