This commit is contained in:
Andreas 2024-07-06 23:24:25 +02:00
commit 33aef5e2f7
46 changed files with 524 additions and 441 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,25 +1,30 @@
// import '../globals.css' // import '../globals.css'
import { Inter } from 'next/font/google' import { Inter } from "next/font/google";
import StyledJsxRegistry from '../registry'; import StyledJsxRegistry from "../registry";
import Providers from '@/providers/providers'; import ContextProviders from "@/providers/contextproviders";
import 'bootstrap/dist/css/bootstrap.css'; import "bootstrap/dist/css/bootstrap.css";
import { Toaster } from 'react-hot-toast'; import { Toaster } from "react-hot-toast";
const inter = Inter({ subsets: ['latin'], fallback: ['system-ui', 'arial'] }) const inter = Inter({ subsets: ["latin"], fallback: ["system-ui", "arial"] });
export const metadata = { export const metadata = {
title: 'Create Next App', title: "Create Next App",
description: 'Generated by create next app', description: "Generated by create next app",
} };
export default function RootLayout({ export default function RootLayout({
children, children,
}: { }: {
children: React.ReactNode children: React.ReactNode;
}) { }) {
return ( return (
<html lang="en"> <html lang="en">
<body className={inter.className}><Toaster position="bottom-center" /><StyledJsxRegistry><Providers>{children}</Providers></StyledJsxRegistry></body> <body className={inter.className}>
</html> <Toaster position="bottom-center" />
) <StyledJsxRegistry>
<ContextProviders>{children}</ContextProviders>
</StyledJsxRegistry>
</body>
</html>
);
} }

11
src/app/api-doc/page.tsx Normal file
View File

@ -0,0 +1,11 @@
import { getApiDocs } from "@/lib/swagger";
import ReactSwagger from "@/components/client/SwaggerComponent";
export default async function IndexPage() {
const spec = await getApiDocs();
return (
<section className="container">
<ReactSwagger spec={spec} />
</section>
);
}

View File

@ -1,6 +1,5 @@
"use server"; "use server";
import { APIError, attemptAPIAction } from "@/util";
import { import {
sequelize, sequelize,
Bucket, Bucket,
@ -20,6 +19,7 @@ import {
} from "@/app/lib/actions/entitymanagement/attachment/attachmentActions"; } from "@/app/lib/actions/entitymanagement/attachment/attachmentActions";
import { Attributes } from "@sequelize/core"; import { Attributes } from "@sequelize/core";
import { RequestCookie } from "next/dist/compiled/@edge-runtime/cookies"; import { RequestCookie } from "next/dist/compiled/@edge-runtime/cookies";
import { APIError, attemptAPIAction } from "@/util/api";
export async function tryCreateAttachment(request: Request) { export async function tryCreateAttachment(request: Request) {
// Make sure the DB is ready // Make sure the DB is ready
@ -127,6 +127,17 @@ export async function tryCreateAttachment(request: Request) {
); );
} }
/**
* @swagger
* /api/attachment:
* get:
* description: Returns all attachments
* responses:
* 200:
* description: idk
*/
export async function tryFetchAttachments(request: Request) { export async function tryFetchAttachments(request: Request) {
await Post.sync(); await Post.sync();

View File

@ -1,8 +1,8 @@
"use server"; "use server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { APIError, UserAuth, parseBasicAuth, getAssociatedUser } from "@/util";
import { Auth, User } from "@/models"; import { Auth, User } from "@/models";
import { APIError, getAssociatedUser, parseBasicAuth } from "@/util/api";
async function tryAuth(request: Request) { async function tryAuth(request: Request) {
// await User.sync(); // await User.sync();
@ -63,11 +63,12 @@ async function tryAuth(request: Request) {
export async function POST(request: Request) { export async function POST(request: Request) {
try { try {
return await tryAuth(request); return await tryAuth(request);
} catch (e) { } catch(e){
if (e instanceof APIError) { if (e instanceof APIError){
return new Response(e.info.responseText, { status: e.info.status }); return new Response(e.info.responseText,{status:e.info.status});
} else { }
throw e; else{
} throw e;
} }
}
} }

View File

@ -1,159 +1,189 @@
'use server' "use server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { APIError} from "@/util/api/error" import { APIError } from "@/util/api/error";
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user" import {
import { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms,dbSync,sequelize} from "@/models"; Attachment,
Auth,
Bucket,
DBState,
Post,
PostTag,
Project,
Tag,
User,
UserPerms,
dbSync,
sequelize,
} from "@/models";
import Sequelize, { CreationAttributes, DataTypes } from "@sequelize/core"; import Sequelize, { CreationAttributes, DataTypes } from "@sequelize/core";
import { SqliteColumnsDescription, SqliteDialect, SqliteQueryInterface } from "@sequelize/sqlite3"; import {
SqliteColumnsDescription,
SqliteDialect,
SqliteQueryInterface,
} from "@sequelize/sqlite3";
import { hashpassword } from "@/util/auth"; import { hashpassword } from "@/util/auth";
import { copyFile, readFileSync } from "fs"; import { copyFile, readFileSync } from "fs";
import path from "path"; import path from "path";
import { Attributes } from '@sequelize/core';
import { DeepPartial } from "@/util/DeepPartial";
import { UUID } from "crypto"; import { UUID } from "crypto";
import { runApiAction } from "@/util/api";
async function seedUsers(qif: SqliteQueryInterface<SqliteDialect>){ async function seedUsers(qif: SqliteQueryInterface<SqliteDialect>) {
const fp = path.resolve('./db/seed/users.json'); const fp = path.resolve("./db/seed/users.json");
const json: {users: CreationAttributes<User>[]} = JSON.parse(Buffer.from(readFileSync(fp).valueOf()).toString()); const json: { users: CreationAttributes<User>[] } = JSON.parse(
Buffer.from(readFileSync(fp).valueOf()).toString()
);
const users = json.users.map(async user=>{ const users = json.users.map(async (user) => {
user.password = await hashpassword(user.password); user.password = await hashpassword(user.password);
return user; return user;
}) });
const dbUsers = await User.bulkCreate(await Promise.all(users), {include: User.associations.perms}) const dbUsers = await User.bulkCreate(await Promise.all(users), {
include: User.associations.perms,
});
} }
async function seedPosts(qif: SqliteQueryInterface<SqliteDialect>){ async function seedPosts(qif: SqliteQueryInterface<SqliteDialect>) {
const fp = path.resolve('./db/seed/posts.json'); const fp = path.resolve("./db/seed/posts.json");
const json: {users: CreationAttributes<User>[]} = JSON.parse(Buffer.from(readFileSync(fp).valueOf()).toString()); const json: { users: CreationAttributes<User>[] } = JSON.parse(
const projects =[ Buffer.from(readFileSync(fp).valueOf()).toString()
{ );
"name": "Blog", const projects = [
"readableIdentifier": "blog", {
"posts": [ name: "Blog",
{ readableIdentifier: "blog",
"title": "Test Post", posts: [
"content": "# Hello <ExampleComponent />\nthis is some **test** markdown and we make some edits\n![](/attachment/788dfc19-55ba-482c-8124-277702296dfb/FB_IMG_1716665756868.jpg)", {
"description": "A new post to test the blog system", title: "Test Post",
"project_id": 1, content:
"user_id": 1, "# Hello <ExampleComponent />\nthis is some **test** markdown and we make some edits\n![](/attachment/788dfc19-55ba-482c-8124-277702296dfb/FB_IMG_1716665756868.jpg)",
"buckets": [ description: "A new post to test the blog system",
{ project_id: 1,
"id": "788dfc19-55ba-482c-8124-277702296dfb", user_id: 1,
"attachments": [ buckets: [
{ {
"filename": "FB_IMG_1716665756868.jpg" id: "788dfc19-55ba-482c-8124-277702296dfb",
} attachments: [
] {
} filename: "FB_IMG_1716665756868.jpg",
] },
} ],
] },
}] ],
projects.map(project=>{ },
Project.create({name: project.name, readableIdentifier: project.readableIdentifier}); ],
project.posts.map(async post=>{ },
const pst = await Post.create({title:post.title, content:post.content, description:post.description, project_id: post.project_id, user_id: post.user_id}); ];
post.buckets.map(async bucket=>{ projects.map((project) => {
pst.createBucket({id:bucket.id as UUID}); Project.create({
bucket.attachments.map(attachment=>{ name: project.name,
Attachment.create({bucket_id:bucket.id as UUID, filename: attachment.filename}).then((a)=>{ readableIdentifier: project.readableIdentifier,
copyFile( });
path.resolve('.','db','seed','post',a.filename), project.posts.map(async (post) => {
path.resolve('.','bucket',bucket.id,a.filename), const pst = await Post.create({
()=>{ title: post.title,
content: post.content,
description: post.description,
project_id: post.project_id,
user_id: post.user_id,
});
post.buckets.map(async (bucket) => {
pst.createBucket({ id: bucket.id as UUID });
bucket.attachments.map((attachment) => {
Attachment.create({
bucket_id: bucket.id as UUID,
filename: attachment.filename,
}).then((a) => {
copyFile(
path.resolve(".", "db", "seed", "post", a.filename),
path.resolve(".", "bucket", bucket.id, a.filename),
() => {}
);
});
});
});
});
});
} const project = await Project.findOne({
) where: {
}) readableIdentifier: "blog",
}) },
}) }).then((e) =>
}) e
}) ? e
: Project.create({
name: "General Blog",
readableIdentifier: "blog",
})
const project = await Project.findOne({where: { );
readableIdentifier: 'blog'
}}).then(e=> e ? e : Project.create({name:'General Blog',readableIdentifier:'blog'}));
} }
async function seedDatabase(qif: SqliteQueryInterface<SqliteDialect>) {
await seedUsers(qif);
await seedPosts(qif);
// await Post.create({
// title: 'Test Post',
// content: `
async function seedDatabase(qif:SqliteQueryInterface<SqliteDialect>){ // # Hello <ExampleComponent />
await seedUsers(qif); // this is some **test** markdown
await seedPosts(qif) // `,
// project_id: project.id,
// await Post.create({ // user_id: user.id
// title: 'Test Post', // })
// content: ` // await Post.create({
// # Hello <ExampleComponent /> // title: 'Test Post 2',
// this is some **test** markdown // content: `
// `, // # Hello <ExampleComponent />
// project_id: project.id, // this is amother post with some **test** markdown
// user_id: user.id // `,
// }) // project_id: project.id,
// await Post.create({ // user_id: user.id
// title: 'Test Post 2', // })
// content: `
// # Hello <ExampleComponent />
// this is amother post with some **test** markdown
// `,
// project_id: project.id,
// user_id: user.id
// })
} }
async function trySetup(request:Request){ export async function GET(request: Request) {
await dbSync; runApiAction(async (request) => {
await dbSync;
const queryInterface = sequelize.queryInterface const queryInterface = sequelize.queryInterface;
const version = (await DBState.findAll())
.sort((a, b) => (a.version > b.version ? 1 : -1))
.map((a) => a.version)[0];
const version = (await DBState.findAll()).sort((a,b)=> ((a.version > b.version) ? 1 : -1)).map(a=>a.version)[0]; switch (version) {
case 1:
break;
default:
seedDatabase(queryInterface);
const postsRows: SqliteColumnsDescription = await queryInterface
.describeTable(Post.table.tableName)
.then((t) => t);
if (!postsRows["project_id"])
queryInterface.addColumn("Posts", "project_id", {
type: DataTypes.INTEGER,
acceptsNull: () => false,
defaultValue: 1,
});
break;
}
return new Response(
JSON.stringify({
switch(version){ test: await queryInterface
case 1: .describeTable("Posts")
break; .then((t) => t),
default: }),
seedDatabase(queryInterface); {
const postsRows:SqliteColumnsDescription = await queryInterface.describeTable(Post.table.tableName).then(t=>t); status: 200,
if (!postsRows['project_id']) queryInterface.addColumn('Posts','project_id',{type: DataTypes.INTEGER, acceptsNull:()=>false,defaultValue:1}) headers: {
break; "Content-Type": "text/JSON",
} },
}
return new Response( );
JSON.stringify( }, request);
{
test: await queryInterface.describeTable('Posts').then(t=>t)
}),
{
status: 200,
headers:{
"Content-Type": "text/JSON"
}
}
);
}
export async function GET(request:Request){
try{
return await trySetup(request);
}
catch(e){
if (e instanceof APIError){
return new Response(e.info.responseText,{status:e.info.status});
}
else{
throw e;
}
}
} }

View File

@ -90,7 +90,7 @@ async function attemptGetUsers(request:Request){
); );
} }
export async function GET(request:Request){ export async function GET(request:Request): Promise<Response>{
try{ try{
return await attemptGetUsers(request); return await attemptGetUsers(request);
} }

View File

@ -12,7 +12,7 @@ import "@/app/index.css"
import { Post } from "@/models"; import { Post } from "@/models";
import { Attributes } from "@sequelize/core"; import { Attributes } from "@sequelize/core";
import { DeepPartial } from "@/util/DeepPartial"; import { DeepPartial } from "@/util/deeppartial";
async function getData(slug:string):Promise<Attributes<Post>> { async function getData(slug:string):Promise<Attributes<Post>> {

View File

@ -1,7 +1,7 @@
import './globals.css' import './globals.css'
import { Inter } from 'next/font/google' import { Inter } from 'next/font/google'
import StyledJsxRegistry from './registry'; import StyledJsxRegistry from './registry';
import Providers from '@/providers/providers'; import ContextProviders from '@/providers/contextproviders';
const inter = Inter({ subsets: ['latin'], fallback: ['system-ui', 'arial'] }) const inter = Inter({ subsets: ['latin'], fallback: ['system-ui', 'arial'] })
@ -17,7 +17,7 @@ export default function RootLayout({
}) { }) {
return ( return (
<html lang="en"> <html lang="en">
<body className={inter.className}><StyledJsxRegistry><Providers>{children}</Providers></StyledJsxRegistry></body> <body className={inter.className}><StyledJsxRegistry><ContextProviders>{children}</ContextProviders></StyledJsxRegistry></body>
</html> </html>
) )
} }

View File

@ -1,129 +1,154 @@
'use server' "use server";
import { constructAPIUrl } from "@/util/url"; import { constructAPIUrl } from "@/util/url";
import { cookies } from "next/headers" import { cookies } from "next/headers";
import { parseSetCookie } from "@/util/parseSetCookie"; import { parseSetCookie } from "@/util/cookies";
import makeFetchCookie from 'fetch-cookie'; import makeFetchCookie from "fetch-cookie";
import fetchCookie from "fetch-cookie"; import fetchCookie from "fetch-cookie";
import { Attribute, Attributes } from "@sequelize/core"; import { User, Auth } from "@/models";
import { User, Auth } from "@/models"; import { AuthContextProviderProps } from "@/providers";
import { AuthProps } from "@/providers/providers";
import { ActionResult } from "./ActionResult";
type LoginReturn = { type LoginReturn = {
cookie?:unknown, cookie?: unknown;
errorMessage?:string; errorMessage?: string;
};
async function attemptAPILogin(
method: string,
formData: FormData
): Promise<LoginReturn | 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 jar = new CookieJar();
const fetchWithCookie = makeFetchCookie(fetch, jar);
// Set Basic Auth
headers.set(
"Authorization",
`Basic ${Buffer.from(
`${formData.get("input_username")}:${formData.get(
"input_password"
)}`
).toString("base64")}`
);
let res = await fetchWithCookie(await constructAPIUrl("auth"), {
method: "POST",
credentials: "include",
headers: headers,
});
console.log(jar.store.idx["localhost"]["/"]);
let koek = res.headers.getSetCookie();
let cookieDict = parseSetCookie(koek);
await cookies().set("auth", cookieDict.auth);
return {
cookie: cookieDict.auth,
errorMessage: "",
};
// console.log(koek);
} }
async function attemptAPILogin(method:string,formData:FormData):Promise<LoginReturn|null> export async function serverAttemptAuthenticateUser(
{ _currentState: unknown,
// Check if form data is present with required fields, return null if not formData: FormData
if(!formData || !formData.get('input_username') || !formData.get('input_password')) return null; ): Promise<LoginReturn | null> {
try {
// Instantiate header object const signInStatus = await attemptAPILogin("credentials", formData);
let headers:Headers = new Headers(); return signInStatus;
} catch (error: any) {
// Prepare fetchCookie if (error) {
const { CookieJar, Cookie } = fetchCookie.toughCookie; console.log(error);
const jar = new CookieJar() switch (error.type) {
const fetchWithCookie = makeFetchCookie(fetch, jar); case "CredentialsSignin":
return { errorMessage: "invalidCredentials" };
// Set Basic Auth default:
headers.set('Authorization', `Basic ${Buffer.from(`${formData.get('input_username')}:${formData.get('input_password')}`).toString('base64')}`); return { errorMessage: "Something went wrong." };
let res = await fetchWithCookie(constructAPIUrl("auth"), { }
method:'POST', }
credentials: 'include', throw Error;
headers:headers, }
});
console.log(jar.store.idx['localhost']['/']);
let koek = res.headers.getSetCookie();
let cookieDict = parseSetCookie(koek);
await cookies().set('auth', cookieDict.auth);
return {
cookie:cookieDict.auth,
errorMessage:""
};
// console.log(koek);
} }
export async function serverAttemptAuthenticateUser(_currentState: unknown, formData: FormData):Promise<LoginReturn|null> export async function serverValidateSessionCookie(
{ koek: string
try { ): Promise<boolean> {
const signInStatus = await attemptAPILogin('credentials', formData) const validateSession = await fetch(
return signInStatus; await constructAPIUrl("auth/validate"),
} catch (error:any) { {
if (error) { method: "POST",
switch (error.type) { headers: {
case 'CredentialsSignin': return { errorMessage: 'invalidCredentials' }; Cookie: `auth=${koek};`,
default: return { errorMessage: 'Something went wrong.' }; },
} }
} );
throw Error if (validateSession.status == 200) return true;
} else return false;
} }
export async function serverValidateSessionCookie(koek:string):Promise<boolean> export async function userIsAdmin(): Promise<boolean> {
{ const cookieAuthValue = await cookies().get("auth")?.value;
const validateSession = await fetch(constructAPIUrl("auth/validate"),{ const cookieAuthSanitized = cookieAuthValue
method:"POST", ? JSON.parse(JSON.stringify(cookieAuthValue))
headers:{ : "";
Cookie: `auth=${koek};`
} if (!cookieAuthSanitized) return false;
}); const parsedAuth = JSON.parse(cookieAuthSanitized);
if(validateSession.status == 200)
return true if (!parsedAuth.id || !parsedAuth.token || !parsedAuth.user_id)
else return false;
return false
const p: AuthContextProviderProps = {
auth: {
id: parsedAuth.id,
token: parsedAuth.token,
user_id: parsedAuth.user_id,
},
};
const foundAuth = await Auth.findOne({ where: { id: p.auth?.id } });
if (!foundAuth || foundAuth.token != p.auth?.token) return false;
return true;
} }
export async function userIsAdmin():Promise<boolean> export async function getCookieAuth(): Promise<AuthContextProviderProps> {
{ const cookieAuthValue = await cookies().get("auth")?.value;
const cookieAuthValue = await cookies().get('auth')?.value; const cookieAuthSanitized = cookieAuthValue
const cookieAuthSanitized = cookieAuthValue? JSON.parse(JSON.stringify(cookieAuthValue)) : ""; ? JSON.parse(JSON.stringify(cookieAuthValue))
: "";
console.log("kanker koek");
if(!cookieAuthSanitized) return false; if (!cookieAuthSanitized) return {};
const parsedAuth = JSON.parse(cookieAuthSanitized);
if(!parsedAuth.id || !parsedAuth.token || !parsedAuth.user_id) return false const kd = JSON.parse(cookieAuthSanitized);
if (!kd.id || !kd.token || !kd.user_id) return {};
const p:AuthProps = { const foundAuth = await Auth.findOne({
auth: { where: { id: kd.id },
id:parsedAuth.id, include: { model: User },
token:parsedAuth.token, });
user_id:parsedAuth.user_id if (!foundAuth) return {};
} const authObject: AuthContextProviderProps = {
}; auth: {
id: kd.id,
const foundAuth = await Auth.findOne({where: { id: p.auth?.id}}); token: kd.token,
if(!foundAuth || foundAuth.token != p.auth?.token ) return false; user_id: kd.user_id,
},
return true; user: await foundAuth.user,
} };
return authObject;
export async function getCookieAuth():Promise<AuthProps>
{
const cookieAuthValue = await cookies().get('auth')?.value;
const cookieAuthSanitized = cookieAuthValue? JSON.parse(JSON.stringify(cookieAuthValue)) : "";
console.log("kanker koek")
if(!cookieAuthSanitized) return {}
const kd = JSON.parse(cookieAuthSanitized);
if(!kd.id || !kd.token || !kd.user_id) return {};
const foundAuth = await Auth.findOne({where: { id: kd.id},include:{model:User}});
if(!foundAuth) return {};
const authObject:AuthProps = {
auth: {
id:kd.id,
token:kd.token,
user_id:kd.user_id
},
user: await foundAuth.user
}
return authObject;
} }

View File

@ -7,7 +7,7 @@ import ReactDOM from "react";
import "/public/global.css" import "/public/global.css"
import "./index.css" import "./index.css"
import { Post } from "@/models"; import { Post } from "@/models";
import { constructAPIUrl } from "@/util"; import { constructAPIUrl } from "@/util/url";
import Link from "next/link"; import Link from "next/link";
import { Attributes } from "@sequelize/core"; import { Attributes } from "@sequelize/core";
@ -17,7 +17,7 @@ type DeepPartial<T> = T extends object ? {
export default async function Test() { export default async function Test() {
const response = await fetch(constructAPIUrl('post')); const response = await fetch(await constructAPIUrl('post'));
const articles:Array<Attributes<Post>> = await response.json(); const articles:Array<Attributes<Post>> = await response.json();

View File

@ -0,0 +1,14 @@
'use client';
import SwaggerUI from 'swagger-ui-react';
import 'swagger-ui-react/swagger-ui.css';
type Props = {
spec: Record<string, any>,
};
function ReactSwagger({ spec }: Props) {
return <SwaggerUI spec={spec} />;
}
export default ReactSwagger;

View File

@ -0,0 +1,15 @@
'use client'
import { AuthContext, AuthContextProviderProps } from "@/providers";
import { ReactNode } from "react"
type CAuthProviderProps = {
children?:ReactNode;
authProps:AuthContextProviderProps
}
export default function CAuthProvider(props:CAuthProviderProps){
return (
<AuthContext.Provider value={props.authProps}>{props.children}</AuthContext.Provider>
)
}

View File

@ -1,19 +1,10 @@
'use client' 'use client'
import { serverAttemptAuthenticateUser } from "@/app/lib/actions/actions"; import { serverAttemptAuthenticateUser } from "@/app/lib/actions/actions";
import { AuthContext } from "@/providers/providers";
import { constructAPIUrl } from "@/util/url";
import { createContext, useState } from "react";
import { useFormState, useFormStatus } from "react-dom"; import { useFormState, useFormStatus } from "react-dom";
// async function authenticate(){ export default function CLoginForm(){
// const url = await constructAPIUrl("auth");
// const auth = await fetch(url);
// }
export default function LoginForm(){
const [loginResult, dispatch] = useFormState(serverAttemptAuthenticateUser, undefined); const [loginResult, dispatch] = useFormState(serverAttemptAuthenticateUser, undefined);
return ( return (

View File

@ -6,9 +6,6 @@ import { EntityEditorTextArea } from '../input/EntityEditorTextArea';
import { import {
ChangeEventHandler, ChangeEventHandler,
MouseEventHandler, MouseEventHandler,
MutableRefObject,
useLayoutEffect,
useRef,
useState, useState,
} from "react"; } from "react";
import { import {

View File

@ -19,7 +19,7 @@ import {
} from "@/app/lib/actions/entityManagement/post/postActions"; } from "@/app/lib/actions/entityManagement/post/postActions";
import { PostViewProps } from "@/views/admin/post"; import { PostViewProps } from "@/views/admin/post";
import { aifa, StateHook } from "@/util/"; import { StateHook } from "@/util/";
export type PostTableStateProps = { export type PostTableStateProps = {
posts: StateHook<GetPostsAttributes[]>; posts: StateHook<GetPostsAttributes[]>;
@ -110,13 +110,10 @@ export default function PostTable({
: `${post.content.substring(0, 255)}...`} : `${post.content.substring(0, 255)}...`}
</td> </td>
<td key="project"> <td key="project">
{aifa( {projects.find((e) => {
projects.find((e) => { console.log(e.id);
console.log(e.id); return e.id == post.project.id;
return e.id == post.project.id; })?.readableIdentifier || "uncategorized"}
})?.readableIdentifier,
"uncategorized"
)}
</td> </td>
<td key="createdAt">{post.createdAt?.toString()}</td> <td key="createdAt">{post.createdAt?.toString()}</td>
<td key="updatedAt">{post.updatedAt?.toString()}</td> <td key="updatedAt">{post.updatedAt?.toString()}</td>

View File

@ -1,16 +0,0 @@
'use client'
import { AuthContext, AuthProps } from "@/providers/providers";
import { ReactNode, createContext } from "react"
type Props = {
children?:ReactNode;
authProps:AuthProps
}
export default function ClientAuthHandler(props:Props){
return (
<AuthContext.Provider value={props.authProps}>{props.children}</AuthContext.Provider>
)
}

View File

@ -5,7 +5,6 @@ import {
MutableRefObject, MutableRefObject,
useLayoutEffect, useLayoutEffect,
ChangeEventHandler, ChangeEventHandler,
useState,
} from "react"; } from "react";
import { StateHook } from "@/util"; import { StateHook } from "@/util";

View File

@ -1,43 +1,46 @@
'use server' "use server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import LoginForm from "@/components/client/admin/loginForm"; import CLoginForm from "@/components/client/admin/CLoginForm";
import ClientAuthHandler from "@/components/client/admin/clientAuthHandler"; import ClientAuthHandler from "@/components/client/admin/CAuthHandler";
import { serverValidateSessionCookie } from "@/app/lib/actions/actions"; import { serverValidateSessionCookie } from "@/app/lib/actions/actions";
import { ReactNode } from "react"; import { ReactNode } from "react";
import { AuthContext, AuthProps } from "@/providers/providers"; import { AuthContextProviderProps } from "@/providers";
interface Props { interface Props {
children?: ReactNode; children?: ReactNode;
params?: any; params?: any;
requiredRole?: number; requiredRole?: number;
} // We interfacing lads } // We interfacing lads
export default async function AuthHandler(props: Props) { export default async function AuthHandler(props: Props) {
const protoKoek = await cookies().get('auth')?.value; const protoKoek = await cookies().get("auth")?.value;
const koek = decodeURIComponent(protoKoek ? protoKoek: ""); const koek = decodeURIComponent(protoKoek ? protoKoek : "");
// console.log("koekje:" + koek) // console.log("koekje:" + koek)
let p:AuthProps = { let p: AuthContextProviderProps = {};
}; if (koek) {
if(koek){ const kd = JSON.parse(koek);
const kd = JSON.parse(koek); if (kd.id && kd.token && kd.user_id) {
if(kd.id && kd.token && kd.user_id){ p = {
p = { auth: {
auth: { id: kd.id,
id:kd.id, token: kd.token,
token:kd.token, user_id: kd.user_id,
user_id:kd.user_id },
} };
} }
} }
}
return ( return (
<div className="flex flex-row w-[100%] h-[100%]"> <div className="flex flex-row w-[100%] h-[100%]">
{!(koek && await serverValidateSessionCookie(koek)) ? <LoginForm>{ }</LoginForm> : <ClientAuthHandler authProps={p}>{props.children}</ClientAuthHandler>} {!(koek && (await serverValidateSessionCookie(koek))) ? (
</div> <CLoginForm>{}</CLoginForm>
); ) : (
<ClientAuthHandler authProps={p}>
{props.children}
</ClientAuthHandler>
)}
</div>
);
} }

View File

@ -1,10 +0,0 @@
'use client'
import 'bootstrap/dist/css/bootstrap.css';
import { useEffect } from 'react';
export default function Bootstrap(Component, children, pageProps){
useEffect(() => {
typeof document !== undefined ? require('bootstrap/dist/js/bootstrap') : null
}, []);
return <><div {...pageProps} /></>;
}

View File

@ -8,7 +8,7 @@ import Link from "next/link";
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
import { Router } from "next/router"; import { Router } from "next/router";
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { truncateString } from "@/util/utils"; import { truncateString } from "@/util/strings";
// @ts-ignore // @ts-ignore
import { MDXRemote } from "next-mdx-remote/rsc"; import { MDXRemote } from "next-mdx-remote/rsc";
import { ExampleComponent } from "./article"; import { ExampleComponent } from "./article";

View File

@ -7,7 +7,7 @@
margin-left:0px; margin-left:0px;
padding: 1px 5px 1px 5px; padding: 1px 5px 1px 5px;
border-radius: 20%; border-radius: 20%;
*display: inline; display: inline;
} }
.tagbar{ .tagbar{
margin: 0px 0px 0px 0px; margin: 0px 0px 0px 0px;

View File

@ -38,16 +38,14 @@ export function CPostView({
posts: parseStateHook(useState(posts)), posts: parseStateHook(useState(posts)),
editor: parseStateHook(useState(initEditorState)), editor: parseStateHook(useState(initEditorState)),
}; };
// render out the post table
return ( return (
<> <PostTable
<PostTable headings={headings}
headings={headings} posts={posts}
posts={posts} projects={projects}
projects={projects} actions={actions}
actions={actions} state={state}
state={state} ></PostTable>
></PostTable>
</>
); );
} }

View File

@ -30,14 +30,6 @@ export async function PostView(props: Props) {
"UpdatedAt", "UpdatedAt",
]; ];
const actions: PostServerActions = {
deletePost: deletePost,
getPosts: getPostsWithBucketsAndProject,
getProjects: getProjects,
savePost: updatePost,
// uploadAttachment:tryCreateAttachment
};
const posts: GetPostsAttributes[] | undefined = handleActionResult( const posts: GetPostsAttributes[] | undefined = handleActionResult(
await getPostsWithBucketsAndProject() await getPostsWithBucketsAndProject()
); );
@ -52,7 +44,12 @@ export async function PostView(props: Props) {
posts={posts ? posts : []} posts={posts ? posts : []}
projects={projects} projects={projects}
headings={headings} headings={headings}
actions={actions} actions={{
deletePost: deletePost,
getPosts: getPostsWithBucketsAndProject,
getProjects: getProjects,
savePost: updatePost
}}
/> />
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
cache: "no-store"; cache: "no-store";
import { ReactNode, useEffect } from "react"; import { ReactNode } from "react";
import EntityManagementTable from "../../../components/client/EntityManagementTable"; import EntityManagementTable from "../../../components/client/EntityManagementTable";
import PostTable from "@/components/client/admin/PostTable"; import PostTable from "@/components/client/admin/PostTable";
import { import {

View File

@ -1,4 +1,4 @@
//@ts-ignore // @ts-ignore
import { createSwaggerSpec } from "next-swagger-doc"; import { createSwaggerSpec } from "next-swagger-doc";
export const getApiDocs = async () => { export const getApiDocs = async () => {

View File

@ -10,10 +10,11 @@ import {
HasOne, HasOne,
UpdatedAt, UpdatedAt,
CreatedAt, CreatedAt,
Table,
} from '@sequelize/core/decorators-legacy'; } from '@sequelize/core/decorators-legacy';
import { SqliteDialect } from '@sequelize/sqlite3'; import { SqliteDialect } from '@sequelize/sqlite3';
@Table({tableName: "DBStates"})
export class DBState extends Model<InferAttributes<DBState>, InferCreationAttributes<DBState>>{ export class DBState extends Model<InferAttributes<DBState>, InferCreationAttributes<DBState>>{
@Attribute(DataTypes.INTEGER) @Attribute(DataTypes.INTEGER)

View File

@ -20,7 +20,7 @@ export class Project extends Model<
> { > {
@Attribute(DataTypes.INTEGER) @Attribute(DataTypes.INTEGER)
@PrimaryKey @PrimaryKey
@Unique // @Unique
@AutoIncrement @AutoIncrement
declare id: CreationOptional<number>; declare id: CreationOptional<number>;
@Attribute(DataTypes.STRING) @Attribute(DataTypes.STRING)

View File

@ -25,7 +25,7 @@ export * from './UserPerms';
export const sequelize = new Sequelize({ export const sequelize = new Sequelize({
dialect: SqliteDialect, dialect: SqliteDialect,
storage: 'db.sqlite', storage: './db/db.sqlite',
models: [Auth, Attachment, Bucket, DBState, Post, PostBucket, PostTag, Project, Tag, User, UserPerms], models: [Auth, Attachment, Bucket, DBState, Post, PostBucket, PostTag, Project, Tag, User, UserPerms],
}); });

View File

@ -0,0 +1,14 @@
import { Auth, User } from "@/models";
import { Attributes } from "@sequelize/core";
import { Context, createContext } from "react";
export type AuthContextProviderProps = {
auth?: Attributes<Auth>
user?: Attributes<User>
}
let p: AuthContextProviderProps = {}
export const AuthContext: Context<AuthContextProviderProps> = createContext(p);

View File

@ -3,23 +3,14 @@
import { Auth, User } from "@/models"; import { Auth, User } from "@/models";
import { ReactNode, createContext } from "react"; import { ReactNode, createContext } from "react";
import { Attributes, InferAttributes } from "@sequelize/core"; import { Attributes, InferAttributes } from "@sequelize/core";
import { AuthContext } from "./authprovider";
interface ProvidersProps {
export type AuthProps = {
auth?: Attributes<Auth>
user?: Attributes<User>
}
let p: AuthProps = {}
export const AuthContext = createContext(p);
interface Props {
children?: ReactNode; children?: ReactNode;
params?: any; params?: any;
} }
export default function Providers(props:Props){ export default function ContextProviders(props:ProvidersProps){
return ( return (
<AuthContext.Provider value={{}}>{props.children}</AuthContext.Provider> <AuthContext.Provider value={{}}>{props.children}</AuthContext.Provider>
) )

2
src/providers/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './authprovider';
export * from './contextproviders';

View File

@ -1,10 +1,9 @@
'server only'; 'server only';
// import Auth from "./Auth"; export async function getAPIEnv() {
export function getAPIEnv() {
return { return {
'schema': process.env.API_SCHEMA, 'schema': process.env.API_SCHEMA,
'host': process.env.API_HOST, 'host': process.env.API_HOST,
'port': process.env.API_PORT, 'port': process.env.API_PORT,
'basepath': process.env.API_BASEPATH 'basepath': process.env.API_BASEPATH,
}; };
} }

View File

@ -1,2 +1,4 @@
export * from './error'; export * from './error';
export * from './getAPIEnv';
export * from './runapiaction';
export * from './user'; export * from './user';

View File

@ -0,0 +1,19 @@
import { NextApiResponse } from "next";
import { APIError } from "./error";
export type APIRequest = (request:Request)=>Promise<Response>;
export async function runApiAction(c:APIRequest,request:Request){
try{
return await c(request);
}
catch(e){
if (e instanceof APIError){
return new Response(e.info.responseText,{status:e.info.status});
console.log("responded");
}
else{
throw e;
}
}
}

View File

@ -0,0 +1,2 @@
export * from './Cookies';
export * from './parseSetCookie';

View File

@ -1,10 +0,0 @@
class Gens{
public static loremipsum():String
{
return 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer egestas eros a imperdiet ultrices. Maecenas tincidunt tristique dolor, vitae dignissim ligula faucibus sit amet. Ut hendrerit elit eu elit molestie, vel consectetur leo accumsan. Phasellus ex mi, dignissim at aliquam at, rutrum eget mi. Curabitur pellentesque auctor nulla sed pulvinar. Maecenas scelerisque orci at sem finibus tincidunt. Mauris viverra pulvinar nibh. Etiam ornare purus leo, at cursus elit ornare nec. Suspendisse potenti. Sed nisl libero, sollicitudin vitae dignissim sit amet, laoreet sit amet odio. Duis rhoncus felis ut erat facilisis, vitae rutrum odio sollicitudin. Praesent et scelerisque eros. Praesent laoreet eu orci ut blandit. Morbi dapibus nibh urna, eget blandit quam aliquet vitae. Nulla quam metus, volutpat et vulputate vel, viverra sed diam.'
}
}
export default Gens;

View File

@ -1,10 +1,5 @@
export * from './api';
export * from './auth'; export * from './auth';
export * from './Cookies'; export * from './cookies';
export * from './DeepPartial'; export * from './deeppartial';
export * from './gens';
export * from './getAPIEnv';
export * from './parseSetCookie';
export * from './state'; export * from './state';
export * from './url'; export * from './strings';
export * from './utils';

View File

@ -0,0 +1,2 @@
export * from './loremipsum';
export * from './truncateString';

View File

@ -0,0 +1,6 @@
export function loremipsum():String
{
return 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer egestas eros a imperdiet ultrices. Maecenas tincidunt tristique dolor, vitae dignissim ligula faucibus sit amet. Ut hendrerit elit eu elit molestie, vel consectetur leo accumsan. Phasellus ex mi, dignissim at aliquam at, rutrum eget mi. Curabitur pellentesque auctor nulla sed pulvinar. Maecenas scelerisque orci at sem finibus tincidunt. Mauris viverra pulvinar nibh. Etiam ornare purus leo, at cursus elit ornare nec. Suspendisse potenti. Sed nisl libero, sollicitudin vitae dignissim sit amet, laoreet sit amet odio. Duis rhoncus felis ut erat facilisis, vitae rutrum odio sollicitudin. Praesent et scelerisque eros. Praesent laoreet eu orci ut blandit. Morbi dapibus nibh urna, eget blandit quam aliquet vitae. Nulla quam metus, volutpat et vulputate vel, viverra sed diam.'
}

View File

@ -0,0 +1,7 @@
export function truncateString(str:string = '', num:number = 255) {
if (str.length > num) {
return str.slice(0, num) + "...";
} else {
return str;
}
}

View File

@ -1,12 +1,12 @@
'use server' 'use server'
import { getAPIEnv } from "../getAPIEnv"; import { getAPIEnv } from "../api/getAPIEnv";
export function constructAPIUrl(endpoint:string){ export async function constructAPIUrl(endpoint:string){
const { schema, host, port, basepath } = getAPIEnv(); const { schema, host, port, basepath } = await getAPIEnv();
return `${schema}://${host}:${port}/${basepath}/${endpoint}` return `${schema}://${host}:${port}/${basepath}/${endpoint}`
} }
export function constructUrl(endpoint:string){ export async function constructUrl(endpoint:string){
const { schema, host, port, basepath } = getAPIEnv(); const { schema, host, port, basepath } = await getAPIEnv();
return `${schema}://${host}:${port}/${endpoint}` return `${schema}://${host}:${port}/${endpoint}`
}1 }1

View File

@ -1,14 +0,0 @@
'server only'
import { ReactNode } from "react";
import Gens from "./gens";
function truncateString(str:string = '', num:number = 255) {
if (str.length > num) {
return str.slice(0, num) + "...";
} else {
return str;
}
}
export { Gens, truncateString }
export const aifa = (a: ReactNode, b: ReactNode) => (a ? a : b);