Spaces:
Sleeping
Sleeping
Download src/modules/users/user.entity.ts from mahrooshishaq/SmartfolioBackend: direct link, hf CLI and curl.
- Browser
- Download file 4.9 kB
-
https://huggingface.co/spaces/mahrooshishaq/SmartfolioBackend/resolve/main/src/modules/users/user.entity.ts
- Command line
-
hf download hf://spaces/mahrooshishaq/SmartfolioBackend/src/modules/users/user.entity.ts
-
curl -L -o user.entity.ts https://huggingface.co/spaces/mahrooshishaq/SmartfolioBackend/resolve/main/src/modules/users/user.entity.ts
4.9 kB
| import { Entity, PrimaryGeneratedColumn, Column, Index, OneToOne, OneToMany } from 'typeorm'; | |
| import { UserProfile } from './entities/user-profile.entity'; | |
| import { UserGoal } from './entities/user-goal.entity'; | |
| () | |
| export class User { | |
| ('uuid') | |
| id!: string; | |
| ({ unique: true }) | |
| email!: string; | |
| ({ type: 'varchar', nullable: true }) | |
| password: string | null; | |
| ({ type:'varchar', nullable: true, unique: true }) // <-- Google ID field | |
| googleId: string | null; | |
| () | |
| name!: string; | |
| ({ default: false }) | |
| isVerified!: boolean; | |
| ({ type: 'text', nullable: true }) | |
| refreshTokenHash: string | null; | |
| ({ type: 'varchar' , nullable: true }) | |
| otpHash: string | null; // store hashed OTP | |
| ({ type: 'timestamptz', nullable: true }) | |
| otpExpiry: Date | null; | |
| ({ type: 'timestamp', nullable: true }) | |
| refreshTokenIssuedAt: Date | null; | |
| ({ type: 'text', nullable: true }) | |
| resetTokenHash: string | null; | |
| ({ type: 'timestamp', nullable: true }) | |
| resetTokenExpiry: Date | null; | |
| ({ default: false }) | |
| isLoggedin!: boolean; | |
| /** | |
| * Access role. Added because the condition AdminKeyGuard's docblock set β | |
| * "if an admin dashboard is built later, replace this with a proper role | |
| * guard" β has now been met by the campaign admin UI. | |
| * | |
| * There is deliberately no self-serve path to 'admin': the column is set by | |
| * hand in the database. AdminKeyGuard stays in place on the existing scraper | |
| * maintenance routes; migrating those is unrelated risk. | |
| */ | |
| ({ type: 'varchar', length: 16, default: 'candidate' }) | |
| role!: 'candidate' | 'admin'; | |
| /** | |
| * Which campaign brought this user in β null for organic signups. | |
| * | |
| * This is the question the whole apply flow exists to answer: did posting a | |
| * role on LinkedIn actually acquire users, or did it just fill one seat? A | |
| * campaign that fills its role and adds nobody to the platform is a different | |
| * outcome from one that does both, and only this column can tell them apart. | |
| */ | |
| ({ type: 'uuid', nullable: true }) | |
| sourceCampaignId!: string | null; | |
| /** | |
| * Whether this person is currently open to being put forward for roles. | |
| * | |
| * `suspended` is set automatically when someone is submitted to companies | |
| * repeatedly and never turns up to the interview - see | |
| * CampaignsService.reviewAvailability. It is deliberately NOT a rejection or | |
| * a ban: it stops us spending a client's goodwill on a candidate who is not | |
| * actually available, and it lifts the moment they say otherwise. | |
| * | |
| * Only the candidate can lift it, by signing in and setting it back. An | |
| * operator cannot flip it for them: the whole point is to find out whether | |
| * the person is still looking, and only they can answer that. | |
| */ | |
| ({ type: 'varchar', length: 24, default: 'looking' }) | |
| availability!: 'looking' | 'suspended'; | |
| ({ type: 'timestamptz', nullable: true }) | |
| availabilityUpdatedAt!: Date | null; | |
| /** Shown to the candidate, so a suspension is never unexplained. */ | |
| ({ type: 'text', nullable: true }) | |
| suspensionReason!: string | null; | |
| /** | |
| * Last time the user did something deliberate β tracked an application, | |
| * updated one, or ran a custom search. Deliberately NOT bumped by feed reads: | |
| * a page load is not intent, and counting it would make everyone look active | |
| * forever. | |
| * | |
| * Stored as a timestamp rather than an `isActive` flag on purpose. Nothing has | |
| * to write "inactive" β no request arrives when a user stops caring β so a flag | |
| * would need a nightly sweep to stay honest and would drift whenever that | |
| * sweep failed. A timestamp goes stale on its own, and the activity window | |
| * becomes a number you can change without a migration or a backfill. | |
| */ | |
| () | |
| ({ type: 'timestamptz', nullable: true }) | |
| lastActiveAt: Date | null; | |
| /** | |
| * When this account was created. | |
| * | |
| * Added late, which is why it is nullable rather than a @CreateDateColumn. | |
| * That decorator would have stamped every existing row with the deploy time | |
| * under `synchronize: true`, so the whole user base would have appeared to | |
| * sign up on the same afternoon - a wrong answer presented with total | |
| * confidence. NULL says "before we started recording this", which is true, | |
| * and the admin list sorts those last and labels them. | |
| * | |
| * scripts/backfill-user-created-at.mjs recovers a real date for most of them | |
| * from the earliest thing they did. | |
| */ | |
| () | |
| ({ type: 'timestamptz', nullable: true }) | |
| createdAt: Date | null; | |
| // Relations | |
| (() => UserProfile, profile => profile.user, { cascade: true }) | |
| profile: UserProfile; | |
| (() => UserGoal, goal => goal.user, { cascade: true }) | |
| goals: UserGoal[]; | |
| } | |