This commit is contained in:
2026-06-24 14:10:53 +02:00
commit fdb3768d63
122 changed files with 13239 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
node_modules
deploy.sh
+9
View File
@@ -0,0 +1,9 @@
root = true
[*]
quote_type = single
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
+32
View File
@@ -0,0 +1,32 @@
DB_USERNAME=
DB_PASSWORD=
DB_HOST=
DB_PORT=
DB_DATABASE=
REDIS_URL=
REDIS_SECRET=
API_PORT=
FRONTEND_ORIGIN=
BACKEND_ORIGIN=
DOMAIN_SUFFIX=
NON_API_ERROR_PREFIX=
NON_API_ERROR_SUFFIX=
AWS_REGION=
AWS_ACCOUNT_ID=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
S3_BUCKET=
QUICKSIGHT_DASHBOARD_ID=
FIREBASE_ADMIN_TYPE=
FIREBASE_ADMIN_PROJECT_ID=
FIREBASE_ADMIN_PRIVATE_KEY_ID=
FIREBASE_ADMIN_PRIVATE_KEY=
FIREBASE_ADMIN_CLIENT_EMAIL=
FIREBASE_ADMIN_CLIENT_ID=
FIREBASE_ADMIN_AUTH_URI=
FIREBASE_TOKEN_URI=
FIREBASE_ADMIN_AUTH_PROVIDER_X509_CERT_URL=
FIREBASE_ADMIN_CLIENT_X509_CERT_URL=
SMTP_HOST=
SMTP_PORT=
SMTP_SECURE=
+5
View File
@@ -0,0 +1,5 @@
node_modules
.env
.env.development
.env.production
deploy.sh
+15
View File
@@ -0,0 +1,15 @@
FROM node:latest
WORKDIR /usr/src/app
COPY package.json .
COPY package-lock.json .
RUN npm install
COPY . .
COPY .env.production .env
ENV NODE_ENV production
EXPOSE 4000
CMD ["npm", "run", "start"]
+1
View File
@@ -0,0 +1 @@
# Backend for Comroots
Vendored
+42
View File
@@ -0,0 +1,42 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
DB_USERNAME: string;
DB_PASSWORD: string;
DB_HOST: string;
DB_PORT: string;
DB_DATABASE: string;
REDIS_URL: string;
REDIS_SECRET: string;
API_PORT: string;
FRONTEND_ORIGIN: string;
BACKEND_ORIGIN: string;
DOMAIN_SUFFIX: string;
NON_API_ERROR_PREFIX: string;
NON_API_ERROR_SUFFIX: string;
AWS_REGION: string;
AWS_ACCOUNT_ID: string;
AWS_ACCESS_KEY_ID: string;
AWS_SECRET_ACCESS_KEY: string;
S3_BUCKET: string;
QUICKSIGHT_DASHBOARD_ID: string;
FIREBASE_ADMIN_TYPE: string;
FIREBASE_ADMIN_PROJECT_ID: string;
FIREBASE_ADMIN_PRIVATE_KEY_ID: string;
FIREBASE_ADMIN_PRIVATE_KEY: string;
FIREBASE_ADMIN_CLIENT_EMAIL: string;
FIREBASE_ADMIN_CLIENT_ID: string;
FIREBASE_ADMIN_AUTH_URI: string;
FIREBASE_TOKEN_URI: string;
FIREBASE_ADMIN_AUTH_PROVIDER_X509_CERT_URL: string;
FIREBASE_ADMIN_CLIENT_X509_CERT_URL: string;
SMTP_HOST: string;
SMTP_PORT: string;
SMTP_SECURE: string;
SMTP_USER: string;
SMTP_PASS: string;
}
}
}
export {}
+6944
View File
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
{
"name": "comroots-backend",
"version": "1.0.0",
"description": "Backend for Comroots. Uses PostgreSQL, Redis, and ExpressJS. Hosted as a Docker container using Dokku.",
"main": "index.js",
"type": "commonjs",
"scripts": {
"dev": "nodemon --watch . src/index.ts",
"start": "ts-node src/index.ts",
"env:generate": "gen-env-types .env -o env.d.ts -e .",
"typeorm": "ts-node ./node_modules/typeorm/cli.js",
"migration:initial": "run typeorm migration:generate -d src/type-orm.source.ts src/migrations/Initial",
"migration:entities-update": "run typeorm migration:generate -d src/type-orm.source.ts src/migrations/EntitiesUpdate",
"migration:new": "run typeorm migration:create"
},
"dependencies": {
"@faker-js/faker": "^7.4.0",
"@types/graphql-upload": "^8.0.11",
"apollo-server-core": "^3.8.2",
"apollo-server-errors": "^3.3.1",
"apollo-server-express": "^3.8.2",
"argon2": "^0.28.5",
"aws-sdk": "^2.1172.0",
"connect-redis": "^6.1.3",
"dataloader": "^2.1.0",
"dotenv-safe": "^8.2.0",
"express": "^4.18.1",
"express-session": "^1.17.3",
"firebase-admin": "^11.0.0",
"fireorm": "^0.23.3",
"graphql": "^15.3.0",
"graphql-upload": "13.0.0",
"ioredis": "^5.1.0",
"lodash": "^4.17.21",
"nodemailer": "^6.7.5",
"pg": "^8.7.3",
"redis-session": "^0.1.0",
"reflect-metadata": "^0.1.13",
"type-graphql": "^1.1.1",
"typeorm": "^0.3.7",
"uuid": "^8.3.2",
"@swc/core": "^1.2.213",
"@swc/helpers": "^0.4.3",
"ts-node": "^10.8.2",
"tsconfig-paths": "^4.0.0",
"regenerator-runtime": "^0.13.9"
},
"devDependencies": {
"@types/connect-redis": "^0.0.18",
"@types/dotenv-safe": "^8.1.2",
"@types/express": "^4.17.13",
"@types/express-session": "^1.17.4",
"@types/ioredis": "^4.28.10",
"@types/lodash": "^4.14.182",
"@types/node": "^17.0.41",
"@types/nodemailer": "^6.4.4",
"@types/uuid": "^8.3.4",
"gen-env-types": "^1.3.4",
"nodemon": "^2.0.16",
"typescript": "^4.7.3"
}
}
+35
View File
@@ -0,0 +1,35 @@
import { Collection, getRepository, ISubCollection, SubCollection } from 'fireorm'
import { Field, Int, ObjectType } from 'type-graphql'
@ObjectType()
export class Message {
@Field()
id: string
@Field(() => String)
createdAt: Date
@Field(() => String)
updatedAt: Date
@Field(() => String)
type: 'text' | 'image' | 'video' | 'audio' | 'file'
@Field(() => String)
content: string
@Field(() => Int)
senderId: number
}
@ObjectType()
@Collection('conversations')
export class ConversationInFirestore {
@Field()
id: string
@SubCollection(Message, 'messages')
messages: ISubCollection<Message>
}
export const ConversationCol = getRepository(ConversationInFirestore)
+17
View File
@@ -0,0 +1,17 @@
import { Collection, getRepository } from 'fireorm'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType()
@Collection('inboxes')
export class Inbox {
@Field()
id: string
@Field(() => Int)
userId: number
@Field(() => [String])
conversationIds: string[]
}
export const InboxCol = getRepository(Inbox)
+2
View File
@@ -0,0 +1,2 @@
export * from './Conversation'
export * from './Inbox'
+12
View File
@@ -0,0 +1,12 @@
import AWS from 'aws-sdk'
AWS.config.update({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
}
})
export const s3 = new AWS.S3({ apiVersion: '2006-03-01' })
export const quicksight = new AWS.QuickSight()
+14
View File
@@ -0,0 +1,14 @@
import admin from 'firebase-admin'
import { getFirestore } from 'firebase-admin/firestore'
import * as fireorm from 'fireorm'
const app = admin.initializeApp({
credential: admin.credential.cert({
clientEmail: process.env.FIREBASE_ADMIN_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_ADMIN_PRIVATE_KEY,
projectId: process.env.FIREBASE_ADMIN_PROJECT_ID
})
})
export const firestore = getFirestore(app)
fireorm.initialize(firestore)
+3
View File
@@ -0,0 +1,3 @@
export * from './aws.config'
export * from './type-orm.config'
export * from './firebase.config'
+39
View File
@@ -0,0 +1,39 @@
import { Comment, CommentVote, Conversation, CV, EducationItem, Experience, Offer, OfferApplication, Page, PageFollow, Post, Qualification, Space, SpaceSubscription, Tag, Vote, User, UserFollow } from '@entities'
import path from 'path'
import { DataSource } from 'typeorm'
export const typeORMConfig = {
type: 'postgres',
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT),
database: process.env.DB_DATABASE,
logging: true,
synchronize: false,
entities: [
User,
Post,
Vote,
Space,
SpaceSubscription,
Comment,
CommentVote,
Offer,
OfferApplication,
Tag,
Conversation,
Page,
PageFollow,
UserFollow,
EducationItem,
Experience,
Qualification,
CV
],
migrationsTableName: 'type_orm_migrations',
migrations: [path.join(__dirname, '../migrations/*')],
cli: {
migrationsDir: 'migrations'
}
} as ConstructorParameters<typeof DataSource>[0]
+4
View File
@@ -0,0 +1,4 @@
// export const __prod__ = process.env.NODE_ENV === 'production'
export const __prod__ = false
export const COOKIE_NAME = 'qid'
export const FORGET_PASSWORD_PREFIX = 'forget-password:'
+30
View File
@@ -0,0 +1,30 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
import { User } from './User'
@ObjectType()
@Entity()
export class CV extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field()
@Column()
filename?: string
@Field()
@Column()
key?: string
@Field({ nullable: true })
url?: string
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
userId?: number
@Field(() => User, { nullable: true })
@ManyToOne(() => User, user => user.cvs, { onDelete: 'CASCADE' })
user?: User
}
+60
View File
@@ -0,0 +1,60 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
import { CommentVote, Post, User, Page } from '@entities'
@ObjectType()
@Entity()
export class Comment extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field(() => String, { nullable: true })
@CreateDateColumn()
createdAt?: Date
@Field(() => String, { nullable: true })
@UpdateDateColumn()
updatedAt?: Date
@Field()
@Column()
text!: string
@Field(() => String)
@Column({ default: 'user' })
creatorType?: 'page' | 'user'
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
creatorId?: number
@Field(() => User, { nullable: true })
@ManyToOne(() => User, user => user.comments, { onDelete: 'CASCADE' })
creator?: User
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
pageCreatorId?: number
@Field(() => Page, { nullable: true })
@ManyToOne(() => Page, page => page.posts, { onDelete: 'CASCADE' })
pageCreator?: Page
@Field(() => Int, { nullable: true })
@Column({ nullable: false })
postId?: number
@ManyToOne(() => Post, post => post.comments, { onDelete: 'CASCADE' })
post?: Post
@OneToMany(() => CommentVote, commentVote => commentVote.comment)
votes?: CommentVote[]
@Field(() => Int)
@Column({ type: 'int', default: 0 })
points!: number
@Field(() => Int, { nullable: true })
voteStatus?: number
}
+27
View File
@@ -0,0 +1,27 @@
import { Comment, User } from '@entities'
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm'
@ObjectType()
@Entity()
export class CommentVote extends BaseEntity {
@Field(() => Int)
@Column({ type: 'int' })
value!: number
@Field(() => Int)
@PrimaryColumn()
userId!: number
@Field(() => User)
@ManyToOne(() => User, user => user.commentVotes, { onDelete: 'CASCADE' })
user?: User
@Field(() => Int)
@PrimaryColumn()
commentId!: number
@Field(() => Comment)
@ManyToOne(() => Comment, comment => comment.votes, { onDelete: 'CASCADE' })
comment?: Comment
}
+18
View File
@@ -0,0 +1,18 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
@ObjectType()
@Entity()
export class Conversation extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id: number
@Field(() => [Int])
@Column('int', { array: true })
participants: number[]
@Field()
@Column()
firestoreCollectionId: string
}
+42
View File
@@ -0,0 +1,42 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
import { User } from './User'
@ObjectType()
@Entity()
export class EducationItem extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field()
@Column()
school?: string
@Field({ nullable: true })
@Column({ nullable: true })
status?: string
@Field(() => Date, { nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
startDate?: Date
@Field(() => Date, { nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
endDate?: Date
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
userId?: number
@Field(() => User, { nullable: true })
@ManyToOne(() => User, user => user.educationItems, { onDelete: 'CASCADE' })
user?: User
@Field({ nullable: true })
@Column({ nullable: true })
photo?: string
@Field({ nullable: true })
photoUrl?: string
}
+42
View File
@@ -0,0 +1,42 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
import { User } from './User'
@ObjectType()
@Entity()
export class Experience extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field()
@Column()
title?: string
@Field({ nullable: true })
@Column({ nullable: true })
workplace?: string
@Field(() => Date, { nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
startDate?: Date
@Field(() => Date, { nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
endDate?: Date
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
userId?: number
@Field(() => User, { nullable: true })
@ManyToOne(() => User, user => user.experiences, { onDelete: 'CASCADE' })
user?: User
@Field({ nullable: true })
@Column({ nullable: true })
photo?: string
@Field({ nullable: true })
photoUrl?: string
}
+104
View File
@@ -0,0 +1,104 @@
import { OfferApplication, Page, Space, User } from '@entities'
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
@ObjectType()
@Entity()
export class Offer extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field(() => String)
@CreateDateColumn()
createdAt?: Date
@Field(() => String)
@UpdateDateColumn()
updatedAt?: Date
@Field(() => String, { nullable: true })
@Column({ default: 'user' })
creatorType?: 'page' | 'user'
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
creatorId!: number
@Field(() => User, { nullable: true })
@ManyToOne(() => User, user => user.offers, { onDelete: 'CASCADE' })
creator?: User
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
pageCreatorId?: number
@Field(() => Page, { nullable: true })
@ManyToOne(() => Page, page => page.offers, { onDelete: 'CASCADE' })
pageCreator?: Page
@Field(() => Int)
@Column()
spaceId!: number
@Field(() => Space, { nullable: true })
@ManyToOne(() => Space, space => space.posts, { onDelete: 'CASCADE' })
space?: Space
@Field()
@Column()
title: string
@Field({ nullable: true })
@Column({ nullable: true })
workplace?: string
@Field({ nullable: true })
@Column({ nullable: true })
address?: string
@Field({ nullable: true })
@Column({ nullable: false, default: false })
recruiting?: boolean
@Field(() => [OfferApplication], { nullable: true })
@OneToMany(() => OfferApplication, offerApplication => offerApplication.offer)
applications: OfferApplication[]
@Field({ nullable: true })
@Column({ nullable: true })
employmentType?: string
@Field({ nullable: true })
@Column({ nullable: true })
salaryRange?: string
@Field({ nullable: true })
@Column({ nullable: true })
department?: string
@Field({ nullable: true })
@Column({ nullable: true })
requirements?: string
@Field({ nullable: true })
@Column({ nullable: true })
benefits?: string
@Field({ nullable: true })
@Column({ nullable: true })
description?: string
@Field({ nullable: true })
@Column({ nullable: true })
photo?: string
@Field({ nullable: true })
photoUrl?: string
@Field(() => Int, { nullable: true })
applicationsNo?: number
@Field(() => String, { nullable: true })
applicationStatus?: 'applied' | 'accepted' | 'rejected'
}
+31
View File
@@ -0,0 +1,31 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, Entity, ManyToOne, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm'
import { User, Offer } from '@entities'
@ObjectType()
@Entity()
export class OfferApplication extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field(() => Int)
@Column()
userId!: number
@Field(() => User)
@ManyToOne(() => User, user => user.applications, { onDelete: 'CASCADE', cascade: true })
user?: User
@Field(() => Int)
@Column()
offerId!: number
@Field(() => Offer)
@ManyToOne(() => Offer, offer => offer.applications, { onDelete: 'CASCADE', cascade: true })
offer?: Offer
@Field()
@Column({ default: 'applied' })
status!: 'applied' | 'accepted' | 'rejected'
}
+84
View File
@@ -0,0 +1,84 @@
import { Comment, Offer, Post, User, PageFollow } from '@entities'
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, CreateDateColumn, Entity, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
@ObjectType()
@Entity()
export class Page extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field(() => String, { nullable: true })
@CreateDateColumn()
createdAt?: Date
@Field(() => String, { nullable: true })
@UpdateDateColumn()
updatedAt?: Date
@Field(() => [User], { nullable: true })
@ManyToMany(() => User, { cascade: true })
@JoinTable()
owners?: User[]
@Field(() => Boolean, { nullable: true })
ownerStatus?: boolean
@OneToMany(() => PageFollow, pageFollow => pageFollow.page)
pageFollows?: PageFollow[]
@Field(() => Boolean, { nullable: true })
followStatus?: boolean
@Field(() => Int)
followerNumber?: number
@Field()
@Column({ unique: true, nullable: false })
pageName?: string
@Field({ nullable: true })
@Column({ unique: true, nullable: true })
email?: string
@OneToMany(() => Post, post => post.pageCreator)
posts?: Post[]
@OneToMany(() => Offer, offer => offer.pageCreator)
offers?: Offer[]
@Field(() => [Comment], { nullable: true })
@OneToMany(() => Comment, comment => comment.creator)
comments?: Comment[]
@Field({ nullable: true })
@Column({ nullable: true })
avatar?: string
@Field({ nullable: true })
avatarUrl?: string
@Field({ nullable: true })
@Column({ nullable: true })
coverPhoto?: string
@Field({ nullable: true })
coverPhotoUrl?: string
@Field({ nullable: true })
@Column({ nullable: true })
fullPageName?: string
@Field({ nullable: true })
@Column({ nullable: true })
headline?: string
@Field({ nullable: true })
@Column({ nullable: true })
address?: string
@Field({ nullable: true })
@Column({ nullable: true })
about?: string
}
+24
View File
@@ -0,0 +1,24 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Entity, ManyToOne, PrimaryColumn } from 'typeorm'
import { Page } from './Page'
import { User } from './User'
@ObjectType()
@Entity()
export class PageFollow extends BaseEntity {
@Field(() => Int)
@PrimaryColumn()
userId!: number
@Field(() => User)
@ManyToOne(() => User, user => user.pageFollows, { onDelete: 'CASCADE' })
user?: User
@Field(() => Int)
@PrimaryColumn()
pageId!: number
@Field(() => Page)
@ManyToOne(() => Page, page => page.pageFollows, { onDelete: 'CASCADE' })
page?: Page
}
+73
View File
@@ -0,0 +1,73 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, CreateDateColumn, Entity, JoinTable, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
import { Vote, User, Space, Comment, Tag, Page } from '@entities'
@ObjectType()
@Entity()
export class Post extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field(() => String)
@CreateDateColumn()
createdAt?: Date
@Field(() => String)
@UpdateDateColumn()
updatedAt?: Date
@Field()
@Column()
title!: string
@Field()
@Column()
text!: string
@Field(() => String)
@Column({ default: 'user' })
creatorType?: 'page' | 'user'
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
creatorId?: number
@Field(() => User, { nullable: true })
@ManyToOne(() => User, user => user.posts, { onDelete: 'CASCADE' })
creator?: User
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
pageCreatorId?: number
@Field(() => Page, { nullable: true })
@ManyToOne(() => Page, page => page.posts, { onDelete: 'CASCADE' })
pageCreator?: Page
@OneToMany(() => Vote, vote => vote.post)
votes?: Vote[]
@Field(() => Int)
@Column({ type: 'int', default: 0 })
points!: number
@Field(() => Int, { nullable: true })
voteStatus?: number
@Field(() => Int)
@Column()
spaceId!: number
@Field(() => Space)
@ManyToOne(() => Space, space => space.posts, { onDelete: 'CASCADE' })
space!: Space
@OneToMany(() => Comment, comment => comment.post)
comments?: Comment[]
@Field(() => [Tag], { nullable: true })
@ManyToMany(() => Tag, { cascade: true })
@JoinTable() // Put @JoinTable() on owner side of a MTM relationship
tags: Tag[]
}
+54
View File
@@ -0,0 +1,54 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
import { User } from './User'
@ObjectType()
@Entity()
export class Qualification extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field()
@Column()
name!: string
@Field()
@Column()
issuingOrganisation!: string
@Field(() => Date, { nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
issuanceDate?: Date
@Field()
@Column()
expire!: boolean
@Field(() => Date, { nullable: true })
@Column({ nullable: true, type: 'timestamptz' })
expirationDate?: Date
@Field({ nullable: true })
@Column({ nullable: true })
credentialID?: string
@Field({ nullable: true })
@Column({ nullable: true })
credentialURL?: string
@Field(() => Int, { nullable: true })
@Column({ nullable: true })
userId?: number
@Field(() => User, { nullable: true })
@ManyToOne(() => User, user => user.qualifications, { onDelete: 'CASCADE' })
user?: User
@Field({ nullable: true })
@Column({ nullable: true })
photo?: string
@Field({ nullable: true })
photoUrl?: string
}
+73
View File
@@ -0,0 +1,73 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, CreateDateColumn, Entity, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
import { Post, SpaceSubscription, User } from '@entities'
@ObjectType()
@Entity()
export class Space extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field(() => String)
@CreateDateColumn()
createdAt?: Date
@Field(() => String)
@UpdateDateColumn()
updatedAt?: Date
@Field()
@Column({ unique: true })
spaceName!: string
@OneToMany(() => Post, post => post.space)
posts?: Post[]
@OneToMany(() => SpaceSubscription, spaceSubscription => spaceSubscription.space)
spaceSubscriptions?: SpaceSubscription[]
@Field(() => Boolean, { nullable: true })
subscriptionStatus?: boolean
@Field(() => Int)
subscriberNumber?: number
@Field(() => Boolean)
modStatus?: boolean
@Field(() => [User], { nullable: true })
@ManyToMany(() => User, { cascade: true })
@JoinTable()
mods?: User[]
@Field({ nullable: true })
@Column({ nullable: true })
avatar?: string // S3 Key (Location) to image
@Field({ nullable: true })
avatarUrl?: string
@Field({ nullable: true })
@Column({ nullable: true })
coverPhoto?: string
@Field({ nullable: true })
coverPhotoUrl?: string
@Field({ nullable: true })
@Column({ nullable: true })
fullSpaceName?: string
@Field({ nullable: true })
@Column({ nullable: true })
rules?: string
@Field({ nullable: true })
@Column({ nullable: true })
headline?: string
@Field({ nullable: true })
@Column({ nullable: true })
about?: string
}
+23
View File
@@ -0,0 +1,23 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Entity, ManyToOne, PrimaryColumn } from 'typeorm'
import { Space, User } from '@entities'
@ObjectType()
@Entity()
export class SpaceSubscription extends BaseEntity {
@Field(() => Int)
@PrimaryColumn()
userId!: number
@Field(() => User)
@ManyToOne(() => User, user => user.spaceSubscriptions, { onDelete: 'CASCADE' })
user?: User
@Field(() => Int)
@PrimaryColumn()
spaceId!: number
@Field(() => Space)
@ManyToOne(() => Space, space => space.spaceSubscriptions, { onDelete: 'CASCADE' })
space?: Space
}
+14
View File
@@ -0,0 +1,14 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm'
@ObjectType()
@Entity()
export class Tag extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id: number
@Field()
@Column()
name: string
}
+124
View File
@@ -0,0 +1,124 @@
import { Comment, CommentVote, CV, EducationItem, Experience, Offer, OfferApplication, PageFollow, Post, Qualification, SpaceSubscription, UserFollow, Vote } from '@entities'
import { Field, Float, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
@ObjectType()
@Entity()
export class User extends BaseEntity {
@Field(() => Int)
@PrimaryGeneratedColumn()
id!: number
@Field(() => String, { nullable: true })
@CreateDateColumn()
createdAt?: Date
@Field(() => String, { nullable: true })
@UpdateDateColumn()
updatedAt?: Date
@Field()
@Column({ unique: true, nullable: false })
username?: string
@Field({ nullable: true })
@Column({ unique: true, nullable: false })
email?: string
@Column({ nullable: false })
password?: string
@OneToMany(() => Post, post => post.creator)
posts?: Post[]
@OneToMany(() => Offer, offer => offer.creator)
offers?: Offer[]
@OneToMany(() => EducationItem, educationItem => educationItem.user, { cascade: true })
educationItems?: EducationItem[]
@OneToMany(() => Experience, experience => experience.user, { cascade: true })
experiences?: Experience[]
@OneToMany(() => Qualification, qualification => qualification.user, { cascade: true })
qualifications?: Qualification[]
@Field(() => [String], { nullable: true })
@Column(`simple-array`, { nullable: true })
skills?: string[]
@OneToMany(() => CV, cv => cv.user)
cvs?: CV[]
@OneToMany(() => Vote, vote => vote.user)
votes?: Vote[]
@OneToMany(() => SpaceSubscription, spaceSubscription => spaceSubscription.user)
spaceSubscriptions?: SpaceSubscription[]
@OneToMany(() => PageFollow, pageFollow => pageFollow.user)
pageFollows?: PageFollow[]
// One user can follow many
@OneToMany(() => UserFollow, userFollow => userFollow.followingUser)
userFollowings?: UserFollow[]
// One user can be followed by many
@OneToMany(() => UserFollow, userFollow => userFollow.followedUser)
userFolloweds?: PageFollow[]
@Field(() => [Comment], { nullable: true })
@OneToMany(() => Comment, comment => comment.creator)
comments?: Comment[]
@OneToMany(() => CommentVote, commentVote => commentVote.user)
commentVotes?: CommentVote[]
@Field(() => [OfferApplication], { nullable: true })
@OneToMany(() => OfferApplication, offerApplication => offerApplication.user)
applications?: OfferApplication[]
@Field({ nullable: true })
@Column({ nullable: true })
avatar?: string // S3 Key (Location) to image
@Field({ nullable: true })
avatarUrl?: string
@Field({ nullable: true })
@Column({ nullable: true })
coverPhoto?: string
@Field({ nullable: true })
coverPhotoUrl?: string
@Field(() => Int)
followerNumber?: number
@Field(() => Int)
followingNumber?: number
@Field({ nullable: true })
@Column({ nullable: true })
fullName?: string
@Field({ nullable: true })
@Column({ nullable: true })
headline?: string
@Field({ nullable: true })
@Column({ nullable: true })
address?: string
@Field({ nullable: true })
@Column({ nullable: true })
about?: string
@Field(() => Float, { nullable: true })
@Column({ type: 'float', nullable: true })
mostRecentLatitude?: number
@Field(() => Float, { nullable: true })
@Column({ type:'float', nullable: true })
mostRecentLongitude?: number
}
+23
View File
@@ -0,0 +1,23 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Entity, ManyToOne, PrimaryColumn } from 'typeorm'
import { User } from './User'
@ObjectType()
@Entity()
export class UserFollow extends BaseEntity {
@Field(() => Int)
@PrimaryColumn()
followingUserId!: number
@Field(() => User)
@ManyToOne(() => User, user => user.userFollowings, { onDelete: 'CASCADE' })
followingUser?: User
@Field(() => Int)
@PrimaryColumn()
followedUserId!: number
@Field(() => User)
@ManyToOne(() => User, user => user.userFolloweds, { onDelete: 'CASCADE' })
followedUser?: User
}
+27
View File
@@ -0,0 +1,27 @@
import { Post, User } from '@entities'
import { Field, Int, ObjectType } from 'type-graphql'
import { BaseEntity, Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm'
@ObjectType()
@Entity()
export class Vote extends BaseEntity {
@Field(() => Int)
@Column({ type: 'int' })
value!: number
@Field(() => Int)
@PrimaryColumn()
userId!: number
@Field(() => User)
@ManyToOne(() => User, user => user.votes, { onDelete: 'CASCADE' })
user?: User
@Field(() => Int)
@PrimaryColumn()
postId!: number
@Field(() => Post)
@ManyToOne(() => Post, post => post.votes, { onDelete: 'CASCADE' })
post?: Post
}
+18
View File
@@ -0,0 +1,18 @@
export * from './User'
export * from './Post'
export * from './Vote'
export * from './Space'
export * from './SpaceSubscription'
export * from './Comment'
export * from './CommentVote'
export * from './OfferApplication'
export * from './Offer'
export * from './Tag'
export * from './Conversation'
export * from './Page'
export * from './PageFollow'
export * from './UserFollow'
export * from './EducationItem'
export * from './Experience'
export * from './Qualification'
export * from './CV'
@@ -0,0 +1,12 @@
import { ConversationInFirestore } from '@collections'
import { Field, ObjectType } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType()
export class CreateConversationResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => ConversationInFirestore, { nullable: true })
conversation?: ConversationInFirestore
}
+20
View File
@@ -0,0 +1,20 @@
import { FileUpload, GraphQLUpload } from 'graphql-upload'
import { Field, InputType } from 'type-graphql'
@InputType()
export class EducationItemInput {
@Field({ nullable: true })
school?: string
@Field({ nullable: true })
status?: string
@Field(() => Date, { nullable: true })
startDate?: Date
@Field(() => Date, { nullable: true })
endDate?: Date
@Field(() => GraphQLUpload, { nullable: true })
photo?: Promise<FileUpload>
}
@@ -0,0 +1,12 @@
import { EducationItem } from '@entities'
import { Field, ObjectType } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType()
export class EducationItemResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => EducationItem, { nullable: true })
educationItem?: EducationItem
}
+20
View File
@@ -0,0 +1,20 @@
import { FileUpload, GraphQLUpload } from 'graphql-upload'
import { Field, InputType } from 'type-graphql'
@InputType()
export class ExperienceInput {
@Field({ nullable: true })
title?: string
@Field({ nullable: true })
workplace?: string
@Field(() => Date, { nullable: true })
startDate?: Date
@Field(() => Date, { nullable: true })
endDate?: Date
@Field(() => GraphQLUpload, { nullable: true })
photo?: Promise<FileUpload>
}
+12
View File
@@ -0,0 +1,12 @@
import { Experience } from '@entities'
import { Field, ObjectType } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType()
export class ExperienceResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => Experience, { nullable: true })
experience?: Experience
}
+10
View File
@@ -0,0 +1,10 @@
import { ObjectType, Field } from 'type-graphql'
@ObjectType()
export class FieldError {
@Field()
field: string
@Field()
message: string
}
@@ -0,0 +1,11 @@
import { ObjectType, Field } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType()
export class ForgotPasswordResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => Boolean)
success: boolean
}
+11
View File
@@ -0,0 +1,11 @@
import { User } from '@entities'
import { Field, ObjectType } from 'type-graphql'
@ObjectType()
export class InboxResponse {
@Field(() => String)
firestoreCollectionId: string
@Field(() => User)
partner: User
}
+10
View File
@@ -0,0 +1,10 @@
import { Field, Float, InputType } from 'type-graphql'
@InputType()
export class Location {
@Field(() => Float, { nullable: true })
latitude?: number
@Field(() => Float, { nullable: true })
longitude?: number
}
+14
View File
@@ -0,0 +1,14 @@
import { FileUpload, GraphQLUpload } from 'graphql-upload'
import { Field, InputType } from 'type-graphql'
@InputType()
export class MessageInput {
@Field(() => String)
type!: 'text' | 'image' | 'video' | 'audio' | 'file'
@Field(() => GraphQLUpload, { nullable: true })
upload?: Promise<FileUpload>
@Field(() => String, { nullable: true })
text?: string
}
+80
View File
@@ -0,0 +1,80 @@
import { FileUpload, GraphQLUpload } from 'graphql-upload'
import { Field, InputType, Int } from 'type-graphql'
@InputType()
export class OfferInputCreate {
@Field(() => Int, { nullable: true })
pageId?: number
@Field({ nullable: true })
spaceName?: string
@Field({ nullable: true })
title?: string
@Field({ nullable: true })
workplace?: string
@Field({ nullable: true })
address?: string
@Field({ nullable: true })
recruiting?: boolean
@Field({ nullable: true })
employmentType?: string
@Field({ nullable: true })
salaryRange?: string
@Field({ nullable: true })
department?: string
@Field({ nullable: true })
requirements?: string
@Field({ nullable: true })
benefits?: string
@Field({ nullable: true })
description?: string
@Field(() => GraphQLUpload, { nullable: true })
photo?: Promise<FileUpload>
}
@InputType()
export class OfferInputUpdate {
@Field({ nullable: true })
title?: string
@Field({ nullable: true })
workplace?: string
@Field({ nullable: true })
address?: string
@Field({ nullable: true })
recruiting?: boolean
@Field({ nullable: true })
employmentType?: string
@Field({ nullable: true })
salaryRange?: string
@Field({ nullable: true })
department?: string
@Field({ nullable: true })
requirements?: string
@Field({ nullable: true })
benefits?: string
@Field({ nullable: true })
description?: string
@Field(() => GraphQLUpload, { nullable: true })
photo?: Promise<FileUpload>
}
+12
View File
@@ -0,0 +1,12 @@
import { Offer } from '@entities'
import { Field, ObjectType } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType()
export class OfferResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => Offer, { nullable: true })
offer?: Offer
}
+16
View File
@@ -0,0 +1,16 @@
import { Field, InputType } from 'type-graphql'
@InputType()
export class PageInfo {
@Field({ nullable: true })
fullPageName?: string
@Field({ nullable: true })
headline?: string
@Field({ nullable: true })
address?: string
@Field({ nullable: true })
about?: string
}
+12
View File
@@ -0,0 +1,12 @@
import { Page } from '@entities'
import { Field, ObjectType } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType()
export class PageResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => Page, { nullable: true })
page?: Page
}
+11
View File
@@ -0,0 +1,11 @@
import { Comment } from '@entities'
import { Field, ObjectType } from 'type-graphql'
@ObjectType()
export class PaginatedComments {
@Field(() => [Comment])
comments: Comment[]
@Field(() => Boolean)
hasMore: boolean
}
+11
View File
@@ -0,0 +1,11 @@
import { Offer } from '@entities'
import { Field, ObjectType } from 'type-graphql'
@ObjectType()
export class PaginatedOffers {
@Field(() => [Offer])
offers: Offer[]
@Field(() => Boolean)
hasMore: boolean
}
+11
View File
@@ -0,0 +1,11 @@
import { Post } from '@entities'
import { Field, ObjectType } from 'type-graphql'
@ObjectType()
export class PaginatedPosts {
@Field(() => [Post])
posts: Post[]
@Field(() => Boolean)
hasMore: boolean
}
+10
View File
@@ -0,0 +1,10 @@
import { ObjectType, Field } from 'type-graphql'
@ObjectType()
export class PhotoResponse {
@Field(() => String, { nullable: true })
key?: string
@Field(() => String, { nullable: true })
url?: string
}
+31
View File
@@ -0,0 +1,31 @@
import { Field, InputType, Int } from 'type-graphql'
@InputType()
export class PostInputCreate {
@Field(() => Int, { nullable: true })
pageId?: number
@Field({ nullable: true })
spaceName?: string
@Field({ nullable: true })
title?: string
@Field({ nullable: true })
text?: string
@Field(() => [Int], { nullable: true })
tags?: number[]
}
@InputType()
export class PostInputUpdate {
@Field({ nullable: true })
title?: string
@Field({ nullable: true })
text?: string
@Field(() => [Int], { nullable: true })
tags?: number[]
}
+12
View File
@@ -0,0 +1,12 @@
import { Post } from '@entities'
import { Field, ObjectType } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType()
export class PostResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => Post, { nullable: true })
post?: Post
}
+29
View File
@@ -0,0 +1,29 @@
import { FileUpload, GraphQLUpload } from 'graphql-upload'
import { Field, InputType } from 'type-graphql'
@InputType()
export class QualificationInput {
@Field()
name!: string
@Field()
issuingOrganisation!: string
@Field(() => Date, { nullable: true })
issuanceDate?: Date
@Field()
expire: boolean
@Field(() => Date, { nullable: true })
expirationDate?: Date
@Field({ nullable: true })
credentialID?: string
@Field({ nullable: true })
credentialURL?: string
@Field(() => GraphQLUpload, { nullable: true })
photo?: Promise<FileUpload>
}
@@ -0,0 +1,12 @@
import { Qualification } from '@entities'
import { Field, ObjectType } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType()
export class QualificationResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => Qualification, { nullable: true })
qualification?: Qualification
}
+16
View File
@@ -0,0 +1,16 @@
import { Field, InputType } from 'type-graphql'
@InputType()
export class SpaceInfo {
@Field({ nullable: true })
fullSpaceName?: string
@Field({ nullable: true })
headline?: string
@Field({ nullable: true })
rules?: string
@Field({ nullable: true })
about?: string
}
+12
View File
@@ -0,0 +1,12 @@
import { Space } from '@entities'
import { Field, ObjectType } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType()
export class SpaceResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => Space, { nullable: true })
space?: Space
}
+16
View File
@@ -0,0 +1,16 @@
import { Field, ObjectType } from 'type-graphql'
@ObjectType()
export class UploadedFileResponse {
@Field()
filename!: String
@Field()
mimetype!: String
@Field()
encoding!: String
@Field()
url!: String
}
+16
View File
@@ -0,0 +1,16 @@
import { Field, InputType } from 'type-graphql'
@InputType()
export class UserInfo {
@Field({ nullable: true })
fullName?: string
@Field({ nullable: true })
headline?: string
@Field({ nullable: true })
address?: string
@Field({ nullable: true })
about?: string
}
+12
View File
@@ -0,0 +1,12 @@
import { User } from '../entities'
import { ObjectType, Field } from 'type-graphql'
import { FieldError } from './FieldError'
@ObjectType() // ObjectType is for return values
export class UserResponse {
@Field(() => [FieldError], { nullable: true })
errors?: FieldError[]
@Field(() => User, { nullable: true })
user?: User
}
+27
View File
@@ -0,0 +1,27 @@
export * from './FieldError'
export * from './UserResponse'
export * from './ForgotPasswordResponse'
export * from './PaginatedPosts'
export * from './PostInput'
export * from './PostResponse'
export * from './PaginatedComments'
export * from './OfferResponse'
export * from './OfferInput'
export * from './PaginatedOffers'
export * from './UploadedFileResponse'
export * from './MessageInput'
export * from './PhotoResponse'
export * from './PageResponse'
export * from './SpaceResponse'
export * from './UserInfo'
export * from './EducationItemResponse'
export * from './EducationItemInput'
export * from './ExperienceInput'
export * from './ExperienceResponse'
export * from './QualificationInput'
export * from './QualificationResponse'
export * from './PageInfo'
export * from './SpaceInfo'
export * from './InboxResponse'
export * from './CreateConversationReponse'
export * from './Location'
+140
View File
@@ -0,0 +1,140 @@
import { __prod__, COOKIE_NAME } from '@constants'
import { createApplicantsNoLoader, createApplicationLoader, createApplicationStatusLoader, createCommentVoteLoader, createFollowerNumberLoader, createFollowLoader, createModStatusLoader, createOwnerStatusLoader, createPageLoader, createSpaceLoader, createSubscriberNumberLoader, createSubscriptionLoader, createTagLoader, createUserFollowerNumberLoader, createUserFollowingNumberLoader, createUserFollowLoader, createUserLoader, createVoteLoader } from '@loaders'
import { CommentResolver, ConversationResolver, CVResolver, EducationItemResolver, ExperienceResolver, OfferResolver, PageResolver, PostResolver, QualificationResolver, QuicksightResolver, S3Resolver, SpaceResolver, TagResolver, UserResolver } from '@resolvers'
import typeORMSource from '@src/type-orm.source'
import { Context } from '@types'
import { ApolloServer } from 'apollo-server-express'
import connectRedis from 'connect-redis'
import cors from 'cors'
import express from 'express'
import session from 'express-session'
import { graphqlUploadExpress } from 'graphql-upload'
import Redis from 'ioredis'
import { buildSchema } from 'type-graphql'
const main = async () => {
const orm = await typeORMSource.initialize()
// await orm.dropDatabase()
await orm.runMigrations()
const app = express()
app.options('*', cors({
credentials: true,
origin: process.env.FRONTEND_ORIGIN,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}))
app.use(cors({
credentials: true,
origin: process.env.FRONTEND_ORIGIN,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}))
app.use(express.json({
limit: '1gb',
verify: (_, __, buf) => {
if (buf.byteLength / (1024 * 1024) > 1024) {
throw new Error(`${process.env.NON_API_ERROR_PREFIX}Request too large. It must be less than 1GB.${process.env.NON_API_ERROR_SUFFIX}`)
}
}
}))
const RedisStore = connectRedis(session)
const redis = new Redis(process.env.REDIS_URL)
// await redis.flushall()
app.set('trust proxy', true)
app.use(
session({
name: COOKIE_NAME,
store: new RedisStore({
client: redis,
disableTTL: true,
disableTouch: true
}),
cookie: { // lax + secure false -> chrome dev; none + secure true -> apollo studio, prod: lax + secure
maxAge: 1000 * 60 * 60 * 24 * 365 * 10,
httpOnly: false,
sameSite: 'lax',
// sameSite: 'none',
secure: true,
// secure: false,
// domain: __prod__ ? process.env.DOMAIN_SUFFIX : undefined
domain: process.env.DOMAIN_SUFFIX
},
secret: process.env.REDIS_SECRET,
saveUninitialized: false,
resave: false
})
)
app.listen(parseInt(process.env.API_PORT), () => {
if (!__prod__) {
console.log(`Server started on localhost:${process.env.API_PORT}.`)
}
else {
console.log(`Server started at ${process.env.BACKEND_ORIGIN}.`)
}
})
app.get('/', (_, res) => {
res.send('Start querying at /graphql.')
})
const apolloServer = new ApolloServer({
introspection: true,
schema: await buildSchema({
resolvers: [
UserResolver,
PostResolver,
SpaceResolver,
CommentResolver,
OfferResolver,
TagResolver,
S3Resolver,
ConversationResolver,
PageResolver,
EducationItemResolver,
ExperienceResolver,
QualificationResolver,
CVResolver,
QuicksightResolver
],
validate: false
}),
context: ({ req, res }): Context => ({
orm,
req,
res,
redis,
userLoader: createUserLoader(),
voteLoader: createVoteLoader(),
spaceLoader: createSpaceLoader(),
commentVoteLoader: createCommentVoteLoader(),
tagLoader: createTagLoader(),
subscriptionLoader: createSubscriptionLoader(),
modStatusLoader: createModStatusLoader(),
pageLoader: createPageLoader(),
followLoader: createFollowLoader(),
ownerStatusLoader: createOwnerStatusLoader(),
userFollowLoader: createUserFollowLoader(),
applicationStatusLoader: createApplicationStatusLoader(),
applicationLoader: createApplicationLoader(),
applicantsNoLoader: createApplicantsNoLoader(),
followerNumberLoader: createFollowerNumberLoader(),
subscriberNumberLoader: createSubscriberNumberLoader(),
userFollowerNumberLoader: createUserFollowerNumberLoader(),
userFollowingNumberLoader: createUserFollowingNumberLoader()
})
})
await apolloServer.start()
apolloServer.applyMiddleware({
app,
cors: false
})
app.use(graphqlUploadExpress())
}
main().catch(console.error)
+20
View File
@@ -0,0 +1,20 @@
import { OfferApplication } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createApplicantsNoLoader = () =>
new DataLoader<number, number>(async (offerIds) => {
const result: { count: string, offerId: number }[] = await orm.createQueryBuilder()
.select('COUNT(*), "offerId"')
.from(OfferApplication, 'oa')
.orWhere(offerIds.map(id => ({ offerId: id })))
.groupBy('"offerId"')
.execute()
return offerIds.map(id => {
const count = result.find(r => r.offerId == id)?.count
if (count) {
return parseInt(count)
}
return 0
})
})
+11
View File
@@ -0,0 +1,11 @@
import { OfferApplication } from '@entities'
import DataLoader from 'dataloader'
import { In } from 'typeorm'
export const createApplicationLoader = () =>
new DataLoader<number, OfferApplication[]>(async (offerIds) => {
const applications = await OfferApplication.find({ where: { offerId: In(offerIds as number[]) }, relations: { user: true } })
return offerIds.map(offerId => {
return applications.filter(application => application.offerId === offerId)
})
})
@@ -0,0 +1,15 @@
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createApplicationStatusLoader = () =>
new DataLoader<{ offerId: number, userId: number }, String | null>(async (keys) => {
const applications: { id: number, offerId: number, userId: number, status: string }[] = await orm.createQueryBuilder()
.select('*')
.from('offer_application', 'oa')
.orWhere(keys)
.execute()
return keys.map(key => {
const application = applications.find(a => a.offerId == key.offerId && a.userId == key.userId)
return application ? application.status : null
})
})
+18
View File
@@ -0,0 +1,18 @@
import { CommentVote } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createCommentVoteLoader = () =>
new DataLoader<{ commentId: number, userId: number }, CommentVote | null>(async (keys) => {
const commentVotes: CommentVote[] = await orm.createQueryBuilder()
.select('*')
.from(CommentVote, 'cv')
.orWhereInIds(keys)
.execute()
const map: Record<string, CommentVote> = {}
commentVotes.forEach(cv => {
map[`${cv.userId}-${cv.commentId}`] = cv
})
return keys.map(key => map[`${key.userId}-${key.commentId}`])
})
+16
View File
@@ -0,0 +1,16 @@
import { PageFollow } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createFollowLoader = () =>
new DataLoader<{ pageId: number, userId: number }, boolean>(async (keys) => {
const follows: PageFollow[] = await orm.createQueryBuilder()
.select('*')
.from(PageFollow, 'pf')
.orWhereInIds(keys)
.execute()
return keys.map(key => {
const follow = follows.find(fol => fol.pageId == key.pageId && fol.userId == key.userId)
return follow ? true : false
})
})
+20
View File
@@ -0,0 +1,20 @@
import { PageFollow } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createFollowerNumberLoader = () =>
new DataLoader<number, number>(async (pageIds) => {
const result: { count: string, pageId: number }[] = await orm.createQueryBuilder()
.select('COUNT(*), "pageId"')
.from(PageFollow, 'pf')
.orWhere(pageIds.map(id => ({ pageId: id })))
.groupBy('"pageId"')
.execute()
return pageIds.map(id => {
const count = result.find(r => r.pageId == id)?.count
if (count) {
return parseInt(count)
}
return 0
})
})
+16
View File
@@ -0,0 +1,16 @@
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createModStatusLoader = () =>
new DataLoader<{ spaceId: number, userId: number }, boolean>(async (keys) => {
const modProofs: { spaceId: number, userId: number }[] = await orm.createQueryBuilder()
.select('*')
.from('space_mods_user', 'smu')
.orWhereInIds(keys)
.execute()
return keys.map(key => {
const proof = modProofs.find(mp => mp.spaceId == key.spaceId && mp.userId == key.userId)
return proof ? true : false
})
})
+15
View File
@@ -0,0 +1,15 @@
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createOwnerStatusLoader = () =>
new DataLoader<{ pageId: number, userId: number }, boolean>(async (keys) => {
const ownerProofs: { pageId: number, userId: number }[] = await orm.createQueryBuilder()
.select('*')
.from('page_owners_user', 'pou')
.orWhereInIds(keys)
.execute()
return keys.map(key => {
const ownerProof = ownerProofs.find(o => o.pageId == key.pageId && o.userId == key.userId)
return ownerProof ? true : false
})
})
+15
View File
@@ -0,0 +1,15 @@
import DataLoader from 'dataloader'
import { In } from 'typeorm'
import { Page, User } from '@entities'
export const createPageLoader = () =>
new DataLoader<number, User>(async (pageIds) => {
const pages = await Page.findBy({ id: In(pageIds as number[]) })
const map: Record<number, User> = {}
pages.forEach(p => {
map[p.id] = p
})
return pageIds.map(id => map[id])
})
+15
View File
@@ -0,0 +1,15 @@
import DataLoader from 'dataloader'
import { In } from 'typeorm'
import { Space } from '@entities'
export const createSpaceLoader = () =>
new DataLoader<number, Space>(async (spaceIds) => {
const spaces = await Space.findBy({ id: In(spaceIds as number[]) })
const map: Record<number, Space> = {}
spaces.forEach(s => {
map[s.id] = s
})
return spaceIds.map(id => map[id])
})
@@ -0,0 +1,20 @@
import { SpaceSubscription } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createSubscriberNumberLoader = () =>
new DataLoader<number, number>(async (spaceIds) => {
const result: { count: string, spaceId: number }[] = await orm.createQueryBuilder()
.select('COUNT(*), "spaceId"')
.from(SpaceSubscription, 'ss')
.orWhere(spaceIds.map(id => ({ spaceId: id })))
.groupBy('"spaceId"')
.execute()
return spaceIds.map(id => {
const count = result.find(r => r.spaceId == id)?.count
if (count) {
return parseInt(count)
}
return 0
})
})
+16
View File
@@ -0,0 +1,16 @@
import { SpaceSubscription } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createSubscriptionLoader = () =>
new DataLoader<{ spaceId: number, userId: number }, boolean>(async (keys) => {
const subscriptions: SpaceSubscription[] = await orm.createQueryBuilder()
.select('*')
.from(SpaceSubscription, 'ss')
.orWhereInIds(keys)
.execute()
return keys.map(key => {
const subscription = subscriptions.find(sub => sub.spaceId == key.spaceId && sub.userId == key.userId)
return subscription ? true : false
})
})
+27
View File
@@ -0,0 +1,27 @@
import { Tag } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createTagLoader = () =>
new DataLoader<number, Tag[]>(async (postIds) => {
const tagItems: { postId: number, tagId: number }[] = await orm.query(`
SELECT * FROM post_tags_tag WHERE "postId" IN (${postIds.join(', ')});
`)
const tagIds = tagItems.map(ti => ti.tagId)
if (tagIds.length > 0) {
const tags: Tag[] = await orm.query(`
SELECT * FROM tag WHERE id IN (${tagIds.join(', ')});
`)
return postIds.map(id => {
const postTagItems = tagItems.filter(ti => ti.postId == id)
const postTagIds = postTagItems.map(pti => pti.tagId)
return tags.filter(t => postTagIds.includes(t.id))
})
}
else {
const newArr = postIds.map(id => [])
return newArr
}
})
+16
View File
@@ -0,0 +1,16 @@
import { UserFollow } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createUserFollowLoader = () =>
new DataLoader<{ followedUserId: number, followingUserId: number }, boolean>(async (keys) => {
const follows: UserFollow[] = await orm.createQueryBuilder()
.select('*')
.from(UserFollow, 'uf')
.orWhereInIds(keys)
.execute()
return keys.map(key => {
const follow = follows.find(fol => fol.followedUserId == key.followedUserId && fol.followingUserId == key.followingUserId)
return follow ? true : false
})
})
@@ -0,0 +1,20 @@
import { UserFollow } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createUserFollowerNumberLoader = () =>
new DataLoader<number, number>(async (userIds) => {
const result: { count: string, followedUserId: number }[] = await orm.createQueryBuilder()
.select('COUNT(*), "followedUserId"')
.from(UserFollow, 'uf')
.orWhere(userIds.map(id => ({ followedUserId: id })))
.groupBy('"followedUserId"')
.execute()
return userIds.map(id => {
const count = result.find(r => r.followedUserId == id)?.count
if (count) {
return parseInt(count)
}
return 0
})
})
@@ -0,0 +1,20 @@
import { UserFollow } from '@entities'
import orm from '@src/type-orm.source'
import DataLoader from 'dataloader'
export const createUserFollowingNumberLoader = () =>
new DataLoader<number, number>(async (userIds) => {
const result: { count: string, followingUserId: number }[] = await orm.createQueryBuilder()
.select('COUNT(*), "followingUserId"')
.from(UserFollow, 'uf')
.orWhere(userIds.map(id => ({ followingUserId: id })))
.groupBy('"followingUserId"')
.execute()
return userIds.map(id => {
const count = result.find(r => r.followingUserId == id)?.count
if (count) {
return parseInt(count)
}
return 0
})
})
+15
View File
@@ -0,0 +1,15 @@
import DataLoader from 'dataloader'
import { In } from 'typeorm'
import { User } from '@entities'
export const createUserLoader = () =>
new DataLoader<number, User>(async (userIds) => {
const users = await User.findBy({ id: In(userIds as number[]) })
const map: Record<number, User> = {}
users.forEach(u => {
map[u.id] = u
})
return userIds.map(id => map[id])
})
+18
View File
@@ -0,0 +1,18 @@
import DataLoader from 'dataloader'
import { Vote } from '@entities'
import orm from '@src/type-orm.source'
export const createVoteLoader = () =>
new DataLoader<{ postId: number, userId: number }, Vote | null>(async (keys) => {
const votes: Vote[] = await orm.createQueryBuilder()
.select('*')
.from(Vote, 'v')
.orWhereInIds(keys)
.execute()
const map: Record<string, Vote> = {}
votes.forEach(v => {
map[`${v.userId}-${v.postId}`] = v
})
return keys.map(key => map[`${key.userId}-${key.postId}`])
})
+18
View File
@@ -0,0 +1,18 @@
export * from './createUserLoader'
export * from './createVoteLoader'
export * from './createSpaceLoader'
export * from './createCommentVoteLoader'
export * from './createTagLoader'
export * from './createSubscriptionLoader'
export * from './createModStatusLoader'
export * from './createPageLoader'
export * from './createFollowLoader'
export * from './createOwnerStatusLoader'
export * from './createUserFollowLoader'
export * from './createApplicationStatusLoader'
export * from './createApplicationLoader'
export * from './createApplicantsNoLoader'
export * from './createFollowerNumberLoader'
export * from './createSubscriberNumberLoader'
export * from './createUserFollowerNumberLoader'
export * from './createUserFollowingNumberLoader'
+1
View File
@@ -0,0 +1 @@
export * from './isAuth'
+12
View File
@@ -0,0 +1,12 @@
import { Context } from '@types'
import { MiddlewareFn } from 'type-graphql'
import { User } from '@entities'
export const isAuth: MiddlewareFn<Context> = async ({ context }, next) => {
if (!context.req.session.userId ||
!(await User.findOne({ where: { id: context.req.session.userId } }))
) {
throw new Error('Not authenticated.')
}
await next()
}
+128
View File
@@ -0,0 +1,128 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class Initial1660494342130 implements MigrationInterface {
name = 'Initial1660494342130'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "user" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "username" character varying NOT NULL, "email" character varying NOT NULL, "password" character varying NOT NULL, "skills" text, "avatar" character varying, "coverPhoto" character varying, "fullName" character varying, "headline" character varying, "address" character varying, "about" character varying, "mostRecentLatitude" double precision, "mostRecentLongitude" double precision, CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "post" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "title" character varying NOT NULL, "text" character varying NOT NULL, "creatorType" character varying NOT NULL DEFAULT 'user', "creatorId" integer, "pageCreatorId" integer, "points" integer NOT NULL DEFAULT '0', "spaceId" integer NOT NULL, CONSTRAINT "PK_be5fda3aac270b134ff9c21cdee" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "vote" ("value" integer NOT NULL, "userId" integer NOT NULL, "postId" integer NOT NULL, CONSTRAINT "PK_16e301aa5efdd994626b2635186" PRIMARY KEY ("userId", "postId"))`);
await queryRunner.query(`CREATE TABLE "space" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "spaceName" character varying NOT NULL, "avatar" character varying, "coverPhoto" character varying, "fullSpaceName" character varying, "rules" character varying, "headline" character varying, "about" character varying, CONSTRAINT "UQ_d3a1749188980a055b0bcd4321d" UNIQUE ("spaceName"), CONSTRAINT "PK_094f5ec727fe052956a11623640" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "space_subscription" ("userId" integer NOT NULL, "spaceId" integer NOT NULL, CONSTRAINT "PK_d628d32fd2777438e93174bc72a" PRIMARY KEY ("userId", "spaceId"))`);
await queryRunner.query(`CREATE TABLE "comment" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "text" character varying NOT NULL, "creatorType" character varying NOT NULL DEFAULT 'user', "creatorId" integer, "pageCreatorId" integer, "postId" integer NOT NULL, "points" integer NOT NULL DEFAULT '0', CONSTRAINT "PK_0b0e4bbc8415ec426f87f3a88e2" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "comment_vote" ("value" integer NOT NULL, "userId" integer NOT NULL, "commentId" integer NOT NULL, CONSTRAINT "PK_9194f426d41fb9a8abc3aae5114" PRIMARY KEY ("userId", "commentId"))`);
await queryRunner.query(`CREATE TABLE "offer_application" ("id" SERIAL NOT NULL, "userId" integer NOT NULL, "offerId" integer NOT NULL, "status" character varying NOT NULL DEFAULT 'applied', CONSTRAINT "PK_d8989a17a7c87cfa5ff63ea84c1" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "offer" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "creatorType" character varying NOT NULL DEFAULT 'user', "creatorId" integer, "pageCreatorId" integer, "spaceId" integer NOT NULL, "title" character varying NOT NULL, "workplace" character varying, "address" character varying, "recruiting" boolean NOT NULL DEFAULT false, "employmentType" character varying, "salaryRange" character varying, "department" character varying, "requirements" character varying, "benefits" character varying, "description" character varying, "photo" character varying, CONSTRAINT "PK_57c6ae1abe49201919ef68de900" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "tag" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_8e4052373c579afc1471f526760" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "conversation" ("id" SERIAL NOT NULL, "participants" integer array NOT NULL, "firestoreCollectionId" character varying NOT NULL, CONSTRAINT "PK_864528ec4274360a40f66c29845" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "page" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "pageName" character varying NOT NULL, "email" character varying, "avatar" character varying, "coverPhoto" character varying, "fullPageName" character varying, "headline" character varying, "address" character varying, "about" character varying, CONSTRAINT "UQ_37f114be85015bdfea6ecfefe6d" UNIQUE ("pageName"), CONSTRAINT "UQ_add9b61d89ec1480843a61c924d" UNIQUE ("email"), CONSTRAINT "PK_742f4117e065c5b6ad21b37ba1f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "page_follow" ("userId" integer NOT NULL, "pageId" integer NOT NULL, CONSTRAINT "PK_ac8f7df2a892662f4edf458ca33" PRIMARY KEY ("userId", "pageId"))`);
await queryRunner.query(`CREATE TABLE "user_follow" ("followingUserId" integer NOT NULL, "followedUserId" integer NOT NULL, CONSTRAINT "PK_bdb4b69fec91b1b2deb7df00b5e" PRIMARY KEY ("followingUserId", "followedUserId"))`);
await queryRunner.query(`CREATE TABLE "education_item" ("id" SERIAL NOT NULL, "school" character varying NOT NULL, "status" character varying, "startDate" TIMESTAMP WITH TIME ZONE, "endDate" TIMESTAMP WITH TIME ZONE, "userId" integer, "photo" character varying, CONSTRAINT "PK_115272e55085398f5a282496bb8" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "experience" ("id" SERIAL NOT NULL, "title" character varying NOT NULL, "workplace" character varying, "startDate" TIMESTAMP WITH TIME ZONE, "endDate" TIMESTAMP WITH TIME ZONE, "userId" integer, "photo" character varying, CONSTRAINT "PK_5e8d5a534100e1b17ee2efa429a" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "qualification" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "issuingOrganisation" character varying NOT NULL, "issuanceDate" TIMESTAMP WITH TIME ZONE, "expire" boolean NOT NULL, "expirationDate" TIMESTAMP WITH TIME ZONE, "credentialID" character varying, "credentialURL" character varying, "userId" integer, "photo" character varying, CONSTRAINT "PK_c8244868552c4364a5264440a66" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "cv" ("id" SERIAL NOT NULL, "filename" character varying NOT NULL, "key" character varying NOT NULL, "userId" integer, CONSTRAINT "PK_4ddf7891daf83c3506efa503bb8" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "post_tags_tag" ("postId" integer NOT NULL, "tagId" integer NOT NULL, CONSTRAINT "PK_e9b7b8e6a07bdccb6a954171676" PRIMARY KEY ("postId", "tagId"))`);
await queryRunner.query(`CREATE INDEX "IDX_b651178cc41334544a7a9601c4" ON "post_tags_tag" ("postId") `);
await queryRunner.query(`CREATE INDEX "IDX_41e7626b9cc03c5c65812ae55e" ON "post_tags_tag" ("tagId") `);
await queryRunner.query(`CREATE TABLE "space_mods_user" ("spaceId" integer NOT NULL, "userId" integer NOT NULL, CONSTRAINT "PK_4768be7da937c7ee02c260fb761" PRIMARY KEY ("spaceId", "userId"))`);
await queryRunner.query(`CREATE INDEX "IDX_6fac656f12453c24f08855fbef" ON "space_mods_user" ("spaceId") `);
await queryRunner.query(`CREATE INDEX "IDX_2172d469a5e9c5e4397bcb94ff" ON "space_mods_user" ("userId") `);
await queryRunner.query(`CREATE TABLE "page_owners_user" ("pageId" integer NOT NULL, "userId" integer NOT NULL, CONSTRAINT "PK_2b7f0419bfbb8f0550592d2cf37" PRIMARY KEY ("pageId", "userId"))`);
await queryRunner.query(`CREATE INDEX "IDX_1c5a8df532d6c129c4f1642b2f" ON "page_owners_user" ("pageId") `);
await queryRunner.query(`CREATE INDEX "IDX_5bd7deaf0c8e15ea861d15efc3" ON "page_owners_user" ("userId") `);
await queryRunner.query(`ALTER TABLE "post" ADD CONSTRAINT "FK_9e91e6a24261b66f53971d3f96b" FOREIGN KEY ("creatorId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "post" ADD CONSTRAINT "FK_605fe7290cfa226a4d444c05ca4" FOREIGN KEY ("pageCreatorId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "post" ADD CONSTRAINT "FK_899a1931431768b2800f695ac60" FOREIGN KEY ("spaceId") REFERENCES "space"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "vote" ADD CONSTRAINT "FK_f5de237a438d298031d11a57c3b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "vote" ADD CONSTRAINT "FK_43cc1af57676ac1b7ec774bd10f" FOREIGN KEY ("postId") REFERENCES "post"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "space_subscription" ADD CONSTRAINT "FK_9f89ed79bb85a199306ce7e3ae1" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "space_subscription" ADD CONSTRAINT "FK_4c2cf829c698bede8b9c8608f10" FOREIGN KEY ("spaceId") REFERENCES "space"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "comment" ADD CONSTRAINT "FK_b6bf60ecb9f6c398e349adff52f" FOREIGN KEY ("creatorId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "comment" ADD CONSTRAINT "FK_37de0ca7e7c8dd9f6a3e1f2b6d3" FOREIGN KEY ("pageCreatorId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "comment" ADD CONSTRAINT "FK_94a85bb16d24033a2afdd5df060" FOREIGN KEY ("postId") REFERENCES "post"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "comment_vote" ADD CONSTRAINT "FK_ade7498b89296b9fb63bcd8dbdd" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "comment_vote" ADD CONSTRAINT "FK_5d77d92a6925ae3fc8da14e1257" FOREIGN KEY ("commentId") REFERENCES "comment"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "offer_application" ADD CONSTRAINT "FK_dcfd5b26f80494128baf7bb7578" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "offer_application" ADD CONSTRAINT "FK_6cb6ed00606a2fb71c05fb3c871" FOREIGN KEY ("offerId") REFERENCES "offer"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "offer" ADD CONSTRAINT "FK_cd84aed04fbd70e9ad48139c727" FOREIGN KEY ("creatorId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "offer" ADD CONSTRAINT "FK_f4621bc66549eda711a0b7c6558" FOREIGN KEY ("pageCreatorId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "offer" ADD CONSTRAINT "FK_69bbcc303da6f7da97744be788a" FOREIGN KEY ("spaceId") REFERENCES "space"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "page_follow" ADD CONSTRAINT "FK_732300fd551f8b08e267018ed99" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "page_follow" ADD CONSTRAINT "FK_b0cd715ee8609422e661e0c43fc" FOREIGN KEY ("pageId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_follow" ADD CONSTRAINT "FK_90ab1c414dac6701f4c126fb736" FOREIGN KEY ("followingUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_follow" ADD CONSTRAINT "FK_4e493e20c9c272183e73da57ddf" FOREIGN KEY ("followedUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "education_item" ADD CONSTRAINT "FK_1dd0c4d5f3e2500b08c3d137b29" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "experience" ADD CONSTRAINT "FK_cbfb1d1219454c9b45f1b3c4274" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "qualification" ADD CONSTRAINT "FK_6550f632c00a2739a1dad0381c1" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "cv" ADD CONSTRAINT "FK_e4b7330e64fd0ecce86720e62f9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "post_tags_tag" ADD CONSTRAINT "FK_b651178cc41334544a7a9601c45" FOREIGN KEY ("postId") REFERENCES "post"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
await queryRunner.query(`ALTER TABLE "post_tags_tag" ADD CONSTRAINT "FK_41e7626b9cc03c5c65812ae55e8" FOREIGN KEY ("tagId") REFERENCES "tag"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
await queryRunner.query(`ALTER TABLE "space_mods_user" ADD CONSTRAINT "FK_6fac656f12453c24f08855fbefc" FOREIGN KEY ("spaceId") REFERENCES "space"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
await queryRunner.query(`ALTER TABLE "space_mods_user" ADD CONSTRAINT "FK_2172d469a5e9c5e4397bcb94ff2" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
await queryRunner.query(`ALTER TABLE "page_owners_user" ADD CONSTRAINT "FK_1c5a8df532d6c129c4f1642b2ff" FOREIGN KEY ("pageId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
await queryRunner.query(`ALTER TABLE "page_owners_user" ADD CONSTRAINT "FK_5bd7deaf0c8e15ea861d15efc36" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "page_owners_user" DROP CONSTRAINT "FK_5bd7deaf0c8e15ea861d15efc36"`);
await queryRunner.query(`ALTER TABLE "page_owners_user" DROP CONSTRAINT "FK_1c5a8df532d6c129c4f1642b2ff"`);
await queryRunner.query(`ALTER TABLE "space_mods_user" DROP CONSTRAINT "FK_2172d469a5e9c5e4397bcb94ff2"`);
await queryRunner.query(`ALTER TABLE "space_mods_user" DROP CONSTRAINT "FK_6fac656f12453c24f08855fbefc"`);
await queryRunner.query(`ALTER TABLE "post_tags_tag" DROP CONSTRAINT "FK_41e7626b9cc03c5c65812ae55e8"`);
await queryRunner.query(`ALTER TABLE "post_tags_tag" DROP CONSTRAINT "FK_b651178cc41334544a7a9601c45"`);
await queryRunner.query(`ALTER TABLE "cv" DROP CONSTRAINT "FK_e4b7330e64fd0ecce86720e62f9"`);
await queryRunner.query(`ALTER TABLE "qualification" DROP CONSTRAINT "FK_6550f632c00a2739a1dad0381c1"`);
await queryRunner.query(`ALTER TABLE "experience" DROP CONSTRAINT "FK_cbfb1d1219454c9b45f1b3c4274"`);
await queryRunner.query(`ALTER TABLE "education_item" DROP CONSTRAINT "FK_1dd0c4d5f3e2500b08c3d137b29"`);
await queryRunner.query(`ALTER TABLE "user_follow" DROP CONSTRAINT "FK_4e493e20c9c272183e73da57ddf"`);
await queryRunner.query(`ALTER TABLE "user_follow" DROP CONSTRAINT "FK_90ab1c414dac6701f4c126fb736"`);
await queryRunner.query(`ALTER TABLE "page_follow" DROP CONSTRAINT "FK_b0cd715ee8609422e661e0c43fc"`);
await queryRunner.query(`ALTER TABLE "page_follow" DROP CONSTRAINT "FK_732300fd551f8b08e267018ed99"`);
await queryRunner.query(`ALTER TABLE "offer" DROP CONSTRAINT "FK_69bbcc303da6f7da97744be788a"`);
await queryRunner.query(`ALTER TABLE "offer" DROP CONSTRAINT "FK_f4621bc66549eda711a0b7c6558"`);
await queryRunner.query(`ALTER TABLE "offer" DROP CONSTRAINT "FK_cd84aed04fbd70e9ad48139c727"`);
await queryRunner.query(`ALTER TABLE "offer_application" DROP CONSTRAINT "FK_6cb6ed00606a2fb71c05fb3c871"`);
await queryRunner.query(`ALTER TABLE "offer_application" DROP CONSTRAINT "FK_dcfd5b26f80494128baf7bb7578"`);
await queryRunner.query(`ALTER TABLE "comment_vote" DROP CONSTRAINT "FK_5d77d92a6925ae3fc8da14e1257"`);
await queryRunner.query(`ALTER TABLE "comment_vote" DROP CONSTRAINT "FK_ade7498b89296b9fb63bcd8dbdd"`);
await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_94a85bb16d24033a2afdd5df060"`);
await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_37de0ca7e7c8dd9f6a3e1f2b6d3"`);
await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_b6bf60ecb9f6c398e349adff52f"`);
await queryRunner.query(`ALTER TABLE "space_subscription" DROP CONSTRAINT "FK_4c2cf829c698bede8b9c8608f10"`);
await queryRunner.query(`ALTER TABLE "space_subscription" DROP CONSTRAINT "FK_9f89ed79bb85a199306ce7e3ae1"`);
await queryRunner.query(`ALTER TABLE "vote" DROP CONSTRAINT "FK_43cc1af57676ac1b7ec774bd10f"`);
await queryRunner.query(`ALTER TABLE "vote" DROP CONSTRAINT "FK_f5de237a438d298031d11a57c3b"`);
await queryRunner.query(`ALTER TABLE "post" DROP CONSTRAINT "FK_899a1931431768b2800f695ac60"`);
await queryRunner.query(`ALTER TABLE "post" DROP CONSTRAINT "FK_605fe7290cfa226a4d444c05ca4"`);
await queryRunner.query(`ALTER TABLE "post" DROP CONSTRAINT "FK_9e91e6a24261b66f53971d3f96b"`);
await queryRunner.query(`DROP INDEX "public"."IDX_5bd7deaf0c8e15ea861d15efc3"`);
await queryRunner.query(`DROP INDEX "public"."IDX_1c5a8df532d6c129c4f1642b2f"`);
await queryRunner.query(`DROP TABLE "page_owners_user"`);
await queryRunner.query(`DROP INDEX "public"."IDX_2172d469a5e9c5e4397bcb94ff"`);
await queryRunner.query(`DROP INDEX "public"."IDX_6fac656f12453c24f08855fbef"`);
await queryRunner.query(`DROP TABLE "space_mods_user"`);
await queryRunner.query(`DROP INDEX "public"."IDX_41e7626b9cc03c5c65812ae55e"`);
await queryRunner.query(`DROP INDEX "public"."IDX_b651178cc41334544a7a9601c4"`);
await queryRunner.query(`DROP TABLE "post_tags_tag"`);
await queryRunner.query(`DROP TABLE "cv"`);
await queryRunner.query(`DROP TABLE "qualification"`);
await queryRunner.query(`DROP TABLE "experience"`);
await queryRunner.query(`DROP TABLE "education_item"`);
await queryRunner.query(`DROP TABLE "user_follow"`);
await queryRunner.query(`DROP TABLE "page_follow"`);
await queryRunner.query(`DROP TABLE "page"`);
await queryRunner.query(`DROP TABLE "conversation"`);
await queryRunner.query(`DROP TABLE "tag"`);
await queryRunner.query(`DROP TABLE "offer"`);
await queryRunner.query(`DROP TABLE "offer_application"`);
await queryRunner.query(`DROP TABLE "comment_vote"`);
await queryRunner.query(`DROP TABLE "comment"`);
await queryRunner.query(`DROP TABLE "space_subscription"`);
await queryRunner.query(`DROP TABLE "space"`);
await queryRunner.query(`DROP TABLE "vote"`);
await queryRunner.query(`DROP TABLE "post"`);
await queryRunner.query(`DROP TABLE "user"`);
}
}
+39
View File
@@ -0,0 +1,39 @@
import { __prod__ } from '@constants'
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddSpaces1660494342131 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
BEGIN;
INSERT INTO space ("spaceName") VALUES ('CS');
INSERT INTO space ("spaceName") VALUES ('economics');
INSERT INTO space ("spaceName") VALUES ('politics');
INSERT INTO space ("spaceName") VALUES ('arts');
INSERT INTO space ("spaceName") VALUES ('law');
INSERT INTO space ("spaceName") VALUES ('agriculture');
INSERT INTO space ("spaceName") VALUES ('tourism');
INSERT INTO space ("spaceName") VALUES ('engineering');
INSERT INTO space ("spaceName") VALUES ('science');
INSERT INTO space ("spaceName") VALUES ('history');
INSERT INTO space ("spaceName") VALUES ('philosophy');
INSERT INTO space ("spaceName") VALUES ('maths');
INSERT INTO space ("spaceName") VALUES ('physics');
INSERT INTO space ("spaceName") VALUES ('chemistry');
INSERT INTO space ("spaceName") VALUES ('biology');
INSERT INTO space ("spaceName") VALUES ('sports');
COMMIT;
`)
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
DELETE FROM space WHERE true;
`)
}
}
}
+135
View File
@@ -0,0 +1,135 @@
import { __prod__ } from '@constants'
import { faker } from '@faker-js/faker'
import _ from 'lodash'
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddUsers1660494342132 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`
INSERT INTO "user" ("username", "email", "password") VALUES ('admin', 'quyanh.nguyen@helsinki.fi', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nullett0', 'drubbert0@oracle.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-02-21 05:19:19', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('evigers1', 'eolyet1@a8.net', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-08-06 04:15:03', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('tpaoloni2', 'fgaenor2@unesco.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-02-23 16:59:03', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('cmacconneely3', 'gglanfield3@google.pl', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-09-30 18:06:03', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('bcraghead4', 'sickovitz4@sakura.ne.jp', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-08-25 23:12:55', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ieat5', 'hkopf5@yellowbook.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-06-14 10:33:57', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('harling6', 'hlortz6@wsj.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-02-18 15:17:28', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lbrandel7', 'kbuyers7@slate.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-02-21 06:04:22', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lkatte8', 'clanston8@i2i.jp', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-09-18 20:16:31', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('baronowitz9', 'spossek9@upenn.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-09-03 06:56:17', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mkalinovicha', 'mmcenerya@unicef.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-11-15 03:21:09', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dburkettb', 'cscuphamb@un.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-10-09 05:42:19', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('bmitchelhillc', 'ccoathc@unc.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-12-22 04:00:30', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('eadamczewskid', 'crozenzweigd@amazonaws.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-08-16 17:24:33', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('fbeszante', 'mfloate@arstechnica.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-04-18 11:23:07', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('prozsaf', 'eturonef@live.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-09-12 22:12:07', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mvittelg', 'nreadittg@icq.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-02-24 00:11:29', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('fcasoh', 'fhaversh@independent.co.uk', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-02-20 03:35:50', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('wmugfordi', 'jcuestai@cmu.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-11-17 03:05:33', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mbruckenthalj', 'skhrishtafovichj@mashable.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-05-19 17:03:47', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gmacgormank', 'khurrellk@tiny.cc', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-07-11 06:32:04', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('plesliel', 'nmclesel@oaic.gov.au', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-06-08 08:03:24', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('rlangmeadm', 'hlamymanm@foxnews.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-08-19 21:44:19', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gchapelhown', 'wcoupmann@posterous.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-06-04 17:34:58', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nbarwacko', 'mtethcoteo@deviantart.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-05-25 20:09:36', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lcardenasp', 'jbridep@wiley.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-03-16 03:10:29', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('fwhatsizeq', 'wadiscotq@berkeley.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-02-27 05:13:13', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lbeardowr', 'kdashkovichr@youtube.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-05-01 03:39:50', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('sdellos', 'wdartnells@smugmug.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-11-01 13:16:25', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('clindwassert', 'afustt@yahoo.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-05-26 16:18:36', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('sgoffu', 'krochesteru@narod.ru', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-01-28 03:50:40', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('cimlackev', 'bnorthamv@storify.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-09-17 13:07:23', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hnaerupw', 'ccunniffw@liveinternet.ru', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-07-24 15:57:24', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('brylandsx', 'sgutsellx@vkontakte.ru', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-07-13 16:24:12', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mbottomleyy', 'glamberthy@paginegialle.it', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-01-10 09:11:07', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gmacgaughiez', 'bbarrittz@youtube.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-09-03 12:45:37', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mdochon10', 'jpostians10@hao123.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-01-24 15:08:32', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ksommerville11', 'lslader11@histats.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-03-23 01:38:11', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('sclemitt12', 'wbickerdike12@gnu.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-03-09 07:20:39', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('rmertin13', 'mphilott13@netscape.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-07-17 11:42:00', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jcreane14', 'borrill14@simplemachines.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-07-15 16:42:57', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hbrood15', 'mgudgin15@go.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-09-14 00:34:51', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('shofler16', 'dchappel16@economist.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-12-03 17:18:12', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ndavydychev17', 'eeggleton17@goodreads.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-07-01 12:55:01', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('bcroutear18', 'droe18@samsung.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-09-01 23:30:53', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dsalvidge19', 'wmollitt19@chicagotribune.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-04-04 18:28:56', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('rthundercliffe1a', 'baveling1a@yale.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-06-23 11:25:30', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('chealeas1b', 'jbuckley1b@theatlantic.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-04-13 01:16:19', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dmayers1c', 'aphetteplace1c@a8.net', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-03-08 09:43:10', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('trope1d', 'brosone1d@free.fr', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-03-12 04:33:36', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('pizzard1e', 'dmahoney1e@slate.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-08-29 22:48:53', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hmcilwrick1f', 'fbullock1f@dedecms.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-04-13 12:16:56', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mkilfoyle1g', 'mduffell1g@drupal.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-04-06 00:06:11', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('aoutright1h', 'lshouler1h@tinyurl.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-03-10 01:46:56', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('vphethean1i', 'egrabban1i@cdbaby.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-08-03 22:31:30', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('thammand1j', 'memms1j@myspace.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-03-17 20:05:07', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('scabotto1k', 'lmorey1k@washington.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-08-02 14:07:16', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ahardwin1l', 'avaller1l@fema.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-08-02 04:00:56', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hbatha1m', 'omonini1m@sbwire.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-12-10 00:48:51', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gfrigot1n', 'fjoderli1n@linkedin.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-11-05 10:10:38', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gcorn1o', 'cpainten1o@usgs.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-11-25 18:56:43', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mbecken1p', 'bflippen1p@addtoany.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-03-25 20:36:23', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nvangeffen1q', 'wverbrugge1q@amazon.co.jp', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-08-05 04:05:19', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('shelversen1r', 'idemanche1r@chron.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2019-09-11 21:39:30', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('vepperson1s', 'pwalburn1s@example.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-03-14 03:48:40', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('cluckin1t', 'nmehaffey1t@webmd.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-07-24 13:26:57', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('edecort1u', 'jcanto1u@artisteer.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-08-09 17:01:53', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jell1v', 'dhissett1v@angelfire.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-11-14 15:37:08', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('kseamarke1w', 'fdeniskevich1w@pcworld.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-02-23 14:51:15', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jteal1x', 'rmustard1x@clickbank.net', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-08-11 03:57:23', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('colenane1y', 'jisakov1y@intel.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-08-21 15:36:04', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('junwins1z', 'acuredell1z@hexun.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-06-26 13:46:42', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jpetyt20', 'ahuff20@examiner.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-01-29 17:28:07', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mgarrelts21', 'rcopeman21@vistaprint.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-09-16 22:44:37', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('aplacidi22', 'mfeldharker22@usda.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-07-20 20:47:24', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('tloft23', 'mdur23@mlb.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-05-02 09:20:22', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nbladon24', 'mplose24@gizmodo.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-07-21 04:32:58', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('gmccaughan25', 'glindl25@yandex.ru', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-12-20 15:15:27', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ocasini26', 'chasloch26@360.cn', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2016-07-06 09:25:32', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hwoolmore27', 'ltriggel27@taobao.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-04-10 10:13:08', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mfaccini28', 'klasselle28@epa.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-11-12 04:27:10', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('nnoddings29', 'nsiddons29@studiopress.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2012-04-26 00:16:37', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dscarlin2a', 'jkarys2a@marriott.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2020-01-24 08:07:07', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('jfields2b', 'lrooze2b@wordpress.org', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-02-07 03:05:29', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('fedgington2c', 'gsurmeir2c@tmall.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2011-01-27 07:05:49', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('kmeekings2d', 'ayeabsley2d@surveymonkey.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-01-11 22:02:29', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('tkirkebye2e', 'mgipps2e@state.tx.us', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-02-06 16:28:54', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('tosmar2f', 'kguntrip2f@shinystat.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-06-02 01:18:00', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mdubber2g', 'cheadey2g@usgs.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-07-03 06:12:22', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('hfeldklein2h', 'peronie2h@cyberchimps.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2017-10-21 13:13:28', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ccaneo2i', 'iadaway2i@aboutads.info', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-07-14 16:22:46', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('lyansons2j', 'tauckland2j@home.pl', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-04-02 17:08:09', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('savann2k', 'hsnelman2k@aboutads.info', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2015-10-09 15:14:30', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('palyokhin2l', 'piacovolo2l@t.co', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2010-01-03 09:12:00', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('dmacallen2m', 'kgorgler2m@instagram.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2018-01-03 11:50:11', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('rpollendine2n', 'emaccahey2n@usda.gov', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-12-06 15:51:04', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('ndurie2o', 'wgrigolashvill2o@ask.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-11-05 17:40:10', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('mohdirscoll2p', 'gbrombell2p@illinois.edu', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2021-05-28 17:29:47', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('abetke2q', 'lcurrie2q@paginegialle.it', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2014-06-29 00:06:53', '${faker.address.latitude()}', '${faker.address.longitude()}');
INSERT INTO "user" ("username", "email", "password", "createdAt", "mostRecentLatitude", "mostRecentLongitude") VALUES ('pberrow2r', 'echettoe2r@techcrunch.com', '$argon2id$v=19$m=4096,t=3,p=1$8AOn1wH68/N07ssOrcsA2Q$ckLNkSP5nTGVoNmi5/w2O5VUVvvVCvHHNIaBPmcqK5k', '2013-09-05 01:58:34', '${faker.address.latitude()}', '${faker.address.longitude()}');
`)
for (let i = 1; i <= 101; i++) { // for each user to be followed
let queryString = ''
const numberOfFollowers = i == 1 ? 100 : _.random(1, 100)
const arr = _.sampleSize(_.range(2, 102), numberOfFollowers).filter(n => n != i)
for (let y = 0; y < arr.length; y++) {
queryString += `\nINSERT INTO user_follow ("followedUserId", "followingUserId") VALUES (${i}, ${arr[y]});`
}
await queryRunner.query(queryString)
}
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
DELETE FROM "user" WHERE true;
DELETE FROM "user_follow" WHERE true;
`)
}
}
}
@@ -0,0 +1,34 @@
import { __prod__ } from '@constants'
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddAdminAsModToSpaces1660494342133 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
CREATE OR REPLACE PROCEDURE main()
AS $$
DECLARE
s integer;
BEGIN
FOR s IN
SELECT id FROM space
LOOP
INSERT INTO space_mods_user ("spaceId", "userId") VALUES (s, (SELECT id FROM "user" WHERE username = 'admin'));
END LOOP;
END;
$$ LANGUAGE plpgsql;
CALL main();
`)
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
DELETE FROM space_mods_user WHERE "userId" = (SELECT id FROM "user" WHERE username = 'admin');
`)
}
}
}
+337
View File
@@ -0,0 +1,337 @@
import { __prod__ } from '@constants'
import _ from 'lodash'
import { MigrationInterface, QueryRunner } from "typeorm"
export class AddPosts1660494342134 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
INSERT INTO page ("pageName", "fullPageName") VALUES ('osiggin0', 'Turcotte-Cronin');
INSERT INTO page ("pageName", "fullPageName") VALUES ('tbasley1', 'Wolf-Price');
INSERT INTO page ("pageName", "fullPageName") VALUES ('mbarthel2', 'Hagenes, Russel and Block');
INSERT INTO page ("pageName", "fullPageName") VALUES ('kduckinfield3', 'Koelpin, Rowe and O''Kon');
INSERT INTO page ("pageName", "fullPageName") VALUES ('mmougeot4', 'Stroman-Lind');
INSERT INTO page ("pageName", "fullPageName") VALUES ('medden5', 'Wintheiser LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('astoney6', 'McGlynn, Hoppe and Little');
INSERT INTO page ("pageName", "fullPageName") VALUES ('cventam7', 'Corwin-Gerhold');
INSERT INTO page ("pageName", "fullPageName") VALUES ('cpragnall8', 'Fahey, Hayes and Predovic');
INSERT INTO page ("pageName", "fullPageName") VALUES ('gkissick9', 'Abbott Group');
INSERT INTO page ("pageName", "fullPageName") VALUES ('dkedwella', 'Bode LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('osprostonb', 'Hartmann, Dibbert and Lang');
INSERT INTO page ("pageName", "fullPageName") VALUES ('snorkettc', 'Reinger LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('tjurczikd', 'Hauck-Jones');
INSERT INTO page ("pageName", "fullPageName") VALUES ('fhoofee', 'Kiehn-Spinka');
INSERT INTO page ("pageName", "fullPageName") VALUES ('hgasperif', 'Reilly and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('lbennitg', 'Ziemann LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('tclaireh', 'Hegmann-Wiza');
INSERT INTO page ("pageName", "fullPageName") VALUES ('vbutteli', 'Stracke LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('kmarchantj', 'Hermann and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('cdonoghuek', 'Runolfsdottir, Ziemann and Grady');
INSERT INTO page ("pageName", "fullPageName") VALUES ('ojelksl', 'Schroeder, Smith and Wunsch');
INSERT INTO page ("pageName", "fullPageName") VALUES ('jmallenderm', 'Macejkovic, Labadie and O''Kon');
INSERT INTO page ("pageName", "fullPageName") VALUES ('tsiemonsn', 'Goldner LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('amethingamo', 'Champlin, Ruecker and Collier');
INSERT INTO page ("pageName", "fullPageName") VALUES ('dleevesp', 'Feil LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('jdoumerq', 'Larkin and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('mkestenr', 'Donnelly and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('lpostins', 'Wisoky Group');
INSERT INTO page ("pageName", "fullPageName") VALUES ('avolkt', 'Thiel, Jacobson and Hermiston');
INSERT INTO page ("pageName", "fullPageName") VALUES ('tbabinskiu', 'Johnston and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('csavilev', 'Rosenbaum LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('vmayesw', 'Jenkins Group');
INSERT INTO page ("pageName", "fullPageName") VALUES ('ywillcockx', 'Satterfield and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('dbulfieldy', 'Steuber-Kreiger');
INSERT INTO page ("pageName", "fullPageName") VALUES ('tcottrillz', 'Fahey, Rowe and Champlin');
INSERT INTO page ("pageName", "fullPageName") VALUES ('mlangthorne10', 'Rogahn and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('zparratt11', 'Schaden and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('edoucette12', 'Gulgowski, Paucek and Herman');
INSERT INTO page ("pageName", "fullPageName") VALUES ('gwillavize13', 'Crona, Effertz and King');
INSERT INTO page ("pageName", "fullPageName") VALUES ('rziemke14', 'Larkin-Walter');
INSERT INTO page ("pageName", "fullPageName") VALUES ('tpickover15', 'Roob and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('awaghorn16', 'Cormier, Graham and Metz');
INSERT INTO page ("pageName", "fullPageName") VALUES ('lspruce17', 'Ebert-Bosco');
INSERT INTO page ("pageName", "fullPageName") VALUES ('sarmistead18', 'Cremin Group');
INSERT INTO page ("pageName", "fullPageName") VALUES ('odanick19', 'Harber Group');
INSERT INTO page ("pageName", "fullPageName") VALUES ('dflipsen1a', 'Kirlin and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('pbygrave1b', 'Kessler, Kutch and Streich');
INSERT INTO page ("pageName", "fullPageName") VALUES ('dbattabee1c', 'O''Conner, Stehr and Becker');
INSERT INTO page ("pageName", "fullPageName") VALUES ('abalfre1d', 'Beatty Group');
INSERT INTO page ("pageName", "fullPageName") VALUES ('elewer1e', 'Bogan, O''Connell and Klocko');
INSERT INTO page ("pageName", "fullPageName") VALUES ('hmclaine1f', 'Zemlak-Stiedemann');
INSERT INTO page ("pageName", "fullPageName") VALUES ('mbevan1g', 'Borer, McKenzie and Mueller');
INSERT INTO page ("pageName", "fullPageName") VALUES ('leldon1h', 'Heller, Johnson and Kuvalis');
INSERT INTO page ("pageName", "fullPageName") VALUES ('gthunnercliff1i', 'Adams Inc');
INSERT INTO page ("pageName", "fullPageName") VALUES ('scrowhurst1j', 'Boyle, Gaylord and Flatley');
INSERT INTO page ("pageName", "fullPageName") VALUES ('mgilloran1k', 'Mosciski and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('cbertholin1l', 'Hodkiewicz-Wolf');
INSERT INTO page ("pageName", "fullPageName") VALUES ('ecastiglione1m', 'Kshlerin, Hills and Jacobs');
INSERT INTO page ("pageName", "fullPageName") VALUES ('sgearty1n', 'Mertz Group');
INSERT INTO page ("pageName", "fullPageName") VALUES ('bpatesel1o', 'Yundt and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('vhoudmont1p', 'Stanton-Jacobs');
INSERT INTO page ("pageName", "fullPageName") VALUES ('cpikhno1q', 'Shanahan, Lueilwitz and Wolf');
INSERT INTO page ("pageName", "fullPageName") VALUES ('lpaute1r', 'Kiehn, Emard and Lemke');
INSERT INTO page ("pageName", "fullPageName") VALUES ('rabramchik1s', 'Smith LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('csommerville1t', 'Crooks-Monahan');
INSERT INTO page ("pageName", "fullPageName") VALUES ('jjaume1u', 'Leuschke LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('alethbury1v', 'Howe, Hand and Jerde');
INSERT INTO page ("pageName", "fullPageName") VALUES ('gdoogood1w', 'Howe-Streich');
INSERT INTO page ("pageName", "fullPageName") VALUES ('ldrabble1x', 'Balistreri-Greenfelder');
INSERT INTO page ("pageName", "fullPageName") VALUES ('pudie1y', 'Wisozk Inc');
INSERT INTO page ("pageName", "fullPageName") VALUES ('econiam1z', 'Legros, Weimann and Stiedemann');
INSERT INTO page ("pageName", "fullPageName") VALUES ('pkipping20', 'Braun, Hessel and Blick');
INSERT INTO page ("pageName", "fullPageName") VALUES ('mcaccavella21', 'Skiles-Douglas');
INSERT INTO page ("pageName", "fullPageName") VALUES ('jbelhomme22', 'Schoen, Rice and Cassin');
INSERT INTO page ("pageName", "fullPageName") VALUES ('dsikora23', 'Swift, Glover and Jerde');
INSERT INTO page ("pageName", "fullPageName") VALUES ('gnorewood24', 'O''Kon and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('jscowcraft25', 'Cronin, Leffler and Spencer');
INSERT INTO page ("pageName", "fullPageName") VALUES ('wjeffreys26', 'Ritchie Group');
INSERT INTO page ("pageName", "fullPageName") VALUES ('amaccoughen27', 'Hickle, Price and Bergstrom');
INSERT INTO page ("pageName", "fullPageName") VALUES ('oseywood28', 'Bailey Group');
INSERT INTO page ("pageName", "fullPageName") VALUES ('kdennison29', 'Hettinger LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('etzuker2a', 'McKenzie Inc');
INSERT INTO page ("pageName", "fullPageName") VALUES ('mguidoni2b', 'Bailey-Hartmann');
INSERT INTO page ("pageName", "fullPageName") VALUES ('etaaffe2c', 'Halvorson, Pagac and Jones');
INSERT INTO page ("pageName", "fullPageName") VALUES ('bglynne2d', 'Kessler Inc');
INSERT INTO page ("pageName", "fullPageName") VALUES ('jmatejovsky2e', 'Blick, Greenholt and Nader');
INSERT INTO page ("pageName", "fullPageName") VALUES ('ftilio2f', 'Nitzsche, Auer and Kemmer');
INSERT INTO page ("pageName", "fullPageName") VALUES ('skhadir2g', 'Beier-Osinski');
INSERT INTO page ("pageName", "fullPageName") VALUES ('blanktree2h', 'Luettgen LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('htofts2i', 'Pagac, Kuhlman and Ernser');
INSERT INTO page ("pageName", "fullPageName") VALUES ('ccrisford2j', 'Pollich LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('vwinterflood2k', 'Reichel LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('mdominey2l', 'Hahn-Green');
INSERT INTO page ("pageName", "fullPageName") VALUES ('ngrave2m', 'Farrell LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('ttoomey2n', 'Yost-Reichel');
INSERT INTO page ("pageName", "fullPageName") VALUES ('tdog2o', 'Johnston LLC');
INSERT INTO page ("pageName", "fullPageName") VALUES ('aseabon2p', 'Goyette and Sons');
INSERT INTO page ("pageName", "fullPageName") VALUES ('tlutsch2q', 'Cassin-Gottlieb');
INSERT INTO page ("pageName", "fullPageName") VALUES ('lwickardt2r', 'Marvin, Hand and McCullough');
INSERT INTO page_owners_user ("pageId", "userId") VALUES (1, 61);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (2, 84);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (3, 56);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (4, 73);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (5, 17);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (6, 43);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (7, 56);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (8, 81);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (9, 23);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (10, 96);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (11, 71);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (12, 16);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (13, 86);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (14, 74);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (15, 44);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (16, 56);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (17, 49);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (18, 71);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (19, 29);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (20, 48);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (21, 53);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (22, 11);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (23, 60);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (24, 22);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (25, 80);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (26, 54);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (27, 95);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (28, 41);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (29, 19);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (30, 31);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (31, 39);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (32, 14);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (33, 40);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (34, 8);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (35, 36);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (36, 21);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (37, 46);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (38, 29);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (39, 79);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (40, 91);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (41, 41);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (42, 86);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (43, 46);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (44, 94);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (45, 5);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (46, 51);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (47, 63);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (48, 26);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (49, 24);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (50, 6);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (51, 27);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (52, 32);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (53, 94);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (54, 4);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (55, 3);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (56, 77);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (57, 62);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (58, 95);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (59, 49);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (60, 65);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (61, 14);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (62, 70);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (63, 73);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (64, 23);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (65, 26);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (66, 8);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (67, 35);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (68, 30);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (69, 41);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (70, 20);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (71, 44);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (72, 76);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (73, 43);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (74, 90);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (75, 35);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (76, 77);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (77, 52);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (78, 84);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (79, 7);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (80, 28);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (81, 22);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (82, 90);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (83, 92);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (84, 58);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (85, 18);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (86, 6);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (87, 36);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (88, 19);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (89, 62);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (90, 3);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (91, 19);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (92, 16);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (93, 95);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (94, 73);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (95, 34);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (96, 39);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (97, 27);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (98, 93);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (99, 49);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (100, 88);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (1, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (2, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (3, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (4, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (5, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (6, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (7, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (8, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (9, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (10, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (11, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (12, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (13, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (14, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (15, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (16, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (17, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (18, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (19, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (20, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (21, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (22, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (23, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (24, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (25, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (26, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (27, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (28, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (29, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (30, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (31, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (32, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (33, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (34, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (35, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (36, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (37, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (38, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (39, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (40, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (41, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (42, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (43, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (44, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (45, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (46, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (47, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (48, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (49, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (50, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (51, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (52, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (53, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (54, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (55, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (56, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (57, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (58, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (59, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (60, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (61, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (62, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (63, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (64, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (65, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (66, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (67, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (68, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (69, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (70, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (71, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (72, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (73, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (74, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (75, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (76, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (77, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (78, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (79, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (80, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (81, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (82, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (83, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (84, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (85, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (86, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (87, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (88, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (89, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (90, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (91, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (92, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (93, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (94, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (95, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (96, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (97, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (98, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (99, 1);
INSERT INTO page_owners_user ("pageId", "userId") VALUES (100, 1);
`)
for (let i = 1; i <= 100; i++) { // for each page
let queryString = ''
const numberOfFollowers = _.random(1, 100)
// Generate an array of unique random integers (min 2, max 101) with length equal to numberOfFollowers
const arr = _.sampleSize(_.range(2, 102), numberOfFollowers)
for (let y = 0; y < arr.length; y++) { // a page can have a maximum of 100 followers
queryString += `\nINSERT INTO page_follow ("pageId", "userId") VALUES (${i}, ${arr[y]});`
}
await queryRunner.query(queryString)
}
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
DELETE FROM page WHERE true;
DELETE FROM page_owners_user WHERE true;
DELETE FROM page_follow WHERE true;
`)
}
}
}
@@ -0,0 +1,29 @@
import { __prod__ } from '@constants'
import _ from 'lodash'
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddSpaceSubscribers1660494342135 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
for (let i = 1; i <= 16; i++) { // for each space
let queryString = ''
const numberOfSubscribers = _.random(1, 100)
const arr = _.sampleSize(_.range(2, 102), numberOfSubscribers)
for (let y = 0; y < arr.length; y++) {
queryString += `\nINSERT INTO space_subscription ("spaceId", "userId") VALUES (${i}, ${arr[y]});`
}
await queryRunner.query(queryString)
}
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
DELETE FROM space_subscription WHERE true;
`)
}
}
}
+33
View File
@@ -0,0 +1,33 @@
import { __prod__ } from '@constants'
import { MigrationInterface, QueryRunner } from "typeorm"
export class AddTags1660494342136 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`--sql
BEGIN;
INSERT INTO tag (name) VALUES ('Question');
INSERT INTO tag (name) VALUES ('Help');
INSERT INTO tag (name) VALUES ('Fundraising');
INSERT INTO tag (name) VALUES ('Introduction');
INSERT INTO tag (name) VALUES ('Self');
INSERT INTO tag (name) VALUES ('AMA');
INSERT INTO tag (name) VALUES ('Resources');
INSERT INTO tag (name) VALUES ('Educational');
INSERT INTO tag (name) VALUES ('Poll');
INSERT INTO tag (name) VALUES ('Recruitment');
INSERT INTO tag (name) VALUES ('Contract');
INSERT INTO tag (name) VALUES ('Networking');
INSERT INTO tag (name) VALUES ('Opinion');
INSERT INTO tag (name) VALUES ('Other');
COMMIT;
`)
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`--sql
DELETE FROM tag WHERE true;
`)
}
}
+43
View File
@@ -0,0 +1,43 @@
import { __prod__ } from '@constants'
import { faker } from '@faker-js/faker'
import _ from 'lodash'
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddPosts1660494342137 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
let queryString = ''
for (let i = 0; i < 1000; i++) {
const title = faker.company.catchPhrase()
const text = faker.lorem.paragraph()
const spaceId = _.random(1, 16)
const creatorId = faker.datatype.boolean() ? _.random(2, 101) : null
const pageCreatorId = creatorId ? null : _.random(1, 100)
const creatorType = creatorId ? 'user' : 'page'
const createdAt = faker.date.between('2020-01-01T00:00:00.000Z', '2021-01-01T00:00:00.000Z').toISOString()
queryString += `\nINSERT INTO post ("title", "text", "spaceId", "creatorId", "pageCreatorId", "createdAt", "creatorType") VALUES ('${title}', '${text}', ${spaceId}, ${creatorId}, ${pageCreatorId}, '${createdAt}', '${creatorType}');`
}
await queryRunner.query(queryString)
queryString = ''
for (let i = 1; i < 1001; i++) {
const numberOfTags = _.random(1, 4)
const arr = _.sampleSize(_.range(1, 15), numberOfTags)
for (let y = 0; y < numberOfTags; y++) {
queryString += `\nINSERT INTO post_tags_tag ("postId", "tagId") VALUES (${i}, ${arr[y]});`
}
}
await queryRunner.query(queryString)
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
DELETE FROM post WHERE true;
DELETE FROM post_tags_tag WHERE true;
`)
}
}
}
+67
View File
@@ -0,0 +1,67 @@
import { __prod__ } from '@constants'
import { faker } from '@faker-js/faker'
import _ from 'lodash'
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AddOffers1660494342138 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
let queryString = ''
for (let i = 0; i < 1000; i++) {
const title = faker.name.jobTitle().replaceAll('\'', '')
const workplace = faker.company.name().replaceAll('\'', '')
const address = faker.address.streetAddress(true).replaceAll('\'', '')
const recruiting = faker.datatype.boolean()
const employmentType = _.sample([
'Full-time',
'Part-time',
'Freelance',
'Online',
'Internship',
'Temporary',
'Contract',
'Volunteer',
'Other'
])
const currency = faker.finance.currencySymbol()
const salaryRange = `${currency}${_.random(0, 10000)} - ${currency}${_.random(10000, 999999)}`
const department = faker.name.jobArea()
const requirements = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`
const benefits = `Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?`
const description = `At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.`
const spaceId = _.random(1, 16)
const creatorId = faker.datatype.boolean() ? _.random(2, 101) : null
const pageCreatorId = creatorId ? null : _.random(1, 100)
const creatorType = creatorId ? 'user' : 'page'
const createdAt = faker.date.between('2020-01-01T00:00:00.000Z', '2021-01-01T00:00:00.000Z').toISOString()
queryString +=
`\nINSERT INTO offer ("title", "workplace", "address", "recruiting", "employmentType", "salaryRange", "department", "requirements", "benefits", "description", "spaceId", "creatorId", "pageCreatorId", "creatorType", "createdAt") VALUES ('${title}', '${workplace}', '${address}', ${recruiting}, '${employmentType}', '${salaryRange}', '${department}', '${requirements}', '${benefits}', '${description}', ${spaceId}, ${creatorId}, ${pageCreatorId}, '${creatorType}', '${createdAt}') ;`
}
await queryRunner.query(queryString)
queryString = ''
for (let i = 1; i < 1001; i++) {
const offerId = i
const numberOfApplications = _.random(0, 3)
const arr = _.sampleSize(_.range(2, 102), numberOfApplications)
for (let y = 0; y < numberOfApplications; y++) {
const status = _.sample(['applied', 'accepted', 'rejected'])
queryString +=
`\nINSERT INTO offer_application ("offerId", "userId", "status") VALUES (${offerId}, ${arr[y]}, '${status}') ;`
}
}
await queryRunner.query(queryString)
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
if (!__prod__) {
await queryRunner.query(`--sql
DELETE FROM offer WHERE true;
DELETE FROM offer_application WHERE true;
`)
}
}
}
+202
View File
@@ -0,0 +1,202 @@
import { Comment, CommentVote, Page, Post, User } from '@entities'
import { PaginatedComments } from '@graphql-types'
import { isAuth } from '@middlewares'
import { Context } from '@types'
import { toPostgresTime } from '@utils'
import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql'
@Resolver(Comment)
export class CommentResolver {
@FieldResolver(() => User)
creator(
@Root() root: Comment,
@Ctx() { userLoader }: Context
) {
return root.creatorId ? userLoader.load(root.creatorId) : null
}
@FieldResolver(() => Page)
pageCreator(
@Root() root: Comment,
@Ctx() { pageLoader }: Context
) {
return root.pageCreatorId ? pageLoader.load(root.pageCreatorId) : null
}
@Query(() => PaginatedComments)
async comments(
@Ctx() { req, orm }: Context,
@Arg('postId', () => Int) postId: number,
@Arg('limit', () => Int) limit: number,
@Arg('cursor', () => String, { nullable: true }) cursor: string | null,
): Promise<PaginatedComments | null> {
const { userId } = req.session
const realLimit = Math.min(limit, 5)
const realLimitPlusOne = realLimit + 1
const comments = await orm.query(`
SELECT *
FROM comment c
WHERE c."postId" = ${postId}
${cursor ? `AND c."createdAt" < '${toPostgresTime(cursor)}'` : ''}
ORDER BY
${userId ? `c."creatorId" = ${userId} DESC,` : ''}
c.points DESC,
c."createdAt" DESC
LIMIT ${realLimitPlusOne}
`)
return {
comments: comments.slice(0, realLimit),
hasMore: comments.length - 1 == realLimit
}
}
@FieldResolver(() => Int, { nullable: true })
async voteStatus(
@Root() root: Comment,
@Ctx() { req, commentVoteLoader }: Context
) {
return req.session?.userId ?
(await commentVoteLoader.load({ commentId: root.id, userId: req.session.userId }))?.value
:
null
}
@UseMiddleware(isAuth)
@Mutation(() => Comment)
async createComment(
@Arg('postId', () => Int) postId: number,
@Arg('text', () => String) text: string,
@Arg('pageId', () => Int, { nullable: true }) pageId: number,
@Ctx() { req, orm }: Context
): Promise<Comment | null> {
const { userId } = req.session
const post = await Post.findOneBy({ id: postId })
if (!post) {
return null
}
if (pageId) {
const page = await Page.findOne({ where: { id: pageId } })
if (!page) {
throw new Error('Page does not exist!')
}
const ownerProof: { userId: number, pageId: number }[] = await orm.query(`
SELECT * FROM page_owners_user WHERE "userId" = ${userId} AND "pageId" = ${pageId};
`)
if (ownerProof.length == 0) {
throw new Error('You are not an owner of this page.')
}
}
return await Comment.create({
text,
postId,
creatorType: pageId ? 'page' : 'user',
creatorId: pageId ? undefined : userId,
pageCreatorId: pageId,
}).save()
}
@Mutation(() => Comment, { nullable: true })
@UseMiddleware(isAuth)
async updateComment(
@Arg('id', () => Int) id: number,
@Arg('text', () => String) text: string,
@Ctx() { orm, req }: Context
): Promise<Comment | null> {
const comment = await Comment.findOne({ where: { id } })
if (comment) {
if (comment.creatorType == 'user') {
if (comment.creatorId != req.session.userId) {
throw new Error('Not authorised!')
}
}
else {
const ownerProof: { userId: number, pageId: number }[] = await orm.query(`
SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${comment.pageCreatorId};
`)
if (ownerProof.length == 0) {
throw new Error('Not authorised!')
}
}
comment.text = text
return comment.save()
}
else {
throw new Error('Post not found.')
}
}
@UseMiddleware(isAuth)
@Mutation(() => Boolean)
async deleteComment(
@Arg('id', () => Int) id: number,
@Ctx() { orm, req }: Context
): Promise<boolean> {
const comment = await Comment.findOneBy({ id })
if (!comment) {
throw new Error('Comment not found!')
}
if (comment.creatorType == 'user') {
if (comment.creatorId != req.session.userId) {
throw new Error('Not authorised!')
}
}
else {
const ownerProof: { userId: number, pageId: number }[] = await orm.query(`
SELECT * FROM page_owners_user WHERE "userId" = ${req.session.userId} AND "pageId" = ${comment.pageCreatorId};
`)
if (ownerProof.length == 0) {
throw new Error('Not authorised!')
}
}
await comment.remove()
return true
}
@UseMiddleware(isAuth)
@Mutation(() => Boolean)
async voteComment(
@Arg('commentId', () => Int) commentId: number,
@Arg('value', () => Int) value: number,
@Ctx() { req, orm }: Context
) {
const { userId } = req.session
if (value == 0) return false
const realValue = value > 0 ? 1 : value < 0 ? -1 : 0
const commentVote = await CommentVote.findOne({ where: { commentId, userId } })
if (commentVote) {
if (commentVote.value == realValue) {
await commentVote.remove()
const comment = await Comment.findOne({ where: { id: commentId } })
if (comment) {
comment.points = comment.points + (realValue == 1 ? -1 : 1)
await comment.save()
}
}
else {
await orm.transaction(async transactionalEntityManager => {
commentVote.value = realValue
await transactionalEntityManager.save(commentVote)
await transactionalEntityManager.increment(Comment, { id: commentId }, 'points', realValue == -1 ? -2 : 2)
})
}
}
else {
await orm.transaction(async transactionalEntityManager => {
const newCommentVote = new CommentVote()
newCommentVote.userId = userId!
newCommentVote.commentId = commentId
newCommentVote.value = realValue
await transactionalEntityManager.save(newCommentVote)
await transactionalEntityManager.increment(Comment, { id: commentId }, 'points', realValue)
})
}
return true
}
}
+215
View File
@@ -0,0 +1,215 @@
import { ConversationCol, ConversationInFirestore, Inbox, InboxCol, Message } from '@collections'
import { s3 } from '@configs'
import { Conversation as ConversationEnt, User } from '@entities'
import { CreateConversationResponse, InboxResponse, MessageInput } from '@graphql-types'
import { isAuth } from '@middlewares'
import { Context } from '@types'
import { getExtensionFromFilename } from '@utils'
import { FileUpload } from 'graphql-upload'
import { Arg, Ctx, Int, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'
import { ArrayContains, In } from 'typeorm'
import { v4 } from 'uuid'
@Resolver(ConversationInFirestore)
export class ConversationResolver {
@UseMiddleware(isAuth)
@Query(() => [ConversationEnt])
async conversations(
@Ctx() { req }: Context
): Promise<ConversationEnt[]> {
const { userId } = req.session
return await ConversationEnt.find({
where: {
participants: ArrayContains([userId])
}
})
}
@UseMiddleware(isAuth)
@Query(() => [User])
async participants(
@Arg('firestoreCollectionId', () => String) firestoreCollectionId: string,
@Ctx() { req }: Context
): Promise<User[]> {
const { userId } = req.session
const convo = await ConversationEnt.findOneBy({ firestoreCollectionId })
if (!convo) {
return []
}
if (!convo.participants.includes(userId!)) {
throw new Error('Not authorised!')
}
return User.findBy({ id: In(convo.participants) })
}
@UseMiddleware(isAuth)
@Query(() => [InboxResponse])
async inboxes(
@Arg('firestoreCollectionIds', () => [String]) firestoreCollectionIds: string[],
@Ctx() { req }: Context
): Promise<InboxResponse[]> {
const { userId } = req.session
const convos = await ConversationEnt.findBy({ firestoreCollectionId: In(firestoreCollectionIds) })
return Promise.all(firestoreCollectionIds.map(async fci => {
const convo = convos.find(c => c.firestoreCollectionId == fci)!
const partnerId = convo.participants.find(id => id != userId)
const partner = (await User.findOne({ where: { id: partnerId } }))!
return {
partner,
firestoreCollectionId: fci
}
}))
}
@UseMiddleware(isAuth)
@Mutation(() => CreateConversationResponse)
async createConversation(
@Arg('partnerUsername', () => String) partnerUsername: string,
@Ctx() { req }: Context
): Promise<CreateConversationResponse> {
const { userId } = req.session
const partner = await User.findOne({ where: { username: partnerUsername } })
if (!partner) {
return {
errors: [{
field: 'partnerUsername',
message: 'Invalid username.'
}]
}
}
const partnerId = partner.id
const existingConvo = await ConversationEnt.findOne({
where: {
participants: ArrayContains([userId, partnerId])
}
})
if (existingConvo) {
return {
errors: [{
field: 'partnerUsername',
message: 'Conversation already exists.'
}]
}
}
const convo = new ConversationInFirestore()
const convoItem = await ConversationCol.create(convo)
let inbox = await InboxCol.whereEqualTo('userId', userId!).findOne()
let partnerInbox = await InboxCol.whereEqualTo('userId', partnerId!).findOne()
if (!inbox) {
inbox = new Inbox()
inbox.userId = userId!
inbox.conversationIds = []
await InboxCol.create(inbox)
}
if (!partnerInbox) {
partnerInbox = new Inbox()
partnerInbox.userId = partnerId!
partnerInbox.conversationIds = []
await InboxCol.create(partnerInbox)
}
inbox.conversationIds.push(convoItem.id)
partnerInbox.conversationIds.push(convoItem.id)
await InboxCol.update(inbox)
await InboxCol.update(partnerInbox)
const convoEnt = new ConversationEnt()
convoEnt.participants = [userId!, partnerId]
convoEnt.firestoreCollectionId = convoItem.id
await convoEnt.save()
return { conversation: convoItem }
}
@UseMiddleware(isAuth)
@Mutation(() => Boolean)
async deleteConversation(
@Arg('firestoreCollectionId', () => String) firestoreCollectionId: string,
@Ctx() { req }: Context
): Promise<boolean> {
const { userId } = req.session
const convo = await ConversationEnt.findOneBy({ firestoreCollectionId })
if (!convo) {
throw new Error('Conversation not found.')
}
if (!convo.participants.includes(userId!)) {
throw new Error('Not authorised!')
}
const partnerId = convo.participants.find(id => id != userId)
const inbox = await InboxCol.whereEqualTo('userId', userId!).findOne()
const partnerInbox = await InboxCol.whereEqualTo('userId', partnerId!).findOne()
const firestoreConvo = await ConversationCol.findById(firestoreCollectionId)
await ConversationCol.delete(firestoreConvo.id)
inbox!.conversationIds = inbox!.conversationIds.filter(id => id != firestoreConvo.id)
partnerInbox!.conversationIds = partnerInbox!.conversationIds.filter(id => id != firestoreConvo.id)
await InboxCol.update(inbox!)
await InboxCol.update(partnerInbox!)
await convo.remove()
return true
}
@UseMiddleware(isAuth)
@Mutation(() => Message)
async createMessage(
@Arg('firestoreCollectionId', () => String) firestoreCollectionId: string,
@Arg('input', () => MessageInput) input: MessageInput,
@Ctx() { req }: Context
): Promise<Message> {
const { userId } = req.session
const { type, upload, text } = input
const convoEnt = await ConversationEnt.findOne({
where: {
firestoreCollectionId,
participants: ArrayContains([userId])
}
})
if (!convoEnt) {
throw new Error('Not authorised!')
}
const message = new Message()
message.senderId = userId!
message.type = type
if (type == 'text') {
message.content = text!
}
else {
const { createReadStream, filename } = await upload as FileUpload
const stream = createReadStream()
const ext = getExtensionFromFilename(filename)
const uploaded = await s3.upload({
Bucket: process.env.S3_BUCKET,
Body: stream,
Key: `conversations/${convoEnt.id}/media/${type}s/${v4()}.${ext}`,
}).promise()
message.content = uploaded.Key
}
const convo = await ConversationCol.findById(firestoreCollectionId)
message.createdAt = new Date()
message.updatedAt = new Date()
return convo.messages.create(message)
}
@UseMiddleware(isAuth)
@Mutation(() => Boolean)
async deleteMessage(
@Arg('firestoreCollectionId', () => String) firestoreCollectionId: string,
@Arg('messageDocumentId', () => String) messageDocumentId: string,
@Ctx() { req }: Context
): Promise<boolean> {
const { userId } = req.session
const convo = await ConversationCol.findById(firestoreCollectionId)
if (!convo) {
throw new Error('Conversation not found.')
}
const message = await convo.messages.findById(messageDocumentId)
if (!message) {
throw new Error('Message not found.')
}
if (message.senderId != userId) {
throw new Error('Not authorised!')
}
await convo.messages.delete(messageDocumentId)
return true
}
}
+67
View File
@@ -0,0 +1,67 @@
import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql'
import { CV } from '@entities'
import { UploadedFileResponse } from '@graphql-types'
import { isAuth } from '@middlewares'
import { getExtensionFromFilename } from '@utils'
import { GraphQLUpload, FileUpload } from 'graphql-upload'
import { v4 } from 'uuid'
import { Context } from '@types'
import { s3 } from '@configs'
@Resolver(CV)
export class CVResolver {
@FieldResolver()
async url(
@Root() cv: CV,
@Ctx() { req }: Context
): Promise<string> {
return cv.key ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: cv.key }) : ''
}
@Mutation(() => UploadedFileResponse, { nullable: true })
@UseMiddleware(isAuth)
async uploadCV(
@Arg('upload', () => GraphQLUpload) upload: FileUpload,
@Ctx() { req }: Context
): Promise<UploadedFileResponse> {
const { userId } = req.session
const { createReadStream, filename, mimetype, encoding } = upload as FileUpload
const stream = createReadStream()
const uploaded = await s3.upload({
Bucket: process.env.S3_BUCKET,
Body: stream,
Key: `users/${userId}/cvs/${filename}`,
}).promise()
await CV.create({ filename, key: uploaded.Key, userId }).save()
return {
filename,
mimetype,
encoding,
url: await s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: uploaded.Key })
}
}
@Query(() => [CV], { nullable: true })
async cvs(
@Arg('userId', () => Int) userId: number,
): Promise<CV[] | null> {
return CV.findBy({ userId })
}
@UseMiddleware(isAuth)
@Mutation(() => Boolean)
async deleteCV(
@Arg('id', () => Int) id: number,
@Ctx() { req }: Context
): Promise<boolean> {
const { userId } = req.session
const cv = await CV.findOneBy({ id, userId })
if (!cv) {
throw new Error('Not authorised!')
}
await s3.deleteObject({ Bucket: process.env.S3_BUCKET, Key: cv.key! }).promise()
await cv.remove()
return true
}
}
+118
View File
@@ -0,0 +1,118 @@
import { s3 } from '@configs'
import { EducationItem } from '@entities'
import { EducationItemInput, EducationItemResponse } from '@graphql-types'
import { isAuth } from '@middlewares'
import { Context } from '@types'
import { getExtensionFromFilename, saveAttributes } from '@utils'
import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql'
import { v4 } from 'uuid'
@Resolver(EducationItem)
export class EducationItemResolver {
@FieldResolver(() => String)
async photoUrl(
@Root() item: EducationItem
): Promise<string | null> {
return item.photo ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: item.photo }) : null
}
@Query(() => [EducationItem])
async educationItems(
@Arg('userId', () => Int) userId: number
): Promise<EducationItem[]> {
return (await EducationItem.find({ where: { userId } }))
}
@UseMiddleware(isAuth)
@Mutation(() => EducationItemResponse)
async createEducationItem(
@Arg('input', () => EducationItemInput) input: EducationItemInput,
@Ctx() { req }: Context
): Promise<EducationItemResponse> {
const { userId } = req.session
const { school, status, startDate, endDate, photo } = input
if (!school) {
return {
errors: [{
field: 'school',
message: 'School cannot be empty!'
}]
}
}
let key
if (photo) {
const { createReadStream, filename } = await photo
const stream = createReadStream()
const ext = getExtensionFromFilename(filename)
const uploaded = await s3.upload({
Bucket: process.env.S3_BUCKET,
Body: stream,
Key: `users/${userId}/photos/educationItems/${v4()}.${ext}`
}).promise()
key = uploaded.Key
}
return {
educationItem: await EducationItem.create({
school,
status,
startDate,
endDate,
photo: key,
userId
}).save()
}
}
@UseMiddleware(isAuth)
@Mutation(() => EducationItemResponse)
async updateEducationItem(
@Arg('id', () => Int) id: number,
@Arg('input', () => EducationItemInput) input: EducationItemInput,
@Ctx() { req }: Context
): Promise<EducationItemResponse> {
const { userId } = req.session
const { school, photo } = input
if (school != undefined && school == '') {
return {
errors: [{
field: 'school',
message: 'School cannot be empty!'
}]
}
}
const educationItem = await EducationItem.findOne({ where: { id, userId } })
if (!educationItem) {
throw new Error('Not authorised!')
}
saveAttributes<EducationItem, EducationItemInput>(educationItem, input)
if (photo) {
const { createReadStream, filename, } = await photo
const ext = getExtensionFromFilename(filename)
const stream = createReadStream()
const uploaded = await s3.upload({
Bucket: process.env.S3_BUCKET,
Body: stream,
Key: `users/${userId}/photos/educationItems/${v4()}.${ext}`,
}).promise()
educationItem.photo = uploaded.Key
}
return {
educationItem: await educationItem.save()
}
}
@UseMiddleware(isAuth)
@Mutation(() => Boolean)
async deleteEducationItem(
@Arg('id', () => Int) id: number,
@Ctx() { req }: Context
): Promise<boolean> {
const { userId } = req.session
const educationItem = await EducationItem.findOne({ where: { id, userId } })
if (!educationItem) {
throw new Error('Not authorised!')
}
await educationItem.remove()
return true
}
}
+118
View File
@@ -0,0 +1,118 @@
import { s3 } from '@configs'
import { Experience } from '@entities'
import { ExperienceInput, ExperienceResponse } from '@graphql-types'
import { isAuth } from '@middlewares'
import { Context } from '@types'
import { getExtensionFromFilename, saveAttributes } from '@utils'
import { Arg, Ctx, FieldResolver, Int, Mutation, Query, Resolver, Root, UseMiddleware } from 'type-graphql'
import { v4 } from 'uuid'
@Resolver(Experience)
export class ExperienceResolver {
@FieldResolver(() => String)
async photoUrl(
@Root() item: Experience
): Promise<string | null> {
return item.photo ? s3.getSignedUrlPromise('getObject', { Bucket: process.env.S3_BUCKET, Key: item.photo }) : null
}
@Query(() => [Experience])
async experiences(
@Arg('userId', () => Int) userId: number
): Promise<Experience[]> {
return (await Experience.find({ where: { userId } }))
}
@UseMiddleware(isAuth)
@Mutation(() => ExperienceResponse)
async createExperience(
@Arg('input', () => ExperienceInput) input: ExperienceInput,
@Ctx() { req }: Context
): Promise<ExperienceResponse> {
const { userId } = req.session
const { title, workplace, startDate, endDate, photo } = input
if (!title) {
return {
errors: [{
field: 'title',
message: 'Title cannot be empty!'
}]
}
}
let key
if (photo) {
const { createReadStream, filename } = await photo
const stream = createReadStream()
const ext = getExtensionFromFilename(filename)
const uploaded = await s3.upload({
Bucket: process.env.S3_BUCKET,
Body: stream,
Key: `users/${userId}/photos/experiences/${v4()}.${ext}`,
}).promise()
key = uploaded.Key
}
return {
experience: await Experience.create({
title,
workplace,
startDate,
endDate,
photo: key,
userId
}).save()
}
}
@UseMiddleware(isAuth)
@Mutation(() => ExperienceResponse)
async updateExperience(
@Arg('id', () => Int) id: number,
@Arg('input', () => ExperienceInput) input: ExperienceInput,
@Ctx() { req }: Context
): Promise<ExperienceResponse> {
const { userId } = req.session
const { title, photo } = input
if (title != undefined && title == '') {
return {
errors: [{
field: 'title',
message: 'Title cannot be empty!'
}]
}
}
const experience = await Experience.findOne({ where: { id, userId } })
if (!experience) {
throw new Error('Not authorised!')
}
saveAttributes<Experience, ExperienceInput>(experience, input)
if (photo) {
const { createReadStream, filename, } = await photo
const ext = getExtensionFromFilename(filename)
const stream = createReadStream()
const uploaded = await s3.upload({
Bucket: process.env.S3_BUCKET,
Body: stream,
Key: `users/${userId}/photos/experiences/${v4()}.${ext}`,
}).promise()
experience.photo = uploaded.Key
}
return {
experience: await experience.save()
}
}
@UseMiddleware(isAuth)
@Mutation(() => Boolean)
async deleteExperience(
@Arg('id', () => Int) id: number,
@Ctx() { req }: Context
): Promise<boolean> {
const { userId } = req.session
const experience = await Experience.findOne({ where: { id, userId } })
if (!experience) {
throw new Error('Not authorised!')
}
await experience.remove()
return true
}
}

Some files were not shown because too many files have changed in this diff Show More