This commit is contained in:
Andreas Schaafsma 2024-05-25 01:17:39 +02:00
parent a7f24f6229
commit 8c56a9421e
27 changed files with 467 additions and 165 deletions

28
.vscode/launch.json vendored Normal file
View 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
View File

@ -0,0 +1,4 @@
{
"window.title": "${dirty}${activeEditorMedium}${separator}${rootName}${separator}${profileName}${separator}${appName}",
"workbench.editor.labelFormat": "medium",
}

View File

@ -1,7 +1,7 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
experimental: { experimental: {
serverComponentsExternalPackages: ['sequelize', 'sequelize-typescript', 'bcrypt', '@sequelize/core'], serverComponentsExternalPackages: ['sequelize', 'sequelize-typescript', 'bcrypt', '@sequelize/core', '@/app/lib/api/error'],
serverActions: { serverActions: {
}, },

View File

@ -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
View 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>
)
}

View File

@ -5,9 +5,9 @@ import { cookies } from "next/headers";
export default async function Page(){ export default async function Page(){
return ( return (
<main className="h-screen w-screen flex flex-col p-10 bg-background-500 box-border m-0"> <main className="h-screen w-screen flex flex-col p-0 bg-background-500 box-border m-0">
<AuthHandler params={null}><AdminPanel><p>this is only shown on the admin panel</p></AdminPanel></AuthHandler> <AuthHandler params={null}><AdminPanel></AdminPanel></AuthHandler>
<section>{JSON.stringify(cookies().getAll())}</section> {/* <section>{JSON.stringify(cookies().getAll())}</section> */}
</main> </main>
); );
} }

View File

@ -2,8 +2,8 @@
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { APIError} from "@/api/error" import { APIError} from "@/util/api/error"
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/api/user" import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
import { Auth, User } from "@/model/Models"; import { Auth, User } from "@/model/Models";

View File

@ -7,8 +7,8 @@ import { cookies } from "next/headers";
import { Auth } from "@/model/Models"; import { Auth } from "@/model/Models";
import { APIError} from "@/api/error" import { APIError} from "@/util/api/error"
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/api/user" import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
async function tryAuth(request:Request){ async function tryAuth(request:Request){

View File

@ -1,6 +1,6 @@
'use server' 'use server'
import { APIError } from "@/api/error"; import { APIError } from "@/app/lib/api/error";
import { Post } from "@/model/Models"; import { Post } from "@/model/Models";

View File

@ -1,6 +1,6 @@
'use server' 'use server'
import { APIError } from "@/api/error"; import { APIError, attemptAPIAction } from "@/util/api/error";
import { Auth, Post, PostTag, Tag, User } from "@/model/Models"; import { Auth, Post, PostTag, Tag, User } from "@/model/Models";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
@ -25,55 +25,45 @@ async function tryCreatePost(request: Request) {
const authObject = JSON.parse(cookieJSON); const authObject = JSON.parse(cookieJSON);
// Fetch User Auth from the database // 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 // Sanity check the auth and associated user
if (!auth || !auth.user) throw new APIError({ status: 401, responseText: "Authentication Error" }); if (!auth || !auth.user) throw new APIError({ status: 401, responseText: "Authentication Error" });
const user = auth.user;
// Handle incomplete data or other problems // Handle incomplete data or other problems
if (!requestBody) throw new APIError({ status: 500, responseText: "Empty request body" }); 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.title) throw new APIError({ status: 500, responseText: "Missing post title" });
if (!requestBody.content) throw new APIError({ status: 500, responseText: "Missing post content" }); 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 (!auth.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.perms || !auth.user.perms.isAdmin) throw new APIError({ status: 401, responseText: `Unauthorized ${JSON.stringify(auth.user)}` });
// Create a new Post in the database // Create a new Post in the database
const post = await Post.create( const post = await Post.create(
{ {
content: requestBody.content, content: requestBody.content,
user_id: user.id, user_id: auth.user.id,
title: requestBody.title, title: requestBody.title,
},{ },{
include: { include: {
association: Post.associations.user association: Post.associations.user
} }
}).then(post=>post.reload()) }).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 }); 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) { async function tryFetchPosts(request: Request) {
@ -94,18 +84,9 @@ async function tryFetchPosts(request: Request) {
} }
export async function GET(request: Request) { export async function GET(request: Request) {
try { return await attemptAPIAction(tryFetchPosts,request);
return await tryFetchPosts(request); }
} export async function POST(request: Request) {
catch (e) { return await attemptAPIAction(tryCreatePost,request);
if (e instanceof APIError) {
return new Response(e.info.responseText, { status: e.info.status });
}
else {
throw e;
}
}
} }

View File

@ -8,8 +8,8 @@
* *
* ================================================================================================= */ * ================================================================================================= */
import { APIError } from "@/api/error"; import { APIError } from "@/util/api/error";
import { UserAuth } from "@/api/user"; 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 { 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"; import { hashPassword } from "@/util/Auth";

View File

@ -1,68 +1,66 @@
'use server' 'use server'
import { constructAPIUrl } from "@/util/Utils" import { constructAPIUrl } from "@/util/Utils"
// import { signIn } from '@/auth' import { cookies } from "next/headers"
import { cookies } from "next/headers"
import { parseSetCookie } from "@/util/parseSetCookie"; import { parseSetCookie } from "@/util/parseSetCookie";
import makeFetchCookie from 'fetch-cookie'; import makeFetchCookie from 'fetch-cookie';
import fetchCookie from "fetch-cookie"; import fetchCookie from "fetch-cookie";
import { Attributes } from "@sequelize/core";
import { User } from "@/model/User";
type LoginReturn = { type LoginReturn = {
cookie?:unknown, cookie?:unknown,
errorMessage?:string; 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:"); // Check if form data is present with required fields, return null if not
console.log(b); if(!formData || !formData.get('input_username') || !formData.get('input_password')) return null;
if(!b || !b.get('input_username') || !b.get('input_password')) return null;
let headers:Headers = new Headers(); // Instantiate header object
const { CookieJar, Cookie } = fetchCookie.toughCookie; let headers:Headers = new Headers();
const cj = new CookieJar()
const fetchKoek = makeFetchCookie(fetch, cj); // Prepare fetchCookie
// Set Basic Auth const { CookieJar, Cookie } = fetchCookie.toughCookie;
headers.set('Authorization', `Basic ${Buffer.from(`${b.get('input_username')}:${b.get('input_password')}`).toString('base64')}`); const jar = new CookieJar()
let res = await fetchKoek(constructAPIUrl("auth"), { const fetchWithCookie = makeFetchCookie(fetch, jar);
method:'POST',
credentials: 'include', // Set Basic Auth
headers:headers, headers.set('Authorization', `Basic ${Buffer.from(`${formData.get('input_username')}:${formData.get('input_password')}`).toString('base64')}`);
}); let res = await fetchWithCookie(constructAPIUrl("auth"), {
console.log(cj.store.idx['localhost']['/']); method:'POST',
// if(res.headers.get('set-coo')) console.log(res.headers['set-cookie']) credentials: 'include',
let koek = res.headers.getSetCookie(); headers:headers,
// console.log("api koek:" + koek.map((k)=>decodeURIComponent(k))); });
let cookieDict = parseSetCookie(koek);
console.log("testing cookiedict") console.log(jar.store.idx['localhost']['/']);
console.log(cookieDict);
await cookies().set('auth', cookieDict.auth); let koek = res.headers.getSetCookie();
return {
cookie:cookieDict.auth, let cookieDict = parseSetCookie(koek);
errorMessage:""
}; await cookies().set('auth', cookieDict.auth);
// console.log(koek); return {
cookie:cookieDict.auth,
errorMessage:""
};
// console.log(koek);
} }
export async function serverAttemptAuthenticateUser(_currentState: unknown, formData: FormData):Promise<LoginReturn|null> export async function serverAttemptAuthenticateUser(_currentState: unknown, formData: FormData):Promise<LoginReturn|null>
{ {
console.log("action triggered") try {
try {
const signInStatus = await attemptAPILogin('credentials', formData) const signInStatus = await attemptAPILogin('credentials', formData)
return signInStatus; return signInStatus;
} catch (error:any) { } catch (error:any) {
if (error) { if (error) {
switch (error.type) { switch (error.type) {
case 'CredentialsSignin': case 'CredentialsSignin': return { errorMessage: 'invalidCredentials' };
return { default: return { errorMessage: 'Something went wrong.' };
errorMessage: 'invalidCredentials'
}
default:
return {
errorMessage: 'Something went wrong.'
}
} }
} }
throw error throw Error
} }
} }
@ -78,4 +76,10 @@ export async function serverValidateSessionCookie(koek:string):Promise<boolean>
return true return true
else else
return false return false
}
export async function userIsAdmin(user:Partial<Attributes<User>>):Promise<boolean>
{
return false;
} }

View File

@ -1,23 +1,67 @@
'use client' 'use client'
import { ReactNode, useContext } from "react"; import { ReactNode, useContext, useState } from "react";
import { AuthContext, AuthProps } from "@/providers/providers"; import { AuthContext, AuthProps } from "@/providers/providers";
import SomeServerSubComponent from "@/components/server/admin/serverContextUserTest"; 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 { interface Props {
children?: ReactNode; children?: ReactNode;
auth?: AuthProps; 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){ export default function AdminPanel(props:Props){
const [view, setView] = useState((<Home></Home>) as ReactNode);
const switchView = function (value:string){
setView(viewMap.get(value));
}
return ( return (
<div className="AdminPanelWrapper"> <div className="AdminPanelWrapper w-[100%] h-[100%] flex flex-row">
<h1>Super Secret Admin Panel!</h1> {/* <h1>Super Secret Admin Panel!</h1> */}
<h2>this is where we use the context test:<SomeSubComponent></SomeSubComponent></h2> {/* <h2>this is where we use the context test:<SomeSubComponent></SomeSubComponent></h2> */}
<SomeServerSubComponent></SomeServerSubComponent> {/* <SomeServerSubComponent></SomeServerSubComponent> */}
<section> {/* <section> */}
{props.children} {/* {props.children} */}
</section> {/* <PostView></PostView> */}
{/* </section> */}
<Sidebar sidebarEntries={sidebarEntries} switchView={switchView}></Sidebar>
<div>{view}</div>
<ServerComponentTest></ServerComponentTest>
</div> </div>
) )
} }

View File

@ -0,0 +1,9 @@
export default function EntryEditor(){
return (
<div className='w[100%] h[100%] bg-primary-800'>
Entries
</div>
);
}

View 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>
);
}

View 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; }

View 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>
);
}

View File

@ -40,8 +40,8 @@ export default async function AuthHandler(props: Props) {
} }
return ( return (
<div className="flex flex-row"> <div className="flex flex-row w-[100%] h-[100%]">
{!(koek && await serverValidateSessionCookie(koek)) ? <LoginForm>{ }</LoginForm> : <ClientAuthHandler authProps={p}><section>{props.children}<div>signed in! :D</div></section></ClientAuthHandler>} {!(koek && await serverValidateSessionCookie(koek)) ? <LoginForm>{ }</LoginForm> : <ClientAuthHandler authProps={p}>{props.children}</ClientAuthHandler>}
</div> </div>
); );
} }

View 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>
);
}

View 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>
);
}

View File

@ -1,11 +1,9 @@
import { Association, BelongsToGetAssociationMixin, CreationOptional, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";import { User } from "./User"; import { Association, BelongsToGetAssociationMixin, CreationOptional, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";import { User } from "./User";
import { SqliteDialect } from '@sequelize/sqlite3'; 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 = { export type ProjectAttributes = {
id: number; id: number;
title: string; title: string;
@ -20,37 +18,21 @@ export type ProjectCreationAttributes = {
user_id:number; user_id:number;
} }
export class Project extends Model<ProjectAttributes, ProjectCreationAttributes> { export class Project extends Model<ProjectAttributes, ProjectCreationAttributes> {
@Attribute(DataTypes.INTEGER) @PrimaryKey @Unique @AutoIncrement
declare id: number; declare id: number;
declare user:NonAttribute<User>; @Attribute(DataTypes.STRING) @Unique
declare user_id:ForeignKey<User['id']>; declare readableIdentifier: string;
declare getUser: BelongsToGetAssociationMixin<User>; @Attribute(DataTypes.STRING)
declare name: string;
declare static associations: { declare static associations: {
user: Association<User, Project>; posts: Association<Post, Project>;
}; };
} }
Project.init( const sequelize = new Sequelize({
{ dialect: SqliteDialect,
id: { models: [Project]
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
}
);

View File

@ -10,12 +10,10 @@ export class Tag extends Model<InferAttributes<Tag>, InferCreationAttributes<Tag
@Attribute(DataTypes.INTEGER) @Attribute(DataTypes.INTEGER)
@Unique @Unique
declare id: number; declare id: number;
/** Defined by {@link Post.postTags} */ /** Defined by {@link Post.postTags} */
declare taggedPosts?:NonAttribute<Post[]>; declare taggedPosts?:NonAttribute<Post[]>;
declare getPosts: BelongsToManyGetAssociationsMixin<Post>;
declare user_id:ForeignKey<Tag>;
declare getPost: BelongsToManyGetAssociationsMixin<Tag>;
declare static associations: { declare static associations: {
posts: Association<Post, Tag>; posts: Association<Post, Tag>;
}; };

View File

@ -55,9 +55,10 @@ export class User extends Model<InferAttributes<User>, InferCreationAttributes<U
/** Defined by {@link Post.user} */ /** Defined by {@link Post.user} */
declare posts?:CreationOptional<Post[]> declare posts?:CreationOptional<Post[]>
declare static associations: { declare static associations: {
perms: Association<UserPerms,User>; perms: Association<UserPerms,User>;
posts: Association<Post,User> posts: Association<Post,User>
authtokens: Association<Auth,User>; authtokens: Association<Auth,User>;
}; };

View File

@ -15,8 +15,19 @@ let p: AuthProps = {
test: "lorem", test: "lorem",
} }
export type AdminViewProps = {
view: string;
}
let avp: AdminViewProps = {
view: "home",
}
export const AdminViewContext = createContext(avp);
export const AuthContext = createContext(p); export const AuthContext = createContext(p);
interface Props { interface Props {
children?: ReactNode; children?: ReactNode;
params?: any; params?: any;
@ -24,6 +35,6 @@ interface Props {
export default function Providers(props:Props){ export default function Providers(props:Props){
return ( 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
View 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 }

View File

@ -2,7 +2,7 @@
// import { MAuth } from "@/model/sequelize/Auth"; // import { MAuth } from "@/model/sequelize/Auth";
import { validatePassword } from "@/util/Auth"; import { validatePassword } from "@/util/Auth";
import { APIError } from "@/api/error"; import { APIError } from "@/util/api/error";
import { User } from "@/model/User"; import { User } from "@/model/User";
export function parseBasicAuth(authb64:string):UserAuth export function parseBasicAuth(authb64:string):UserAuth