diff --git a/db/.gitkeep b/db/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/db/seed/posts.json b/db/seed/posts.json new file mode 100644 index 0000000..a16ac35 --- /dev/null +++ b/db/seed/posts.json @@ -0,0 +1,29 @@ +{ + "projects": [ + { + "name": "Blog", + "readableIdentifier": "blog", + "posts": [ + { + "title": "Test Post", + "content": "# Hello \nthis is some **test** markdown and we make some edits\n![](/attachment/788dfc19-55ba-482c-8124-277702296dfb/FB_IMG_1716665756868.jpg)", + "description": "A new post to test the blog system", + "project_id": 1, + "user_id": 1, + "buckets": [ + { + "id": "788dfc19-55ba-482c-8124-277702296dfb", + "attachments": [ + { + "path": "FB_IMG_1716665756868.jpg" + },{ + "path": "patchnotes.jpg" + } + ] + } + ] + } + ] + } + ] +} diff --git a/db/seed/posts/FB_IMG_1716665756868.jpg b/db/seed/posts/FB_IMG_1716665756868.jpg new file mode 100644 index 0000000..3fb6d8e Binary files /dev/null and b/db/seed/posts/FB_IMG_1716665756868.jpg differ diff --git a/db/seed/posts/patchnotes.png b/db/seed/posts/patchnotes.png new file mode 100644 index 0000000..716a0fe Binary files /dev/null and b/db/seed/posts/patchnotes.png differ diff --git a/db/seed/users.json b/db/seed/users.json new file mode 100644 index 0000000..71ad0ef --- /dev/null +++ b/db/seed/users.json @@ -0,0 +1,21 @@ +{ + "users": [{ + "username": "admin", + "password": "changeme", + "perms": { + "isAdmin": true + } + },{ + "username": "notadmin", + "password": "changeme", + "perms": { + "isAdmin": false + } + },{ + "username": "theodore", + "password": "changeme", + "perms": { + "isAdmin": false + } + }] +} \ No newline at end of file diff --git a/src/app/api/setupDB/route.ts b/src/app/api/setupDB/route.ts index bb1bfe6..1063b23 100644 --- a/src/app/api/setupDB/route.ts +++ b/src/app/api/setupDB/route.ts @@ -5,38 +5,109 @@ import { cookies } from "next/headers"; import { APIError} from "@/util/api/error" import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user" import { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms,dbSync,sequelize} from "@/models"; -import Sequelize, { DataTypes } from "@sequelize/core"; +import Sequelize, { CreationAttributes, DataTypes } from "@sequelize/core"; import { SqliteColumnsDescription, SqliteDialect, SqliteQueryInterface } from "@sequelize/sqlite3"; import { hashPassword } from "@/util/Auth"; +import { copyFile, readFileSync } from "fs"; +import path from "path"; +import { Attributes } from '@sequelize/core'; +import { DeepPartial } from "@/util/DeepPartial"; +import { UUID } from "crypto"; +async function seedUsers(qif: SqliteQueryInterface){ + const fp = path.resolve('./db/seed/users.json'); + const json: {users: CreationAttributes[]} = JSON.parse(Buffer.from(readFileSync(fp).valueOf()).toString()); -async function seedDatabase(queryInterface:SqliteQueryInterface){ + const users = json.users.map(async user=>{ + user.password = await hashPassword(user.password); + return user; + }) - const password = await hashPassword('changeme'); + const dbUsers = await User.bulkCreate(await Promise.all(users), {include: User.associations.perms}) +} + +async function seedPosts(qif: SqliteQueryInterface){ + const fp = path.resolve('./db/seed/posts.json'); + const json: {users: CreationAttributes[]} = JSON.parse(Buffer.from(readFileSync(fp).valueOf()).toString()); + const projects =[ + { + "name": "Blog", + "readableIdentifier": "blog", + "posts": [ + { + "title": "Test Post", + "content": "# Hello \nthis is some **test** markdown and we make some edits\n![](/attachment/788dfc19-55ba-482c-8124-277702296dfb/FB_IMG_1716665756868.jpg)", + "description": "A new post to test the blog system", + "project_id": 1, + "user_id": 1, + "buckets": [ + { + "id": "788dfc19-55ba-482c-8124-277702296dfb", + "attachments": [ + { + "filename": "FB_IMG_1716665756868.jpg" + } + ] + } + ] + } + ] + }] + projects.map(project=>{ + Project.create({name: project.name, readableIdentifier: project.readableIdentifier}); + project.posts.map(async post=>{ + const pst = await Post.create({title:post.title, content:post.content, description:post.description, project_id: post.project_id, user_id: post.user_id}); + post.buckets.map(async bucket=>{ + pst.createBucket({id:bucket.id as UUID}); + bucket.attachments.map(attachment=>{ + Attachment.create({bucket_id:bucket.id as UUID, filename: attachment.filename}).then((a)=>{ + copyFile( + path.resolve('.','db','seed','post',a.filename), + path.resolve('.','bucket',bucket.id,a.filename), + ()=>{ + + } + ) + }) + }) + }) + }) + }) + + + + const project = await Project.findOne({where: { readableIdentifier: 'blog' }}).then(e=> e ? e : Project.create({name:'General Blog',readableIdentifier:'blog'})); - const user = await User.findOne({where: { - username: 'admin' - }}).then(e=> e ? e : User.create({username: 'admin', password: password, perms: {isAdmin: true}}, {include: User.associations.perms})); - await Post.create({ - title: 'Test Post', - content: ` - # Hello - this is some **test** markdown - `, - project_id: project.id, - user_id: user.id - }) - await Post.create({ - title: 'Test Post 2', - content: ` - # Hello - this is amother post with some **test** markdown - `, - project_id: project.id, - user_id: user.id - }) +} + + + + + +async function seedDatabase(qif:SqliteQueryInterface){ + await seedUsers(qif); + await seedPosts(qif) + + // await Post.create({ + // title: 'Test Post', + // content: ` + // # Hello + // this is some **test** markdown + // `, + // project_id: project.id, + // user_id: user.id + // }) + // await Post.create({ + // title: 'Test Post 2', + // content: ` + // # Hello + // this is amother post with some **test** markdown + // `, + // project_id: project.id, + // user_id: user.id + // }) } async function trySetup(request:Request){ diff --git a/src/app/attachment/[...slug]/route.ts b/src/app/attachment/[...slug]/route.ts index 299aabd..441a789 100644 --- a/src/app/attachment/[...slug]/route.ts +++ b/src/app/attachment/[...slug]/route.ts @@ -1,8 +1,30 @@ +import { Attachment, Bucket, dbSync } from "@/models"; import { open, openSync, readFileSync } from "fs"; import { NextRequest, NextResponse } from "next/server"; import path from "path"; -export async function GET(req:NextRequest, { params }: {params:{slug: string}}){ +export async function GET(req:NextRequest, { params }: {params:{slug: string[]}}){ + await dbSync; + const filename: `${string}${'.'}${string}` = params.slug[params.slug.length-1] as `${string}${'.'}${string}`; + const bucket = await Bucket.findOne({ + where: { + id:params.slug[0], + }, + include: [{ + association: Bucket.associations.attachments, + where: { + filename: params.slug[params.slug.length-1] + } + }] + },) + console.log(params.slug); + if(!bucket || !bucket.attachments || !bucket.attachments[0] || bucket.attachments[0].filename != params.slug[params.slug.length-1]){ + const headers = new Headers(); + headers.set("Content-Type", "application/json"); + return new NextResponse(JSON.stringify({ + error:"Access Denied" + }),{headers:headers}); + } const fp = path.resolve('.',`bucket`,...params.slug); return new Response(readFileSync(fp)); } \ No newline at end of file diff --git a/src/app/lib/actions/entityManagement/postActions.ts b/src/app/lib/actions/entityManagement/postActions.ts index 3eda9ff..ed881fb 100644 --- a/src/app/lib/actions/entityManagement/postActions.ts +++ b/src/app/lib/actions/entityManagement/postActions.ts @@ -70,4 +70,13 @@ export async function updatePost(postAttributes: Partial>): Pro if(! await userIsAdmin()) return {error:"Unauthorized, not updating Post."} const post = await Post.update(postAttributes, {where:{id:postAttributes.id}}); return {result:JSON.parse(JSON.stringify(post))}; -} \ No newline at end of file +} + +export type PostServerActions = { + deletePost: (id: number) => Promise>; + getPosts: () => Promise>; + getProjects: () => Promise[]>>; + savePost: ( + data: Partial> + ) => Promise[]>>; +}; diff --git a/src/components/client/admin/PostEditor.tsx b/src/components/client/admin/PostEditor.tsx index 7923ad6..3e27c66 100644 --- a/src/components/client/admin/PostEditor.tsx +++ b/src/components/client/admin/PostEditor.tsx @@ -2,6 +2,7 @@ import { GetPostsAttributes } from "@/app/lib/actions/entityManagement/postActio import { Post, Project, Bucket, PostBucket, Attachment } from "@/models"; import { Attributes } from "@sequelize/core"; import { UUID } from "crypto"; +import { EntityEditorTextArea } from '../input/EntityEditorTextArea'; import { ChangeEventHandler, MouseEventHandler, @@ -49,23 +50,6 @@ export default function PostEditor({ let [postProjectIDState, setPostProjectIDState] = useState( editorPost.project.id ); - let textbox: any = useRef(undefined); - - // Autosize the text area - function textAreaAutoSize( - textbox: MutableRefObject - ): 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)); - - // Handle user input on the text area by updating state and autosizing the textfield - const onTextAreaChange: ChangeEventHandler = (e) => { - setPostContentState(e.target.value); // Update State - textAreaAutoSize(textbox); // Autosize the text area - }; // Handle changing the selected project using the dropdown select const projectSelectionChange: ChangeEventHandler = (e) => @@ -102,14 +86,14 @@ export default function PostEditor({

Content

-