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,161 +1,148 @@
'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 // Make sure the DB is ready
await dbSync; await dbSync;
// Prepare data // Prepare data
const formData = await request.formData(); const formData = await request.formData();
const requestData: string | Object | undefined = formData.get('data')?.valueOf(); const requestData: string | Object | undefined = formData
const files: FormDataEntryValue[] = formData.getAll('files') .get("data")
const authCkie = await cookies().get("auth"); ?.valueOf();
const files: FormDataEntryValue[] = formData.getAll("files");
const authCkie: RequestCookie | undefined = await cookies().get("auth");
// Sanity check auth cookie // Sanity check auth cookie
if (!authCkie || !authCkie.value) throw new APIError({ status: 500, responseText: "missing auth cookie" }); if (!authCkie || !authCkie.value)
throw new APIError({
status: 500,
responseText: "missing auth cookie",
});
// Get JSON from the Cookie // Get JSON from the Cookie
const cookieJSON = authCkie.value; const cookieJSON: string = authCkie.value;
const authObject = JSON.parse(cookieJSON); const authObject:Attributes<Auth> = JSON.parse(cookieJSON);
// Fetch User Auth from the database // Fetch User Auth from thse database
const auth = await Auth.findOne({ const auth = await Auth.findOne({
include: [ include: [
{ {
model: User.withScope(['withPerms']), model: User.withScope(["withPerms"]),
attributes: { attributes: {
exclude: ['username', 'password', 'updatedAt', 'createdAt'] exclude: ["username", "password", "updatedAt", "createdAt"],
} },
} },
], ],
where: { token: authObject.token } where: { token: authObject.token },
}); });
// Sanity check the auth and associated user for authorization // Sanity check the auth and associated user for authorization
if (!auth if (!auth || !auth.user)
|| !auth.user) throw new APIError({ status: 401, responseText: "Authentication Error" }); throw new APIError({
if (!auth.user.id) throw new APIError({ status: 401, responseText: "Missing user id" }); status: 401,
if (!auth.user.perms responseText: "Authentication Error",
|| !auth.user.perms.isAdmin) throw new APIError({ status: 401, responseText: `Unauthorized` }); });
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` });
// Handle incomplete data or other problems // Handle incomplete data or other problems
if (!files) throw new APIError({ status: 500, responseText: "Missing file" }); if (!files)
if (!formData) throw new APIError({ status: 500, responseText: "Empty request body" }); throw new APIError({ status: 500, responseText: "Missing file" });
if (!requestData) throw new APIError({ status: 500, responseText: "Missing request data" }); if (!formData)
if (!(typeof requestData == "string")) throw new APIError({ status: 500, responseText: "Malformed request data" }); 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 // Parse JSON
const data = JSON.parse(requestData); const data = JSON.parse(requestData);
// Get or create bucket // Get or create bucket
const bucket: Bucket = (data.postid && !data.bucketid) const bucket: Bucket =
data.postid && !data.bucketid
? await addToNewBucketForPost(data.postid) ? await addToNewBucketForPost(data.postid)
: await addToExistingBucket(data.bucketid); : await addToExistingBucket(data.bucketid);
// Write files to bucket and store as attachments in DB // Write files to bucket and store as attachments in DB
const writeResult = await writeFilesToFS(bucket.id, files); const writeResult = await writeFilesToFS(bucket.id, files);
// Handle failure // Handle failure
if (!writeResult.success) throw new APIError({ status: 500, responseText: "Error writing files to Bucket" }); if (!writeResult.success)
throw new APIError({
status: 500,
responseText: "Error writing files to Bucket",
});
// Write attachments to db // Write attachments to db
const attachments = ((writeResult).filePaths.map(async (fp) => { const attachments = writeResult.filePaths.map(async (fp) => {
return (await Attachment.create( return await Attachment.create(
{ {
bucket_id: bucket.id, bucket_id: bucket.id,
filename: fp filename: fp,
}, },
{ include: Attachment.associations.bucket } { include: Attachment.associations.bucket }
)); );
})) });
attachments; attachments;
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
bucket: await Bucket.findOne({ bucket: await Bucket.findOne({
where: { id: bucket.id }, where: { id: bucket.id },
include: [Bucket.associations.posts, Bucket.associations.attachments] include: [
}) Bucket.associations.posts,
Bucket.associations.attachments,
],
}),
}), }),
{ status: 200 } { 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({ const foundPosts = await Post.findAll({
include: [ include: [
{ {
association: Post.associations.user, association: Post.associations.user,
attributes: { exclude: ['password', 'createdAt', 'updatedAt'] } 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) {

View File

@ -1,14 +1,10 @@
'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) { async function tryAuth(request: Request) {
// await User.sync(); // await User.sync();
await Auth.sync(); await Auth.sync();
@ -21,50 +17,45 @@ async function tryAuth(request:Request){
const userAuth = parseBasicAuth(auth); const userAuth = parseBasicAuth(auth);
const user = await getAssociatedUser(userAuth); const user = await getAssociatedUser(userAuth);
if (!user || !user.id) if (!user || !user.id) return new Response("error", { status: 500 });
return new Response("error",{status:500});
const authentication = await Auth.create({ const authentication = await Auth.create({
user_id: user.id, user_id: user.id,
}) });
console.log('ok'); console.log("ok");
const foundAuth = await Auth.findOne({ const foundAuth = await Auth.findOne({
include: { include: {
model: User, model: User,
as: 'user' as: "user",
}, },
where: { where: {
user_id: user.id user_id: user.id,
} },
}) });
console.log('ok2'); console.log("ok2");
if(!foundAuth) if (!foundAuth) return new Response("error", { status: 500 });
return new Response("error",{status:500});
const usr = foundAuth.user; const usr = foundAuth.user;
const authUser = await authentication.getUser(); const authUser = await authentication.getUser();
// @ts-ignore // @ts-ignore
cookies().set('auth', JSON.stringify(authentication)); cookies().set("auth", JSON.stringify(authentication));
return new Response( return new Response(
JSON.stringify( JSON.stringify({
{
credentials: userAuth, credentials: userAuth,
auth: authentication, auth: authentication,
user: authUser, user: authUser,
foundAuth: foundAuth foundAuth: foundAuth,
}), }),
{ {
status: 200, status: 200,
headers: { headers: {
"Content-Type": "text/JSON" "Content-Type": "text/JSON",
} },
} }
); );
} }
@ -72,12 +63,10 @@ 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 {
else{
throw e; 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,17 +19,16 @@ 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,
headings, headings,
@ -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,11 +1,13 @@
'use client' "use client";
import { useRef, MutableRefObject, useLayoutEffect, ChangeEventHandler, useState } from "react"; import {
import { StateHook } from '../../../util/types/StateHook'; useRef,
MutableRefObject,
export function EntityEditorTextArea({contentHook, className}: {contentHook:StateHook<string>, className:string}){ useLayoutEffect,
ChangeEventHandler,
let textbox: any = useRef(undefined); useState,
} from "react";
import { StateHook } from "@/util";
// Autosize the text area // Autosize the text area
function textAreaAutoSize( function textAreaAutoSize(
@ -15,6 +17,18 @@ export function EntityEditorTextArea({contentHook, className}: {contentHook:Stat
textbox.current.style.height = "fit-content"; textbox.current.style.height = "fit-content";
textbox.current.style.height = `${textbox.current.scrollHeight}px`; textbox.current.style.height = `${textbox.current.scrollHeight}px`;
} }
type EntityEditorTextAreaProps = {
contentHook: StateHook<string>;
className: string;
};
export function EntityEditorTextArea({
contentHook,
className,
}: EntityEditorTextAreaProps) {
let textbox: any = useRef(undefined);
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,7 +37,8 @@ export function EntityEditorTextArea({contentHook, className}: {contentHook:Stat
textAreaAutoSize(textbox); // Autosize the text area textAreaAutoSize(textbox); // Autosize the text area
}; };
return <textarea return (
<textarea
key="input-content" key="input-content"
onChange={onTextAreaChange} onChange={onTextAreaChange}
ref={textbox} ref={textbox}
@ -31,4 +46,5 @@ export function EntityEditorTextArea({contentHook, className}: {contentHook:Stat
style={{ height: "100%" }} style={{ height: "100%" }}
className={className} 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,
"base64"
).toString("utf8");
var userAuth: UserAuth = { var userAuth: UserAuth = {
username: authString.split(":")[0] as any, username: authString.split(":")[0] as any,
password:authString.split(":")[1] as any password: authString.split(":")[1] as any,
}; };
return userAuth return userAuth;
} }
export type UserAuth = { export type UserAuth = {
username: string, username: string;
password: string password: string;
} };
export async function getAssociatedUser(auth:UserAuth) export async function getAssociatedUser(auth: UserAuth) {
{ let foundUser = await User.findOne({
let foundUser = await User.findOne({ attributes: {include: ['password']}, where: {username: auth.username} }); attributes: { include: ["password"] },
where: { username: auth.username },
});
if (!foundUser) if (!foundUser)
throw new APIError({status: 401, responseText:"Unauthorized: Invalid Username"}); throw new APIError({
status: 401,
responseText: "Unauthorized: Invalid Username",
});
if (!(await validatePassword(auth.password, foundUser.password))) if (!(await validatepassword(auth.password, foundUser.password)))
throw new APIError({status: 401, responseText:"Unauthorized: Invalid Password"}); throw new APIError({
status: 401,
responseText: "Unauthorized: Invalid Password",
});
return foundUser; 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";