From 21e31d0ee475324de69a210e2bccd64738a6b7dc Mon Sep 17 00:00:00 2001 From: Andreas Schaafsma Date: Thu, 13 Jun 2024 07:03:38 +0200 Subject: [PATCH] finally got the associations to work through black magic --- src/app/api/attachment/route.ts | 26 ++++++++++------------ src/app/api/post/route.ts | 6 ++--- src/app/api/setupDB/route.ts | 22 ++++--------------- src/app/api/user/route.ts | 10 +++++---- src/model/APIKey.ts | 6 ----- src/model/Attachment.ts | 7 +----- src/model/Auth.ts | 6 ----- src/model/Bucket.ts | 13 ++++------- src/model/DBState.ts | 6 ----- src/model/Models.ts | 36 ++++++++++++++++++++---------- src/model/Post.ts | 39 +++++++++++++++++++++++---------- src/model/PostTag.ts | 5 ----- src/model/Project.ts | 6 ----- src/model/Tag.ts | 6 ----- src/model/User.ts | 36 +++++++++++------------------- src/model/UserPerms.ts | 12 ++++------ 16 files changed, 98 insertions(+), 144 deletions(-) diff --git a/src/app/api/attachment/route.ts b/src/app/api/attachment/route.ts index 35f9882..23bd198 100644 --- a/src/app/api/attachment/route.ts +++ b/src/app/api/attachment/route.ts @@ -1,14 +1,12 @@ 'use server' import { APIError, attemptAPIAction } from "@/util/api/error"; -import { Auth, Post, PostTag, Tag, User } from "@/model/Models"; +import { sequelize, Bucket, Auth, Post, PostTag, Tag, User, dbSync } from "@/model/Models"; import { cookies } from "next/headers"; import { Attachment } from "@/model/Attachment"; 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"; @@ -37,28 +35,28 @@ async function writeFilesToBucket(uuid: UUID, files:any[]) { async function addToPost(postid:number):Promise { - 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}) + Post.sync(); + const post = await Post.findOne({where: {id:postid}}); - console.log(bucketPost); - return bucket.id + if (!post) throw new APIError({ status: 500, responseText: "invalid postid" }); + const bucket = await post.createBucket({id:randomUUID()}); + // const bucketPost = await Post.associations.postBuckets.create({bucketId: bucket.id, postId: postid}) + + // console.log(bucketPost); + return bucket.id; } async function addToBucket(bucketid:number):Promise { const bucket = await Bucket.findOne({where: {id: bucketid}}); if (!bucket) throw new APIError({ status: 500, responseText: "invalid bucketid" }); - return bucket.id + return bucket.id; } async function tryCreateAttachment(request: Request) { // Make sure the DB is ready - await Attachment.sync(); - await Bucket.sync(); - await Post.sync(); - + await dbSync; + // Prepare data const formData = await request.formData(); const requestData:string | Object | undefined = formData.get('data')?.valueOf(); diff --git a/src/app/api/post/route.ts b/src/app/api/post/route.ts index 118322a..ef0bdb3 100644 --- a/src/app/api/post/route.ts +++ b/src/app/api/post/route.ts @@ -1,7 +1,7 @@ 'use server' import { APIError, attemptAPIAction } from "@/util/api/error"; -import { Auth, Post, PostTag, Tag, User } from "@/model/Models"; +import { Auth, Post, PostTag, Tag, User, dbSync } from "@/model/Models"; import { cookies } from "next/headers"; @@ -9,9 +9,7 @@ import { cookies } from "next/headers"; async function tryCreatePost(request: Request) { // Make sure the DB is ready - await PostTag.sync(); - await Tag.sync(); - await Post.sync(); + const sync = await dbSync; // Prepare data const requestBody = await request.json(); diff --git a/src/app/api/setupDB/route.ts b/src/app/api/setupDB/route.ts index 452686c..fd3a842 100644 --- a/src/app/api/setupDB/route.ts +++ b/src/app/api/setupDB/route.ts @@ -4,17 +4,14 @@ import { cookies } from "next/headers"; 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 { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms,dbSync,sequelize} from "@/model/Models"; import Sequelize, { DataTypes } from "@sequelize/core"; import { SqliteColumnsDescription, SqliteDialect, SqliteQueryInterface } from "@sequelize/sqlite3"; import { hashPassword } from "@/util/Auth"; async function seedDatabase(queryInterface:SqliteQueryInterface){ + const password = await hashPassword('changeme'); const project = await Project.findOne({where: { readableIdentifier: 'blog' @@ -43,21 +40,10 @@ async function seedDatabase(queryInterface:SqliteQueryInterface){ } async function trySetup(request:Request){ + await dbSync; - const sequelize = await new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite' - }) const queryInterface = sequelize.queryInterface - // 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 DBState.findAll()).sort((a,b)=> ((a.version > b.version) ? 1 : -1)).map(a=>a.version)[0]; diff --git a/src/app/api/user/route.ts b/src/app/api/user/route.ts index a04fb3a..1c961fc 100644 --- a/src/app/api/user/route.ts +++ b/src/app/api/user/route.ts @@ -10,15 +10,16 @@ import { APIError } from "@/util/api/error"; import { UserAuth } from "@/util/api/user"; -import { UserPerms, User, Auth } from "@/model/Models"; // Do not alter "unused" imports, they are required to perform the nescessairy includes on the User model +import { Attachment, Auth, Bucket, DBState, Post, PostTag, Project, Tag, User, UserPerms,dbSync,sequelize} from "@/model/Models"; import { hashPassword } from "@/util/Auth"; +import { addUserScopes } from "@/model/User"; // Attempt to register a new User -async function attemptRegister(request:Request){ +async function attemptRegister(request:Request){ + // Sync db + await dbSync; - // Sync User model - User.sync(); // Get request body const requestBody:Partial = await request.json(); // Handle edgecases @@ -73,6 +74,7 @@ export async function POST(request:Request){ } async function attemptGetUsers(request:Request){ + await dbSync; // Get users with scopes applied const users = await User.withScope(['defaultScope','withPerms','withAuthtokens']).findAll(); return new Response( diff --git a/src/model/APIKey.ts b/src/model/APIKey.ts index 55a5c88..73ce73c 100644 --- a/src/model/APIKey.ts +++ b/src/model/APIKey.ts @@ -34,9 +34,3 @@ export class APIKey extends Model, InferCreationAttribut declare updatedAt: CreationOptional; } - -const sequelize = new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite', - models: [APIKey] -}); diff --git a/src/model/Attachment.ts b/src/model/Attachment.ts index 2cddf00..6b82fcb 100644 --- a/src/model/Attachment.ts +++ b/src/model/Attachment.ts @@ -26,10 +26,5 @@ export class Attachment extends Model, InferCreation declare static associations: { bucket: Association; }; + } - -const sequelize = new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite', - models: [Attachment] -}) diff --git a/src/model/Auth.ts b/src/model/Auth.ts index 1070714..4891e78 100644 --- a/src/model/Auth.ts +++ b/src/model/Auth.ts @@ -46,9 +46,3 @@ export class Auth extends Model, InferCreationAttributes, InferCreationAttribut declare id: UUID /** Defined by {@link Post.buckets} */ - declare bucketPosts?:NonAttribute[]; + declare posts?:NonAttribute[]; + /** Defined by {@link Attachment.bucket} */ declare attachments?:NonAttribute[]; declare static associations: { - bucketPosts: Association; + posts: Association; attachments: Association; }; } - -const sequelize = new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite', - models: [Bucket] -}) diff --git a/src/model/DBState.ts b/src/model/DBState.ts index 8b080a3..0ada976 100644 --- a/src/model/DBState.ts +++ b/src/model/DBState.ts @@ -32,9 +32,3 @@ export class DBState extends Model, InferCreationAttrib } -const sequelize = new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite', - models: [DBState] -}); - diff --git a/src/model/Models.ts b/src/model/Models.ts index 0f2a399..5d8a6f4 100644 --- a/src/model/Models.ts +++ b/src/model/Models.ts @@ -1,15 +1,29 @@ -import { User } from './User'; import { Auth } from './Auth'; -import { UserPerms } from './UserPerms'; -import { Post } from './Post'; -import { Tag } from './Tag'; +import { Attachment } from './Attachment'; +import { Bucket } from './Bucket'; +import { DBState } from './DBState'; +import { Post, PostBucket } from './Post'; import { PostTag } from './PostTag'; +import { Project } from './Project'; +import { Tag } from './Tag'; +import { User, addUserScopes } from './User'; +import { UserPerms, addUserPermsScopes } from './UserPerms'; +import { SqliteDialect } from '@sequelize/sqlite3'; +import Sequelize from '@sequelize/core'; -User.sync(); -Auth.sync(); -UserPerms.sync(); -PostTag.sync(); -Tag.sync(); -Post.sync(); -export { User, Auth, UserPerms, Post, Tag, PostTag } \ No newline at end of file +const sequelize = new Sequelize({ + dialect: SqliteDialect, + storage: 'db.sqlite', + models: [Auth, Attachment, Bucket, DBState, Post, PostBucket, PostTag, Project, Tag, User, UserPerms], + +}); + + +const dbSync = (async ()=> await sequelize.sync())().then(()=>{ + addUserScopes(); + addUserPermsScopes(); +}); + + +export { sequelize, dbSync, Auth, Attachment, Bucket, DBState, Post, PostBucket, PostTag, Project, Tag, User, UserPerms } \ No newline at end of file diff --git a/src/model/Post.ts b/src/model/Post.ts index 3904243..9f38039 100644 --- a/src/model/Post.ts +++ b/src/model/Post.ts @@ -1,4 +1,4 @@ -import { Association, BelongsToGetAssociationMixin, BelongsToManyAssociation, BelongsToManyCreateAssociationMixin, CreationOptional, DataTypes, ForeignKey, InferAttributes, InferCreationAttributes, Model, NonAttribute, Sequelize } from "@sequelize/core";import { User } from "./User"; +import { Association, BelongsToGetAssociationMixin, BelongsToManyAddAssociationMixin, BelongsToManyAssociation, BelongsToManyCreateAssociationMixin, BelongsToManyGetAssociationsMixin, BelongsToManyRemoveAssociationMixin, BelongsToManySetAssociationsMixin, 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"; @@ -49,11 +49,17 @@ export class Post extends Model, InferCreationAttributes

Tag, { through: { model: ()=>PostTag, unique: false}, inverse: {as: 'taggedPosts'} }) declare postTags?:NonAttribute; - @BelongsToMany(()=>Bucket,{through: { model: ()=> PostBucket, unique: false}, inverse:{as: 'bucketPosts'}, foreignKey: 'postId', otherKey: 'bucketId'}) - declare postBuckets:NonAttribute; + + @BelongsToMany(()=>Bucket,{through: {model:'PostBucket'}, throughAssociations: { + fromSource: 'postPostBuckets', + toSource: 'post', + fromTarget: 'postBucketPosts', + toTarget: 'PostBucket', + }, inverse:{as: 'posts'}, foreignKey: 'postId', otherKey: 'bucketId'}) + declare buckets?:NonAttribute; - declare getUser: BelongsToGetAssociationMixin; + declare static associations: { @@ -62,16 +68,25 @@ export class Post extends Model, InferCreationAttributes

postTags: Association; }; + + declare getUser: BelongsToGetAssociationMixin; + declare getBuckets: BelongsToManyGetAssociationsMixin; + declare setBuckets: BelongsToManySetAssociationsMixin; + declare addBucket: BelongsToManyAddAssociationMixin; + declare addBuckets: BelongsToManyAddAssociationMixin; + declare removeBucket: BelongsToManyRemoveAssociationMixin; + declare removeBuckets: BelongsToManyRemoveAssociationMixin; + declare createBucket: BelongsToManyCreateAssociationMixin; } -export class PostBucket extends Model, InferCreationAttributes>{ + +export class PostBucket extends Model, InferCreationAttributes> { declare postId: number; declare bucketId: UUID; -} -const sequelize = new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite', - models: [Post, PostBucket] -}) + declare post?:NonAttribute + declare bucket?:NonAttribute -PostBucket.sync(); \ No newline at end of file + + + + } diff --git a/src/model/PostTag.ts b/src/model/PostTag.ts index bab3ccf..050a036 100644 --- a/src/model/PostTag.ts +++ b/src/model/PostTag.ts @@ -11,10 +11,5 @@ class PostTag extends Model,InferCreationAttributes, InferCreationAttrib }; } - -const sequelize = new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite', - models: [Project] -}) diff --git a/src/model/Tag.ts b/src/model/Tag.ts index 8ec5ab0..08b085c 100644 --- a/src/model/Tag.ts +++ b/src/model/Tag.ts @@ -18,9 +18,3 @@ export class Tag extends Model, InferCreationAttributes; }; } - -const sequelize = new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite', - models: [Tag] -}) diff --git a/src/model/User.ts b/src/model/User.ts index 723dec5..57d70f6 100644 --- a/src/model/User.ts +++ b/src/model/User.ts @@ -22,9 +22,7 @@ type UserCreationAttributes = { password:string; perms?:Partial> } - export class User extends Model, InferCreationAttributes>{ - @Attribute(DataTypes.INTEGER) @PrimaryKey @AutoIncrement @@ -63,24 +61,16 @@ export class User extends Model, InferCreationAttributes, InferCreationAt } - -const sequelize = new Sequelize({ - dialect: SqliteDialect, - storage: 'db.sqlite', - models: [UserPerms], -}); - +export function addUserPermsScopes(){ UserPerms.addScope('defaultScope',{ attributes: { @@ -43,4 +37,6 @@ UserPerms.addScope('withTimestamps',{ attributes: { include: ['createdAt', 'updatedAt'], } -}); \ No newline at end of file +}); +} +