unoffical wafrn mirror wafrn.net
atproto social-network activitypub
at angular21 411 lines 9.9 kB view raw
1import { 2 Model, 3 Table, 4 Column, 5 DataType, 6 ForeignKey, 7 HasMany, 8 HasOne, 9 BelongsToMany, 10 BelongsTo, 11} from "sequelize-typescript"; 12import { Notification } from "./notification.js"; 13import { Ask } from "./ask.js"; 14import { QuestionPoll } from "./questionPoll.js"; 15import { EmojiReaction } from "./emojiReaction.js"; 16import { Emoji } from "./emoji.js"; 17import { PostEmojiRelations } from "./postEmojiRelations.js"; 18import { Quotes } from "./quotes.js"; 19import { PostReport } from "./postReport.js"; 20import { SilencedPost } from "./silencedPost.js"; 21import { PostTag } from "./postTag.js"; 22import { User } from "./user.js"; 23import { Media } from "./media.js"; 24import { PostMentionsUserRelation } from "./postMentionsUserRelation.js"; 25import { UserLikesPostRelations } from "./userLikesPostRelations.js"; 26import { UserBookmarkedPosts } from "./userBookmarkedPosts.js"; 27import { PostHostView } from "./postHostView.js"; 28import { RemoteUserPostView } from "./remoteUserPostView.js"; 29import { FederatedHost } from "./federatedHost.js"; 30import { PostAncestor } from "./postAncestor.js"; 31import { 32 BelongsToGetAssociationMixin, 33 BelongsToManyGetAssociationsMixin, 34 BelongsToManySetAssociationsMixin, 35 BelongsToSetAssociationMixin, 36 HasManyGetAssociationsMixin, 37 HasManyRemoveAssociationMixin, 38 HasManyRemoveAssociationsMixin, 39 HasManySetAssociationsMixin, 40 HasOneGetAssociationMixin, 41} from "sequelize"; 42import { completeEnvironment } from "../utils/backendOptions.js"; 43 44export const Privacy = { 45 Public: 0, 46 FollowersOnly: 1, 47 LocalOnly: 2, 48 Unlisted: 3, 49 DirectMessage: 10, 50 LinkOnly: 20, 51} as const; 52 53export const InteractionControl = { 54 Anyone: 0, 55 Followers: 1, 56 Following: 2, 57 FollowersAndFollowing: 3, 58 FollowersAndMentioned: 4, 59 FollowingAndMentioned: 5, 60 FollowersFollowersAndMentioned: 6, 61 MentionedUsersOnly: 7, 62 NoOne: 8, 63 SameAsOp: 100, // this one is bsky exclusive and its gona be FUN (a headache). This only applies to REPLIES. Nothing else. 64}; 65 66export type PrivacyType = typeof Privacy[keyof typeof Privacy]; 67 68export type InteractionControlType = 69 typeof InteractionControl[keyof typeof InteractionControl]; 70 71export interface PostAttributes { 72 id?: string; 73 createdAt?: Date; 74 updatedAt?: Date; 75 content_warning?: string | null; 76 content?: string; 77 markdownContent?: string; 78 title?: string; 79 slug?: string; 80 remotePostId?: string | null; 81 bskyUri?: string | null; 82 bskyCid?: string | null; 83 privacy?: PrivacyType; 84 featured?: Date | null; 85 isReblog?: boolean; 86 isDeleted?: boolean; 87 userId?: string; 88 hierarchyLevel?: number; 89 parentId?: string; 90 replyControl?: InteractionControlType; 91 likeControl?: InteractionControlType; 92 reblogControl?: InteractionControlType; 93 quoteControl?: InteractionControlType; 94 displayUrl: String | null; 95} 96 97@Table({ 98 tableName: "posts", 99 modelName: "posts", 100 timestamps: true, 101}) 102export class Post 103 extends Model<PostAttributes, PostAttributes> 104 implements PostAttributes 105{ 106 @Column({ 107 primaryKey: true, 108 type: DataType.UUID, 109 defaultValue: DataType.UUIDV4, 110 }) 111 declare id: string; 112 113 @Column({ 114 allowNull: true, 115 type: DataType.STRING, 116 }) 117 declare content_warning: string; 118 119 @Column({ 120 allowNull: true, 121 type: DataType.STRING, 122 }) 123 declare content: string; 124 125 @Column({ 126 allowNull: true, 127 type: DataType.STRING, 128 }) 129 declare markdownContent: string; 130 131 @Column({ 132 allowNull: true, 133 type: DataType.STRING(256), 134 }) 135 declare title: string; 136 137 @Column({ 138 allowNull: true, 139 type: DataType.STRING(256), 140 }) 141 declare slug: string; 142 143 @Column({ 144 allowNull: true, 145 type: DataType.STRING(768), 146 }) 147 declare remotePostId: string | null; 148 149 @Column({ 150 allowNull: true, 151 type: DataType.STRING(768), 152 }) 153 declare bskyUri: string | null; 154 155 @Column({ 156 allowNull: true, 157 type: DataType.STRING(768), 158 }) 159 declare bskyCid: string | null; 160 161 @Column({ 162 allowNull: true, 163 type: DataType.INTEGER, 164 }) 165 declare privacy: PrivacyType; 166 167 @Column({ 168 allowNull: true, 169 type: DataType.DATE, 170 defaultValue: null, 171 }) 172 declare featured: Date | null; 173 174 @Column({ 175 allowNull: true, 176 type: DataType.BOOLEAN, 177 defaultValue: false, 178 }) 179 declare isReblog: boolean; 180 181 @Column({ 182 allowNull: true, 183 type: DataType.BOOLEAN, 184 defaultValue: false, 185 }) 186 declare isDeleted: boolean; 187 188 @ForeignKey(() => User) 189 @Column({ 190 allowNull: true, 191 type: DataType.UUID, 192 }) 193 declare userId: string; 194 195 @Column({ 196 allowNull: true, 197 type: DataType.INTEGER, 198 }) 199 declare hierarchyLevel: number; 200 201 @Column({ 202 allowNull: true, 203 type: DataType.STRING, 204 }) 205 declare displayUrl: string; 206 207 @ForeignKey(() => Post) 208 @Column({ 209 allowNull: true, 210 type: DataType.UUID, 211 }) 212 declare parentId: string; 213 214 @Column({ 215 allowNull: true, 216 type: DataType.INTEGER, 217 defaultValue: 0, 218 }) 219 declare replyControl: InteractionControlType; 220 221 @Column({ 222 allowNull: true, 223 type: DataType.INTEGER, 224 defaultValue: 0, 225 }) 226 declare likeControl: InteractionControlType; 227 228 @Column({ 229 allowNull: true, 230 type: DataType.INTEGER, 231 defaultValue: 0, 232 }) 233 declare reblogControl: InteractionControlType; 234 235 @Column({ 236 allowNull: true, 237 type: DataType.INTEGER, 238 defaultValue: 0, 239 }) 240 declare quoteControl: InteractionControlType; 241 242 @BelongsTo(() => Post, "parentId") 243 declare parent: Post; 244 declare getParent: BelongsToGetAssociationMixin<Post>; 245 declare setParent: BelongsToSetAssociationMixin<Post, string>; 246 247 @HasMany(() => Post, "parentId") 248 declare children: Post[]; 249 250 @BelongsToMany(() => Post, () => PostAncestor, "postsId", "ancestorId") 251 declare ancestors: Post[]; 252 declare getAncestors: BelongsToManyGetAssociationsMixin<Post>; 253 254 @BelongsToMany(() => Post, () => PostAncestor, "ancestorId", "postsId") 255 declare descendents: Post[]; 256 declare getDescendents: BelongsToManyGetAssociationsMixin<Post>; 257 258 @HasMany(() => Notification, { 259 sourceKey: "id", 260 }) 261 declare notifications: Notification[]; 262 263 @HasOne(() => Ask, { 264 sourceKey: "id", 265 }) 266 declare ask: Ask; 267 declare getAsk: HasOneGetAssociationMixin<Ask>; 268 269 @HasOne(() => QuestionPoll, { 270 sourceKey: "id", 271 }) 272 declare questionPoll: QuestionPoll; 273 274 @HasMany(() => EmojiReaction, { 275 sourceKey: "id", 276 }) 277 declare emojiReacions: EmojiReaction[]; 278 279 @BelongsToMany(() => Emoji, () => PostEmojiRelations) 280 declare emojis: Emoji[]; 281 declare getEmojis: BelongsToManyGetAssociationsMixin<Emoji>; 282 283 @HasMany(() => Quotes, { 284 foreignKey: "quoterPostId", 285 }) 286 declare quoterQuotes: Quotes[]; 287 288 @HasMany(() => Quotes, { 289 foreignKey: "quotedPostId", 290 }) 291 declare quotedQuotes: Quotes[]; 292 293 @BelongsToMany(() => Post, () => Quotes, "quoterPostId", "quotedPostId") 294 declare quoted: Post[]; 295 declare setQuoted: BelongsToManySetAssociationsMixin<Post, string>; 296 297 @BelongsToMany(() => Post, () => Quotes, "quotedPostId", "quoterPostId") 298 declare quoter: Post[]; 299 300 @HasMany(() => PostReport, { 301 sourceKey: "id", 302 }) 303 declare postReports: PostReport[]; 304 305 @HasMany(() => SilencedPost, { 306 sourceKey: "id", 307 }) 308 declare silencedPosts: SilencedPost[]; 309 310 @HasMany(() => PostTag, { 311 sourceKey: "id", 312 }) 313 declare postTags: PostTag[]; 314 declare getPostTags: HasManyGetAssociationsMixin<PostTag>; 315 316 @BelongsTo(() => User) 317 declare user: User; 318 declare getUser: BelongsToGetAssociationMixin<User>; 319 320 @HasMany(() => Media, { 321 sourceKey: "id", 322 }) 323 declare medias: Media[]; 324 declare setMedias: HasManySetAssociationsMixin<Media, number>; 325 declare getMedias: HasManyGetAssociationsMixin<Media>; 326 declare removeMedias: HasManyRemoveAssociationsMixin<Media, number>; 327 328 @HasMany(() => PostMentionsUserRelation, { 329 sourceKey: "id", 330 }) 331 declare pMURs: PostMentionsUserRelation[]; 332 333 @HasMany(() => UserLikesPostRelations, { 334 sourceKey: "id", 335 }) 336 declare userLikesPostRelations: UserLikesPostRelations[]; 337 338 @BelongsToMany(() => User, () => PostMentionsUserRelation) 339 declare mentionPost: User[]; 340 declare getMentionPost: BelongsToManyGetAssociationsMixin<User>; 341 342 @HasMany(() => UserBookmarkedPosts, { 343 sourceKey: "id", 344 }) 345 declare userBookmarkedPosts: UserBookmarkedPosts[]; 346 347 @HasMany(() => PostHostView, { 348 sourceKey: "id", 349 }) 350 declare postHostViewList: PostHostView[]; 351 352 @BelongsToMany(() => FederatedHost, () => PostHostView) 353 declare hostView: FederatedHost[]; 354 355 @HasMany(() => RemoteUserPostView, { 356 sourceKey: "id", 357 }) 358 declare remoteUserPostViewList: RemoteUserPostView[]; 359 360 @BelongsToMany(() => User, () => RemoteUserPostView) 361 declare view: User[]; 362 363 static get hierarchy() { 364 return { 365 as: "parent", 366 childrenAs: "children", 367 ancestorsAs: "ancestors", 368 descendentsAs: "descendents", 369 primaryKey: "id", 370 foreignKey: "parentId", 371 levelFieldName: "hierarchyLevel", 372 through: PostAncestor, 373 throughKey: "postsId", 374 throughForeignKey: "ancestorId", 375 throughTable: "postancestors", 376 }; 377 } 378 379 get hierarchy() { 380 return Post.hierarchy; 381 } 382 383 get fullUrl() { 384 return ( 385 this.remotePostId || 386 `${completeEnvironment.frontendUrl}/fediverse/post/${this.id}` 387 ); 388 } 389 390 // only use if the user is already loaded into the object, otherwise use `await isRemoteBlueskyPostAsync()` 391 get isRemoteBlueskyPost() { 392 return this.bskyUri && this.user.isRemoteUser && !this.remotePostId; 393 } 394 395 async isRemoteBlueskyPostAsync() { 396 return this.bskyUri && (await this.getUser()).isRemoteUser; 397 } 398 399 get postShouldGoFedi() { 400 return this.remotePostId || !this.user.isRemoteUser; 401 } 402 403 async fullUrlIncludingBsky() { 404 return ( 405 this.remotePostId || 406 ((await this.isRemoteBlueskyPostAsync()) 407 ? this.bskyUri 408 : `${completeEnvironment.frontendUrl}/fediverse/post/${this.id}`) 409 ); 410 } 411}