more refactoring
This commit is contained in:
parent
de148eaf19
commit
1971966099
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
@ -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";
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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";
|
||||||
|
|
||||||
|
|||||||
@ -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}
|
||||||
}
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
@ -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 };
|
|
||||||
@ -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
2
src/util/api/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './error';
|
||||||
|
export * from './user';
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
8
src/util/auth/hashpassword.ts
Normal file
8
src/util/auth/hashpassword.ts
Normal 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
2
src/util/auth/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './hashpassword';
|
||||||
|
export * from './validatepassword';
|
||||||
9
src/util/auth/validatepassword.ts
Normal file
9
src/util/auth/validatepassword.ts
Normal 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
10
src/util/getAPIEnv.ts
Normal 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
10
src/util/index.ts
Normal 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
2
src/util/state/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './statetypes';
|
||||||
|
export * from './stateutils';
|
||||||
@ -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>> = {};
|
||||||
|
|||||||
9
src/util/state/statetypes.ts
Normal file
9
src/util/state/statetypes.ts
Normal 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
1
src/util/url/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./urlConstructor"
|
||||||
12
src/util/url/urlConstructor.ts
Normal file
12
src/util/url/urlConstructor.ts
Normal 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
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user