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 { Auth, Post, PostTag, Tag, User } from "@/model/Models";
|
||||||
import { cookies } from "next/headers";
|
import { cookies } from "next/headers";
|
||||||
import { Attachment } from "@/model/Attachment";
|
import { Attachment } from "@/model/Attachment";
|
||||||
import { randomUUID } from "crypto";
|
import { UUID, randomUUID } from "crypto";
|
||||||
import { mkdir, mkdirSync, writeFile } from "fs";
|
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) {
|
async function tryCreateAttachment(request: Request) {
|
||||||
|
|
||||||
// Make sure the DB is ready
|
// Make sure the DB is ready
|
||||||
await Attachment.sync();
|
await Attachment.sync();
|
||||||
|
await Bucket.sync();
|
||||||
await Post.sync();
|
await Post.sync();
|
||||||
|
|
||||||
// Prepare data
|
// Prepare data
|
||||||
const formData = await request.formData();
|
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");
|
const authCkie = await cookies().get("auth");
|
||||||
|
|
||||||
// Sanity check auth cookie
|
// Sanity check auth cookie
|
||||||
@ -39,46 +85,27 @@ async function tryCreateAttachment(request: Request) {
|
|||||||
where: { token: authObject.token }
|
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" });
|
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.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)}` });
|
if (!auth.user.perms || !auth.user.perms.isAdmin) throw new APIError({ status: 401, responseText: `Unauthorized ${JSON.stringify(auth.user)}` });
|
||||||
|
// Handle incomplete data or other problems
|
||||||
// peepee
|
if (!files) throw new APIError({ status: 500, responseText: "Missing file" });
|
||||||
const uuid = randomUUID()
|
if (!formData) throw new APIError({ status: 500, responseText: "Empty request body" });
|
||||||
const fileArray:{name:string, content:Promise<ReadableStreamReadResult<Uint8Array>>|ReadableStreamReadResult<Uint8Array>}[] = await Promise.all( files.map((file:File) => {
|
if (!requestData) throw new APIError({ status: 500, responseText: "Missing request data" });
|
||||||
return {'name':( ()=>file.name)(), 'content':file.stream().getReader().read()};
|
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({
|
return new Response(JSON.stringify({
|
||||||
'files': fileArray,
|
'files': 'ya yeet',
|
||||||
'uuid': uuid,
|
'uuid': uuid,
|
||||||
}), { status: 200 });
|
}), { status: 200 });
|
||||||
|
|
||||||
|
|||||||
@ -6,11 +6,42 @@ import { APIError} from "@/util/api/error"
|
|||||||
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
|
import { UserAuth, parseBasicAuth, getAssociatedUser } from "@/util/api/user"
|
||||||
import { Auth, Post, Tag, User } from "@/model/Models";
|
import { Auth, Post, Tag, User } from "@/model/Models";
|
||||||
import { Project } from "@/model/Project";
|
import { Project } from "@/model/Project";
|
||||||
|
import { Attachment } from "@/model/Attachment";
|
||||||
|
import { Bucket } from "@/model/Bucket";
|
||||||
import { DBState } from "@/model/DBState";
|
import { DBState } from "@/model/DBState";
|
||||||
import Sequelize, { DataTypes } from "@sequelize/core";
|
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){
|
async function trySetup(request:Request){
|
||||||
|
|
||||||
const sequelize = await new Sequelize({
|
const sequelize = await new Sequelize({
|
||||||
@ -21,27 +52,23 @@ async function trySetup(request:Request){
|
|||||||
// await User.sync();
|
// await User.sync();
|
||||||
await Auth.sync();
|
await Auth.sync();
|
||||||
await User.sync();
|
await User.sync();
|
||||||
|
await Attachment.sync();
|
||||||
|
await Bucket.sync();
|
||||||
await Project.sync()
|
await Project.sync()
|
||||||
await Tag.sync();
|
await Tag.sync();
|
||||||
await Post.sync();
|
await Post.sync();
|
||||||
await DBState.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){
|
switch(version){
|
||||||
case 1:
|
case 1:
|
||||||
break;
|
break;
|
||||||
default:
|
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})
|
if (!postsRows['project_id']) queryInterface.addColumn('Posts','project_id',{type: DataTypes.INTEGER, acceptsNull:()=>false,defaultValue:1})
|
||||||
break;
|
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 { Post } from "./Post";
|
||||||
|
|
||||||
import { SqliteDialect } from '@sequelize/sqlite3';
|
import { SqliteDialect } from '@sequelize/sqlite3';
|
||||||
import { Attribute, AutoIncrement, BelongsTo, BelongsToMany, NotNull, PrimaryKey, Unique } from "@sequelize/core/decorators-legacy";
|
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>> {
|
export class Attachment extends Model<InferAttributes<Attachment>, InferCreationAttributes<Attachment>> {
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
@AutoIncrement
|
@AutoIncrement
|
||||||
@Attribute(DataTypes.INTEGER)
|
@Attribute(DataTypes.INTEGER)
|
||||||
@Unique
|
@Unique
|
||||||
declare id: number;
|
declare id: CreationOptional<number>;
|
||||||
@Attribute(DataTypes.STRING)
|
@Attribute(DataTypes.STRING)
|
||||||
declare path: string
|
declare filename: string
|
||||||
|
|
||||||
// Associations
|
// Associations
|
||||||
@Attribute(DataTypes.INTEGER)
|
@Attribute(DataTypes.UUIDV4)
|
||||||
@NotNull
|
@NotNull
|
||||||
declare postid: number;
|
declare bucket_id: ForeignKey<Bucket['id']>;
|
||||||
@BelongsTo(()=>Post,{foreignKey: 'postid', inverse: {type: "hasMany", as: 'attachments'}})
|
|
||||||
declare post?:NonAttribute<Post>;
|
@BelongsTo(()=>Bucket,{foreignKey: 'bucket_id', inverse: {type: "hasMany", as: 'attachments'}})
|
||||||
|
declare bucket?:NonAttribute<Bucket>;
|
||||||
|
|
||||||
declare static associations: {
|
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 { SqliteDialect } from '@sequelize/sqlite3';
|
||||||
import { Attribute, AutoIncrement, BelongsTo, BelongsToMany, CreatedAt, Default, HasMany, NotNull, PrimaryKey, Unique, UpdatedAt } from "@sequelize/core/decorators-legacy";
|
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 { PostTag } from "./PostTag";
|
||||||
import { Project } from "./Project";
|
import { Project } from "./Project";
|
||||||
import { Attachment } from "./Attachment";
|
import { Attachment } from "./Attachment";
|
||||||
|
import { Bucket } from "./Bucket";
|
||||||
|
import { UUID } from "crypto";
|
||||||
|
|
||||||
export class Post extends Model<InferAttributes<Post>, InferCreationAttributes<Post>> {
|
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'} })
|
@BelongsToMany(()=>Tag, { through: { model: ()=>PostTag, unique: false}, inverse: {as: 'taggedPosts'} })
|
||||||
declare postTags?:NonAttribute<Tag[]>;
|
declare postTags?:NonAttribute<Tag[]>;
|
||||||
|
|
||||||
/** Defined by {@link Attachment.post} */
|
@BelongsToMany(()=>Bucket,{through: { model: ()=> PostBucket, unique: false}, inverse:{as: 'bucketPosts'}, foreignKey: 'postId', otherKey: 'bucketId'})
|
||||||
declare Attachments:NonAttribute<Attachment>[];
|
declare postBuckets:NonAttribute<Bucket[]>;
|
||||||
|
|
||||||
|
|
||||||
declare getUser: BelongsToGetAssociationMixin<User>;
|
declare getUser: BelongsToGetAssociationMixin<User>;
|
||||||
|
|
||||||
|
|
||||||
declare static associations: {
|
declare static associations: {
|
||||||
user: Association<User, Post>;
|
user: Association<User, Post>;
|
||||||
project: Association<Project, Post>;
|
project: Association<Project, Post>;
|
||||||
|
postBuckets: Association<Bucket, Post>
|
||||||
postTags: Association<Tag, Post>;
|
postTags: Association<Tag, Post>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
export class PostBucket extends Model<InferAttributes<PostBucket>, InferCreationAttributes<PostBucket>>{
|
||||||
|
declare postId: number;
|
||||||
|
declare bucketId: UUID;
|
||||||
|
}
|
||||||
|
|
||||||
const sequelize = new Sequelize({
|
const sequelize = new Sequelize({
|
||||||
dialect: SqliteDialect,
|
dialect: SqliteDialect,
|
||||||
storage: 'db.sqlite',
|
storage: 'db.sqlite',
|
||||||
models: [Post]
|
models: [Post, PostBucket]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
PostBucket.sync();
|
||||||
Loading…
x
Reference in New Issue
Block a user