Working state for attachments and buckets
This commit is contained in:
parent
e84ce38604
commit
c72ac5e67f
@ -4,19 +4,65 @@ import { APIError, attemptAPIAction } from "@/util/api/error";
|
||||
import { Auth, Post, PostTag, Tag, User } from "@/model/Models";
|
||||
import { cookies } from "next/headers";
|
||||
import { Attachment } from "@/model/Attachment";
|
||||
import { randomUUID } from "crypto";
|
||||
import { UUID, randomUUID } from "crypto";
|
||||
import { mkdir, mkdirSync, writeFile } from "fs";
|
||||
import { Bucket } from "@/model/Bucket";
|
||||
import { where } from "@sequelize/core";
|
||||
import { PostBucket } from "@/model/Post";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function writeFilesToBucket(uuid: UUID, files:any[]) {
|
||||
const fileArray:{name:string, content:Promise<ReadableStreamReadResult<Uint8Array>>|ReadableStreamReadResult<Uint8Array>}[] = await Promise.all( files.map((file:File) => {
|
||||
return {'name':( ()=>file.name)(), 'content':file.stream().getReader().read()};
|
||||
}));
|
||||
|
||||
let finalFileArray:{name:string,content:ReadableStreamReadResult<Uint8Array>}[] = await (async () => {
|
||||
for(let file in fileArray){
|
||||
fileArray[file].content = await fileArray[file].content
|
||||
}
|
||||
return [...fileArray as {name:string,content:ReadableStreamReadResult<Uint8Array>}[]]
|
||||
})() ;
|
||||
|
||||
mkdirSync(`./bucket/${uuid}/`)
|
||||
for(let file in finalFileArray){
|
||||
|
||||
writeFile(`./bucket/${uuid}/${finalFileArray[file].name}`,Buffer.from(finalFileArray[file].content.value as Uint8Array),(e)=>{console.log(e)})
|
||||
const attachment = await Attachment.create({bucket_id:uuid, filename:finalFileArray[file].name}, {include: Attachment.associations.bucket});
|
||||
console.log(attachment);
|
||||
}
|
||||
}
|
||||
|
||||
async function addToPost(postid:number):Promise<UUID>
|
||||
{
|
||||
const post = await Post.findOne({where: {id:postid}, include: {association: Post.associations.postBuckets}});
|
||||
if (!post) throw new APIError({ status: 500, responseText: "invalid postid" });
|
||||
const bucket = await Bucket.create({id:randomUUID()});
|
||||
const bucketPost = await PostBucket.create({bucketId: bucket.id, postId: postid})
|
||||
|
||||
console.log(bucketPost);
|
||||
return bucket.id
|
||||
}
|
||||
async function addToBucket(bucketid:number):Promise<UUID> {
|
||||
const bucket = await Bucket.findOne({where: {id: bucketid}});
|
||||
if (!bucket) throw new APIError({ status: 500, responseText: "invalid bucketid" });
|
||||
return bucket.id
|
||||
}
|
||||
|
||||
|
||||
async function tryCreateAttachment(request: Request) {
|
||||
|
||||
// Make sure the DB is ready
|
||||
await Attachment.sync();
|
||||
await Bucket.sync();
|
||||
await Post.sync();
|
||||
|
||||
|
||||
// Prepare data
|
||||
const formData = await request.formData();
|
||||
const requestData:string | Object | undefined = formData.get('data')?.valueOf();
|
||||
const files:FormDataEntryValue[] = formData.getAll('files')
|
||||
const authCkie = await cookies().get("auth");
|
||||
|
||||
// Sanity check auth cookie
|
||||
@ -39,46 +85,27 @@ async function tryCreateAttachment(request: Request) {
|
||||
where: { token: authObject.token }
|
||||
});
|
||||
|
||||
// Sanity check the auth and associated user
|
||||
|
||||
// Sanity check the auth and associated user for authorization
|
||||
if (!auth || !auth.user) throw new APIError({ status: 401, responseText: "Authentication Error" });
|
||||
|
||||
// Handle incomplete data or other problems
|
||||
if (!formData) throw new APIError({ status: 500, responseText: "Empty request body" });
|
||||
const files:any[] = formData.getAll('files')
|
||||
|
||||
if (!files) throw new APIError({ status: 500, responseText: "Missing file" });
|
||||
if (!auth.user.id) throw new APIError({ status: 500, responseText: "Missing user id" });
|
||||
if (!auth.user.perms || !auth.user.perms.isAdmin) throw new APIError({ status: 401, responseText: `Unauthorized ${JSON.stringify(auth.user)}` });
|
||||
|
||||
// peepee
|
||||
const uuid = randomUUID()
|
||||
const fileArray:{name:string, content:Promise<ReadableStreamReadResult<Uint8Array>>|ReadableStreamReadResult<Uint8Array>}[] = await Promise.all( files.map((file:File) => {
|
||||
return {'name':( ()=>file.name)(), 'content':file.stream().getReader().read()};
|
||||
}));
|
||||
|
||||
// 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" });
|
||||
|
||||
|
||||
let finalFileArray:{name:string,content:ReadableStreamReadResult<Uint8Array>}[] = await (async () => {
|
||||
for(let file in fileArray){
|
||||
fileArray[file].content = await fileArray[file].content
|
||||
}
|
||||
return [...fileArray as {name:string,content:ReadableStreamReadResult<Uint8Array>}[]]
|
||||
})() ;
|
||||
|
||||
mkdirSync(`./bucket/${uuid}/`)
|
||||
for(let file in finalFileArray){
|
||||
|
||||
writeFile(`./bucket/${uuid}/${finalFileArray[file].name}`,Buffer.from(finalFileArray[file].content.value as Uint8Array),(e)=>{console.log(e)})
|
||||
}
|
||||
// const kanker = files.map(parseFiles)
|
||||
|
||||
|
||||
const data = JSON.parse(requestData);
|
||||
|
||||
let uuid:UUID = (data.postid && !data.bucketid)? await addToPost(data.postid) : await addToBucket(data.bucketid)
|
||||
writeFilesToBucket(uuid, files);
|
||||
|
||||
// console.log(await kanker[0]);
|
||||
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
'files': fileArray,
|
||||
'files': 'ya yeet',
|
||||
'uuid': uuid,
|
||||
}), { status: 200 });
|
||||
|
||||
|
||||
@ -6,11 +6,42 @@ import { APIError} from "@/util/api/error"
|
||||
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
|
||||
import { Auth, Post, Tag, User } from "@/model/Models";
|
||||
import { Project } from "@/model/Project";
|
||||
import { Attachment } from "@/model/Attachment";
|
||||
import { Bucket } from "@/model/Bucket";
|
||||
import { DBState } from "@/model/DBState";
|
||||
import Sequelize, { DataTypes } from "@sequelize/core";
|
||||
import { SqliteColumnsDescription, SqliteDialect } from "@sequelize/sqlite3";
|
||||
import { SqliteColumnsDescription, SqliteDialect, SqliteQueryInterface } from "@sequelize/sqlite3";
|
||||
import { hashPassword } from "@/util/Auth";
|
||||
|
||||
|
||||
async function seedDatabase(queryInterface:SqliteQueryInterface<SqliteDialect>){
|
||||
const password = await hashPassword('changeme');
|
||||
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 <ExampleComponent />
|
||||
this is some **test** markdown
|
||||
`,
|
||||
project_id: project.id,
|
||||
user_id: user.id
|
||||
})
|
||||
await Post.create({
|
||||
title: 'Test Post 2',
|
||||
content: `
|
||||
# Hello <ExampleComponent />
|
||||
this is amother post with some **test** markdown
|
||||
`,
|
||||
project_id: project.id,
|
||||
user_id: user.id
|
||||
})
|
||||
}
|
||||
|
||||
async function trySetup(request:Request){
|
||||
|
||||
const sequelize = await new Sequelize({
|
||||
@ -21,27 +52,23 @@ async function trySetup(request:Request){
|
||||
// await User.sync();
|
||||
await Auth.sync();
|
||||
await User.sync();
|
||||
await Attachment.sync();
|
||||
await Bucket.sync();
|
||||
await Project.sync()
|
||||
await Tag.sync();
|
||||
await Post.sync();
|
||||
await DBState.sync();
|
||||
|
||||
const version = await (await DBState.findAll()).sort((a,b)=> ((a.version > b.version) ? 1 : -1)).map(a=>a.version)[0];
|
||||
const version = (await DBState.findAll()).sort((a,b)=> ((a.version > b.version) ? 1 : -1)).map(a=>a.version)[0];
|
||||
|
||||
await Project.findOne({where: {
|
||||
readableIdentifier: 'blog'
|
||||
}}).then(e=> e ? e : Project.create({name:'General Blog',readableIdentifier:'blog'}));
|
||||
await User.findOne({where: {
|
||||
username: 'admin'
|
||||
}}).then(e=> e ? e : User.create({username: 'admin', password: 'changeme', perms: {isAdmin: true}}));
|
||||
|
||||
|
||||
switch(version){
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
const postsRows:SqliteColumnsDescription = await queryInterface.describeTable('Posts').then(t=>t);
|
||||
|
||||
seedDatabase(queryInterface);
|
||||
const postsRows:SqliteColumnsDescription = await queryInterface.describeTable(Post.table.tableName).then(t=>t);
|
||||
if (!postsRows['project_id']) queryInterface.addColumn('Posts','project_id',{type: DataTypes.INTEGER, acceptsNull:()=>false,defaultValue:1})
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1,26 +1,30 @@
|
||||
import { Association, BelongsToGetAssociationMixin, BelongsToManyGetAssociationsMixin, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";
|
||||
import { Association, BelongsToGetAssociationMixin, BelongsToManyGetAssociationsMixin, CreationOptional, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";
|
||||
import { Post } from "./Post";
|
||||
|
||||
import { SqliteDialect } from '@sequelize/sqlite3';
|
||||
import { Attribute, AutoIncrement, BelongsTo, BelongsToMany, NotNull, PrimaryKey, Unique } from "@sequelize/core/decorators-legacy";
|
||||
import { Bucket } from "./Bucket";
|
||||
import { UUID } from "crypto";
|
||||
|
||||
export class Attachment extends Model<InferAttributes<Attachment>, InferCreationAttributes<Attachment>> {
|
||||
@PrimaryKey
|
||||
@AutoIncrement
|
||||
@Attribute(DataTypes.INTEGER)
|
||||
@Unique
|
||||
declare id: number;
|
||||
declare id: CreationOptional<number>;
|
||||
@Attribute(DataTypes.STRING)
|
||||
declare path: string
|
||||
declare filename: string
|
||||
|
||||
// Associations
|
||||
@Attribute(DataTypes.INTEGER)
|
||||
@Attribute(DataTypes.UUIDV4)
|
||||
@NotNull
|
||||
declare postid: number;
|
||||
@BelongsTo(()=>Post,{foreignKey: 'postid', inverse: {type: "hasMany", as: 'attachments'}})
|
||||
declare post?:NonAttribute<Post>;
|
||||
declare bucket_id: ForeignKey<Bucket['id']>;
|
||||
|
||||
@BelongsTo(()=>Bucket,{foreignKey: 'bucket_id', inverse: {type: "hasMany", as: 'attachments'}})
|
||||
declare bucket?:NonAttribute<Bucket>;
|
||||
|
||||
declare static associations: {
|
||||
posts: Association<Post, Attachment>;
|
||||
bucket: Association<Bucket, Attachment>;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
32
src/model/Bucket.ts
Normal file
32
src/model/Bucket.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { Association, BelongsToGetAssociationMixin, BelongsToManyGetAssociationsMixin, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize, sql } from "@sequelize/core";
|
||||
import { Post } from "./Post";
|
||||
|
||||
import { SqliteDialect } from '@sequelize/sqlite3';
|
||||
import { Attribute, AutoIncrement, BelongsTo, BelongsToMany, Default, NotNull, PrimaryKey, Unique } from "@sequelize/core/decorators-legacy";
|
||||
import { Attachment } from "./Attachment";
|
||||
import { UUID } from "crypto";
|
||||
|
||||
export class Bucket extends Model<InferAttributes<Bucket>, InferCreationAttributes<Bucket>> {
|
||||
@PrimaryKey
|
||||
@Unique
|
||||
@Attribute(DataTypes.UUIDV4)
|
||||
@Default(sql.uuidV4)
|
||||
declare id: UUID
|
||||
|
||||
/** Defined by {@link Post.buckets} */
|
||||
declare bucketPosts?:NonAttribute<Post>[];
|
||||
|
||||
/** Defined by {@link Attachment.bucket} */
|
||||
declare attachments?:NonAttribute<Attachment>[];
|
||||
|
||||
declare static associations: {
|
||||
bucketPosts: Association<Post, Bucket>;
|
||||
attachments: Association<Attachment, Bucket>;
|
||||
};
|
||||
}
|
||||
|
||||
const sequelize = new Sequelize({
|
||||
dialect: SqliteDialect,
|
||||
storage: 'db.sqlite',
|
||||
models: [Bucket]
|
||||
})
|
||||
@ -1,4 +1,4 @@
|
||||
import { Association, BelongsToGetAssociationMixin, CreationOptional, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";import { User } from "./User";
|
||||
import { Association, BelongsToGetAssociationMixin, BelongsToManyAssociation, BelongsToManyCreateAssociationMixin, CreationOptional, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";import { User } from "./User";
|
||||
|
||||
import { SqliteDialect } from '@sequelize/sqlite3';
|
||||
import { Attribute, AutoIncrement, BelongsTo, BelongsToMany, CreatedAt, Default, HasMany, NotNull, PrimaryKey, Unique, UpdatedAt } from "@sequelize/core/decorators-legacy";
|
||||
@ -6,6 +6,8 @@ import { Tag } from "./Tag";
|
||||
import { PostTag } from "./PostTag";
|
||||
import { Project } from "./Project";
|
||||
import { Attachment } from "./Attachment";
|
||||
import { Bucket } from "./Bucket";
|
||||
import { UUID } from "crypto";
|
||||
|
||||
export class Post extends Model<InferAttributes<Post>, InferCreationAttributes<Post>> {
|
||||
|
||||
@ -47,21 +49,29 @@ export class Post extends Model<InferAttributes<Post>, InferCreationAttributes<P
|
||||
@BelongsToMany(()=>Tag, { through: { model: ()=>PostTag, unique: false}, inverse: {as: 'taggedPosts'} })
|
||||
declare postTags?:NonAttribute<Tag[]>;
|
||||
|
||||
/** Defined by {@link Attachment.post} */
|
||||
declare Attachments:NonAttribute<Attachment>[];
|
||||
@BelongsToMany(()=>Bucket,{through: { model: ()=> PostBucket, unique: false}, inverse:{as: 'bucketPosts'}, foreignKey: 'postId', otherKey: 'bucketId'})
|
||||
declare postBuckets:NonAttribute<Bucket[]>;
|
||||
|
||||
|
||||
declare getUser: BelongsToGetAssociationMixin<User>;
|
||||
|
||||
|
||||
declare static associations: {
|
||||
user: Association<User, Post>;
|
||||
project: Association<Project, Post>;
|
||||
postBuckets: Association<Bucket, Post>
|
||||
postTags: Association<Tag, Post>;
|
||||
};
|
||||
}
|
||||
export class PostBucket extends Model<InferAttributes<PostBucket>, InferCreationAttributes<PostBucket>>{
|
||||
declare postId: number;
|
||||
declare bucketId: UUID;
|
||||
}
|
||||
|
||||
const sequelize = new Sequelize({
|
||||
dialect: SqliteDialect,
|
||||
storage: 'db.sqlite',
|
||||
models: [Post]
|
||||
})
|
||||
models: [Post, PostBucket]
|
||||
})
|
||||
|
||||
PostBucket.sync();
|
||||
Loading…
x
Reference in New Issue
Block a user