more refactoring

This commit is contained in:
Andreas 2024-07-02 09:08:49 +02:00
parent de148eaf19
commit 1971966099
27 changed files with 400 additions and 324 deletions

View File

@ -1,167 +1,154 @@
'use server' "use server";
import { APIError, attemptAPIAction } from "@/util/api/error"; import { APIError, attemptAPIAction } from "@/util";
import { sequelize, Bucket, Auth, Post, PostTag, Tag, User, dbSync } from "@/models"; import {
sequelize,
Bucket,
Auth,
Post,
PostTag,
Tag,
User,
dbSync,
} from "@/models";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { Attachment } from "@/models"; import { Attachment } from "@/models";
import { UUID } from "crypto"; import {
import { mkdir, mkdirSync, writeFile } from "fs"; addToExistingBucket,
import { where } from "@sequelize/core"; addToNewBucketForPost,
import { addToNewBucketForPost } from "../../lib/actions/entityManagement/attachment/attachmentActions"; writeFilesToFS,
} from "@/app/lib/actions/entityManagement/attachment/attachmentActions";
async function writeFilesToFS(uuid: UUID, files: any[]): Promise<{ success: boolean, filePaths: string[] }> { import { Attributes } from "@sequelize/core";
import { RequestCookie } from "next/dist/compiled/@edge-runtime/cookies";
type FileArrayEntryWithPromise = {
name: string;
content: Promise<ReadableStreamReadResult<Uint8Array>> | ReadableStreamReadResult<Uint8Array>
}
type FileArrayEntry = {
name: string;
content: ReadableStreamReadResult<Uint8Array>
};
// Resolve promises
const fileArray: FileArrayEntryWithPromise[] = await Promise.all(files.map((file: File) => {
return { 'name': (() => file.name)(), 'content': file.stream().getReader().read() };
}));
let finalFileArray: FileArrayEntry[] = await (
async () => {
for (let file in fileArray) {
fileArray[file].content = await fileArray[file].content
}
return [...fileArray as FileArrayEntry[]]
})();
// Make Bucket Directory
mkdirSync(`./bucket/${uuid}/`, { recursive: true })
// Write files to filesystem
for (let file in finalFileArray) {
writeFile(`./bucket/${uuid}/${finalFileArray[file].name}`
, Buffer.from(finalFileArray[file].content.value as Uint8Array)
, (e) => { console.log(e) });
}
return {
success: true,
filePaths: finalFileArray.map((e) => e.name)
}
}
async function addToExistingBucket(bucketid: number): Promise<Bucket> {
const bucket = await Bucket.findOne({
where: { id: bucketid }, include: { association: Bucket.associations.posts }
});
if (!bucket) throw new APIError({ status: 500, responseText: "invalid bucketid" });
return bucket;
}
export async function tryCreateAttachment(request: Request) { export async function tryCreateAttachment(request: Request) {
// Make sure the DB is ready
await dbSync;
// Make sure the DB is ready // Prepare data
await dbSync; const formData = await request.formData();
const requestData: string | Object | undefined = formData
.get("data")
?.valueOf();
const files: FormDataEntryValue[] = formData.getAll("files");
const authCkie: RequestCookie | undefined = await cookies().get("auth");
// Prepare data // Sanity check auth cookie
const formData = await request.formData(); if (!authCkie || !authCkie.value)
const requestData: string | Object | undefined = formData.get('data')?.valueOf(); throw new APIError({
const files: FormDataEntryValue[] = formData.getAll('files') status: 500,
const authCkie = await cookies().get("auth"); responseText: "missing auth cookie",
});
// Sanity check auth cookie // Get JSON from the Cookie
if (!authCkie || !authCkie.value) throw new APIError({ status: 500, responseText: "missing auth cookie" }); const cookieJSON: string = authCkie.value;
const authObject:Attributes<Auth> = JSON.parse(cookieJSON);
// Get JSON from the Cookie // Fetch User Auth from thse database
const cookieJSON = authCkie.value; const auth = await Auth.findOne({
const authObject = JSON.parse(cookieJSON); include: [
{
model: User.withScope(["withPerms"]),
attributes: {
exclude: ["username", "password", "updatedAt", "createdAt"],
},
},
],
where: { token: authObject.token },
});
// Fetch User Auth from the database // Sanity check the auth and associated user for authorization
const auth = await Auth.findOne({ if (!auth || !auth.user)
include: [ throw new APIError({
{ status: 401,
model: User.withScope(['withPerms']), responseText: "Authentication Error",
attributes: { });
exclude: ['username', 'password', 'updatedAt', 'createdAt'] if (!auth.user.id)
} throw new APIError({ status: 401, responseText: "Missing user id" });
} if (!auth.user.perms || !auth.user.perms.isAdmin)
], throw new APIError({ status: 401, responseText: `Unauthorized` });
where: { token: authObject.token } // Handle incomplete data or other problems
}); if (!files)
throw new APIError({ status: 500, responseText: "Missing file" });
if (!formData)
throw new APIError({ status: 500, responseText: "Empty request body" });
if (!requestData)
throw new APIError({
status: 500,
responseText: "Missing request data",
});
if (!(typeof requestData == "string"))
throw new APIError({
status: 500,
responseText: "Malformed request data",
});
// Parse JSON
const data = JSON.parse(requestData);
// Sanity check the auth and associated user for authorization // Get or create bucket
if (!auth const bucket: Bucket =
|| !auth.user) throw new APIError({ status: 401, responseText: "Authentication Error" }); data.postid && !data.bucketid
if (!auth.user.id) throw new APIError({ status: 401, responseText: "Missing user id" }); ? await addToNewBucketForPost(data.postid)
if (!auth.user.perms : await addToExistingBucket(data.bucketid);
|| !auth.user.perms.isAdmin) throw new APIError({ status: 401, responseText: `Unauthorized` });
// Handle incomplete data or other problems
if (!files) throw new APIError({ status: 500, responseText: "Missing file" });
if (!formData) throw new APIError({ status: 500, responseText: "Empty request body" });
if (!requestData) throw new APIError({ status: 500, responseText: "Missing request data" });
if (!(typeof requestData == "string")) throw new APIError({ status: 500, responseText: "Malformed request data" });
// Write files to bucket and store as attachments in DB
const writeResult = await writeFilesToFS(bucket.id, files);
// Handle failure
if (!writeResult.success)
throw new APIError({
status: 500,
responseText: "Error writing files to Bucket",
});
// Parse JSON // Write attachments to db
const data = JSON.parse(requestData); const attachments = writeResult.filePaths.map(async (fp) => {
return await Attachment.create(
{
bucket_id: bucket.id,
filename: fp,
},
{ include: Attachment.associations.bucket }
);
});
attachments;
// Get or create bucket return new Response(
const bucket: Bucket = (data.postid && !data.bucketid) JSON.stringify({
? await addToNewBucketForPost(data.postid) bucket: await Bucket.findOne({
: await addToExistingBucket(data.bucketid); where: { id: bucket.id },
include: [
// Write files to bucket and store as attachments in DB Bucket.associations.posts,
const writeResult = await writeFilesToFS(bucket.id, files); Bucket.associations.attachments,
// Handle failure ],
if (!writeResult.success) throw new APIError({ status: 500, responseText: "Error writing files to Bucket" }); }),
}),
// Write attachments to db { status: 200 }
const attachments = ((writeResult).filePaths.map(async (fp) => { );
return (await Attachment.create(
{
bucket_id: bucket.id,
filename: fp
},
{ include: Attachment.associations.bucket }
));
}))
attachments;
return new Response(
JSON.stringify({
bucket: await Bucket.findOne({
where: { id: bucket.id },
include: [Bucket.associations.posts, Bucket.associations.attachments]
})
}),
{ status: 200 }
);
} }
export async function tryFetchAttachments(request: Request) { export async function tryFetchAttachments(request: Request) {
await Post.sync();
await Post.sync(); const foundPosts = await Post.findAll({
include: [
const foundPosts = await Post.findAll({ {
include: [ association: Post.associations.user,
{ attributes: { exclude: ["password", "createdAt", "updatedAt"] },
association: Post.associations.user, },
attributes: { exclude: ['password', 'createdAt', 'updatedAt'] } {
}, { association: Post.associations.postTags,
association: Post.associations.postTags },
}] ],
}); });
return new Response(JSON.stringify(foundPosts), { status: 200 });
return new Response(JSON.stringify(foundPosts), { status: 200 });
} }
export async function GET(request: Request) { export async function GET(request: Request) {
return await attemptAPIAction(tryFetchAttachments, request); return await attemptAPIAction(tryFetchAttachments, request);
} }
export async function POST(request: Request) { export async function POST(request: Request) {
return await attemptAPIAction(tryCreateAttachment, request); return await attemptAPIAction(tryCreateAttachment, request);
} }

View File

@ -1,84 +1,73 @@
'use server' "use server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { APIError, UserAuth, parseBasicAuth, getAssociatedUser } from "@/util";
import { APIError} from "@/util/api/error"
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
import { Auth, User } from "@/models"; import { Auth, User } from "@/models";
async function tryAuth(request: Request) {
// await User.sync();
await Auth.sync();
async function tryAuth(request:Request){ const auth: string | null = request.headers.get("authorization");
// await User.sync(); if (!auth) {
await Auth.sync(); return new Response("unauthorized", { status: 403 });
}
const auth:string|null = request.headers.get("authorization"); const userAuth = parseBasicAuth(auth);
const user = await getAssociatedUser(userAuth);
if(!auth){ if (!user || !user.id) return new Response("error", { status: 500 });
return new Response("unauthorized",{status:403});
}
const userAuth = parseBasicAuth(auth); const authentication = await Auth.create({
const user = await getAssociatedUser(userAuth); user_id: user.id,
});
if (!user || !user.id)
return new Response("error",{status:500});
const authentication = await Auth.create({ console.log("ok");
user_id: user.id, const foundAuth = await Auth.findOne({
}) include: {
model: User,
as: "user",
},
where: {
user_id: user.id,
},
});
console.log("ok2");
console.log('ok'); if (!foundAuth) return new Response("error", { status: 500 });
const foundAuth = await Auth.findOne({
include: {
model: User,
as: 'user'
},
where: {
user_id: user.id
}
})
console.log('ok2');
if(!foundAuth) const usr = foundAuth.user;
return new Response("error",{status:500});
const usr = foundAuth.user;
const authUser = await authentication.getUser();
const authUser = await authentication.getUser(); // @ts-ignore
cookies().set("auth", JSON.stringify(authentication));
// @ts-ignore return new Response(
cookies().set('auth', JSON.stringify(authentication)); JSON.stringify({
credentials: userAuth,
return new Response( auth: authentication,
JSON.stringify( user: authUser,
{ foundAuth: foundAuth,
credentials: userAuth, }),
auth: authentication, {
user: authUser, status: 200,
foundAuth: foundAuth headers: {
}), "Content-Type": "text/JSON",
{ },
status: 200, }
headers:{ );
"Content-Type": "text/JSON"
}
}
);
} }
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

@ -7,7 +7,7 @@ import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
import { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms,dbSync,sequelize} from "@/models"; import { 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 { Attributes } from '@sequelize/core';
@ -19,7 +19,7 @@ async function seedUsers(qif: SqliteQueryInterface<SqliteDialect>){
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;
}) })

View File

@ -11,7 +11,7 @@
import { APIError } from "@/util/api/error"; import { APIError } from "@/util/api/error";
import { UserAuth } from "@/util/api/user"; import { UserAuth } from "@/util/api/user";
import { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms, addUserScopes, dbSync,sequelize} from "@/models"; import { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms, addUserScopes, dbSync,sequelize} from "@/models";
import { hashPassword } from "@/util/Auth"; import { hashpassword } from "@/util/auth";
// Attempt to register a new User // Attempt to register a new User
@ -32,7 +32,7 @@ async function attemptRegister(request:Request){
// Hash the password and create a new user in the database // Hash the password and create a new user in the database
const user = await User.create({ const user = await User.create({
username: requestBody.username, username: requestBody.username,
password: await hashPassword(requestBody.password), password: await hashpassword(requestBody.password),
perms:{ perms:{
isAdmin: false isAdmin: false
} }

View File

@ -1,6 +1,6 @@
'use server' 'use server'
import { constructAPIUrl } from "@/util/Utils" import { constructAPIUrl } from "@/util/url";
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';

View File

@ -1,13 +1,65 @@
'use server'; 'use server';
import { APIError } from "@/util/api/error"; import { APIError } from "@/util/api/error";
import { Bucket, Post } from "@/models"; import { Bucket, Post } from "@/models";
import { randomUUID } from "crypto"; import { randomUUID, UUID } from "crypto";
import { mkdirSync, writeFile } from "fs";
export async function addToNewBucketForPost(postid: number): Promise<Bucket> { export async function addToNewBucketForPost(postid: number): Promise<Bucket> {
Post.sync(); Post.sync();
const post = await Post.findOne({ where: { id: postid } }); const post: Post | null = await Post.findOne({ where: { id: postid } });
if (!post) throw new APIError({ status: 500, responseText: "invalid postid" }); if (!post) throw new APIError({ status: 500, responseText: "invalid postid" });
const bucket = await post.createBucket({ id: randomUUID() }); const bucket = await post.createBucket({ id: randomUUID() });
return bucket; return bucket;
} }
export async function writeFilesToFS(uuid: UUID, files: any[]): Promise<{ success: boolean, filePaths: string[] }> {
type FileArrayEntryWithPromise = {
name: string;
content: Promise<ReadableStreamReadResult<Uint8Array>> | ReadableStreamReadResult<Uint8Array>
}
type FileArrayEntry = {
name: string;
content: ReadableStreamReadResult<Uint8Array>
};
// Resolve promises
const fileArray: FileArrayEntryWithPromise[] = await Promise.all(files.map((file: File) => {
return { 'name': (() => file.name)(), 'content': file.stream().getReader().read() };
}));
let finalFileArray: FileArrayEntry[] = await (
async () => {
for (let file in fileArray) {
fileArray[file].content = await fileArray[file].content
}
return [...fileArray as FileArrayEntry[]]
})();
// Make Bucket Directory
mkdirSync(`./bucket/${uuid}/`, { recursive: true })
// Write files to filesystem
for (let file in finalFileArray) {
writeFile(`./bucket/${uuid}/${finalFileArray[file].name}`
, Buffer.from(finalFileArray[file].content.value as Uint8Array)
, (e) => { console.log(e) });
}
return {
success: true,
filePaths: finalFileArray.map((e) => e.name)
}
}
export async function addToExistingBucket(bucketid: number): Promise<Bucket> {
const bucket = await Bucket.findOne({
where: { id: bucketid },
include: { association: Bucket.associations.posts },
});
if (!bucket)
throw new APIError({ status: 500, responseText: "invalid bucketid" });
return bucket;
}

View File

@ -6,8 +6,8 @@ import ArticlePreview from "@/components/shared/news/article-preview"
import ReactDOM from "react"; import ReactDOM from "react";
import "/public/global.css" import "/public/global.css"
import "./index.css" import "./index.css"
import { Post } from "@/model/Post"; import { Post } from "@/models";
import { constructAPIUrl } from "@/util/Utils"; import { constructAPIUrl } from "@/util";
import Link from "next/link"; import Link from "next/link";
import { Attributes } from "@sequelize/core"; import { Attributes } from "@sequelize/core";

View File

@ -4,7 +4,13 @@ import EntityManagementTable from "../EntityManagementTable";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { EditorRenderer, EditorState, PostTableCallbacks } from "./PostEditor"; import { EditorRenderer, EditorState, PostTableCallbacks } from "./PostEditor";
import { Attributes, InferAttributes } from "@sequelize/core"; import { Attributes, InferAttributes } from "@sequelize/core";
import { Project, Post, Bucket, User, PostAttributesWithBuckets } from "@/models"; import {
Project,
Post,
Bucket,
User,
PostAttributesWithBuckets,
} from "@/models";
import { handleActionResult } from "@/app/lib/actions/clientActionHandler"; import { handleActionResult } from "@/app/lib/actions/clientActionHandler";
import { import {
@ -13,16 +19,15 @@ import {
PostServerActions, PostServerActions,
} from "@/app/lib/actions/entityManagement/post/postActions"; } from "@/app/lib/actions/entityManagement/post/postActions";
import { PostViewProps } from "@/views/admin/ClientPostView"; import { PostViewProps } from "@/views/admin/ClientPostView";
import { aifa } from "@/util/Utils"; import { aifa } from "@/util/utils";
import { StateHook } from "@/util/state/stateUtils"; import { StateHook } from "@/util/state";
export type PostTableStateProps = { export type PostTableStateProps = {
posts:StateHook<GetPostsAttributes[]>, posts: StateHook<GetPostsAttributes[]>;
editor:StateHook<EditorState> editor: StateHook<EditorState>;
} };
export type PostTableProps = PostViewProps & {state:PostTableStateProps};
export type PostTableProps = PostViewProps & { state: PostTableStateProps };
export default function PostTable({ export default function PostTable({
children, children,
@ -32,8 +37,6 @@ export default function PostTable({
actions, actions,
state, state,
}: PostTableProps) { }: PostTableProps) {
// Define editor controls // Define editor controls
const editorControls = { const editorControls = {
closeEditor: () => { closeEditor: () => {
@ -93,10 +96,7 @@ export default function PostTable({
}; };
return ( return (
<EntityManagementTable <EntityManagementTable entityName="Post" headings={headings}>
entityName="Post"
headings={headings}
>
{state.posts.state.map((post: GetPostsAttributes) => ( {state.posts.state.map((post: GetPostsAttributes) => (
<React.Fragment key={`postrow-${post.id}`}> <React.Fragment key={`postrow-${post.id}`}>
<tr> <tr>

View File

@ -2,7 +2,7 @@
import { serverAttemptAuthenticateUser } from "@/app/lib/actions/actions"; import { serverAttemptAuthenticateUser } from "@/app/lib/actions/actions";
import { AuthContext } from "@/providers/providers"; import { AuthContext } from "@/providers/providers";
import { constructAPIUrl } from "@/util/Utils"; import { constructAPIUrl } from "@/util/url";
import { createContext, useState } from "react"; import { createContext, useState } from "react";
import { useFormState, useFormStatus } from "react-dom"; import { useFormState, useFormStatus } from "react-dom";

View File

@ -1,20 +1,34 @@
'use client' "use client";
import { useRef, MutableRefObject, useLayoutEffect, ChangeEventHandler, useState } from "react"; import {
import { StateHook } from '../../../util/types/StateHook'; useRef,
MutableRefObject,
useLayoutEffect,
ChangeEventHandler,
useState,
} from "react";
import { StateHook } from "@/util";
export function EntityEditorTextArea({contentHook, className}: {contentHook:StateHook<string>, className:string}){ // Autosize the text area
function textAreaAutoSize(
textbox: MutableRefObject<HTMLTextAreaElement>
): void {
if (!textbox.current || !textbox.current.style) return;
textbox.current.style.height = "fit-content";
textbox.current.style.height = `${textbox.current.scrollHeight}px`;
}
let textbox: any = useRef(undefined); type EntityEditorTextAreaProps = {
contentHook: StateHook<string>;
className: string;
};
export function EntityEditorTextArea({
contentHook,
className,
}: EntityEditorTextAreaProps) {
let textbox: any = useRef(undefined);
// Autosize the text area
function textAreaAutoSize(
textbox: MutableRefObject<HTMLTextAreaElement>
): void {
if (!textbox.current || !textbox.current.style) return;
textbox.current.style.height = "fit-content";
textbox.current.style.height = `${textbox.current.scrollHeight}px`;
}
useLayoutEffect(() => textAreaAutoSize(textbox)); useLayoutEffect(() => textAreaAutoSize(textbox));
// Handle user input on the text area by updating state and autosizing the textfield // Handle user input on the text area by updating state and autosizing the textfield
@ -23,12 +37,14 @@ export function EntityEditorTextArea({contentHook, className}: {contentHook:Stat
textAreaAutoSize(textbox); // Autosize the text area textAreaAutoSize(textbox); // Autosize the text area
}; };
return <textarea return (
key="input-content" <textarea
onChange={onTextAreaChange} key="input-content"
ref={textbox} onChange={onTextAreaChange}
value={contentHook?.state} ref={textbox}
style={{ height: "100%" }} value={contentHook?.state}
className={className} style={{ height: "100%" }}
/> className={className}
} />
);
}

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/utils";
// @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

@ -1,16 +0,0 @@
'server only'
import { hash, compare } from "bcrypt";
export async function validatePassword(password:string, hashString:string){
const result = await compare(password, hashString);
return result;
}
export async function hashPassword(password:string){
const hashString = await hash(password, 10);
return hashString;
}
export default { validatePassword, hashPassword };

View File

@ -1,25 +1,6 @@
'server only' 'server only'
import { ReactNode } from "react"; import { ReactNode } from "react";
import Gens from "./gens"; import Gens from "./gens";
// import Auth from "./Auth";
function getAPIEnv(){
return {
'schema': process.env.API_SCHEMA,
'host': process.env.API_HOST,
'port': process.env.API_PORT,
'basepath': process.env.API_BASEPATH
};
}
function constructAPIUrl(endpoint:string){
const { schema, host, port, basepath } = getAPIEnv();
return `${schema}://${host}:${port}/${basepath}/${endpoint}`
}
function constructUrl(endpoint:string){
const { schema, host, port, basepath } = getAPIEnv();
return `${schema}://${host}:${port}/${endpoint}`
}
function truncateString(str:string = '', num:number = 255) { function truncateString(str:string = '', num:number = 255) {
if (str.length > num) { if (str.length > num) {
return str.slice(0, num) + "..."; return str.slice(0, num) + "...";
@ -29,5 +10,5 @@ function truncateString(str:string = '', num:number = 255) {
} }
export { Gens, constructAPIUrl, constructUrl, truncateString } export { Gens, truncateString }
export const aifa = (a: ReactNode, b: ReactNode) => (a ? a : b); export const aifa = (a: ReactNode, b: ReactNode) => (a ? a : b);

2
src/util/api/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './error';
export * from './user';

View File

@ -1,34 +1,44 @@
// import { MUser } from "@/model/sequelize/User"; // import { MUser } from "@/model/sequelize/User";
// 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 "@/util/api/error"; import { APIError } from "@/util/api/error";
import { User } from "@/models"; import { User } from "@/models";
export function parseBasicAuth(authb64:string):UserAuth export function parseBasicAuth(authb64: string): UserAuth {
{ const authString: string = Buffer.from(
const authString:string = Buffer.from(authb64.split(" ")[1] as any, "base64").toString("utf8"); authb64.split(" ")[1] as any,
var userAuth:UserAuth = { "base64"
username:authString.split(":")[0] as any, ).toString("utf8");
password:authString.split(":")[1] as any var userAuth: UserAuth = {
}; username: authString.split(":")[0] as any,
return userAuth password: authString.split(":")[1] as any,
};
return userAuth;
} }
export type UserAuth = { export type UserAuth = {
username: string, username: string;
password: string password: string;
};
export async function getAssociatedUser(auth: UserAuth) {
let foundUser = await User.findOne({
attributes: { include: ["password"] },
where: { username: auth.username },
});
if (!foundUser)
throw new APIError({
status: 401,
responseText: "Unauthorized: Invalid Username",
});
if (!(await validatepassword(auth.password, foundUser.password)))
throw new APIError({
status: 401,
responseText: "Unauthorized: Invalid Password",
});
return foundUser;
} }
export async function getAssociatedUser(auth:UserAuth)
{
let foundUser = await User.findOne({ attributes: {include: ['password']}, where: {username: auth.username} });
if (!foundUser)
throw new APIError({status: 401, responseText:"Unauthorized: Invalid Username"});
if (!(await validatePassword(auth.password, foundUser.password)))
throw new APIError({status: 401, responseText:"Unauthorized: Invalid Password"});
return foundUser;
}

View File

@ -0,0 +1,8 @@
'server only'
import { hash } from "bcrypt";
export async function hashpassword(password:string){
const hashString = await hash(password, 10);
return hashString;
}

2
src/util/auth/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './hashpassword';
export * from './validatepassword';

View File

@ -0,0 +1,9 @@
'server only';
import { compare } from "bcrypt";
export async function validatepassword(password: string, hashString: string) {
const result = await compare(password, hashString);
return result;
}

10
src/util/getAPIEnv.ts Normal file
View File

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

10
src/util/index.ts Normal file
View File

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

2
src/util/state/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './statetypes';
export * from './stateutils';

View File

@ -1,12 +1,4 @@
import { Dispatch, SetStateAction } from "react"; import { StateHookArray, StateHook } from "./statetypes";
export type StateHook<T> = {
state: T;
setState: Dispatch<SetStateAction<T>>;
};
export type StateHookArray<T> = [
T,Dispatch<SetStateAction<T>>
]
export function parseStateHook<T>(hook:StateHookArray<T>):StateHook<T>{ export function parseStateHook<T>(hook:StateHookArray<T>):StateHook<T>{
const stateHook:Partial<StateHook<T>> = {}; const stateHook:Partial<StateHook<T>> = {};

View File

@ -0,0 +1,9 @@
import { Dispatch, SetStateAction } from "react";
export type StateHook<T> = {
state: T;
setState: Dispatch<SetStateAction<T>>;
};
export type StateHookArray<T> = [
T,Dispatch<SetStateAction<T>>
]

1
src/util/url/index.ts Normal file
View File

@ -0,0 +1 @@
export * from "./urlConstructor"

View File

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

View File

@ -11,7 +11,7 @@ import { EditorState } from "@/components/client/admin/PostEditor";
import { Project } from "@/models"; import { Project } from "@/models";
import { Dispatch, ReactNode, SetStateAction, useState } from "react"; import { Dispatch, ReactNode, SetStateAction, useState } from "react";
import { Attributes } from "@sequelize/core"; import { Attributes } from "@sequelize/core";
import { parseStateHook, StateHookArray } from "@/util/state/stateUtils"; import { parseStateHook } from "@/util/state";
export type PostViewProps = { export type PostViewProps = {
children?: ReactNode; children?: ReactNode;

View File

@ -1,7 +1,7 @@
cache: "no-store"; cache: "no-store";
import { tryFetchPosts } from "@/app/api/post/route"; import { tryFetchPosts } from "@/app/api/post/route";
import { constructAPIUrl } from "@/util/Utils"; import { constructAPIUrl } from "@/util/utils";
import { ReactNode, useEffect } from "react"; import { ReactNode, useEffect } 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";