Repository navigation
Expand file tree
/
Copy pathuser.ts
More file actions
129 lines (110 loc) · 3.31 KB
/
Copy pathuser.ts
File metadata and controls
129 lines (110 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { Column, Entity, Index, ManyToOne } from "typeorm";
import { z } from "zod";
import { ActorMention } from "../util/activitypub/constants.js";
import { Actor } from "./actor.js";
import { InstanceInvite } from "./instanceInvite.js";
import { getImageProxyUrl } from "../util/embeds/index.js";
import { UrlTransformer } from "../util/columns.js";
import { makeInstanceUrl } from "../util/url.js";
@Entity("users")
@Index(["name", "domain"], { unique: true })
export class User extends Actor {
/**
* The username of this user. Forms their mention
* Do not allow modification, as this will break federation.
*/
@Column({ update: false })
name: string;
/** Tokens generated past this date are valid */
@Column({ nullable: true, type: "timestamptz" })
valid_tokens_since: Date | null;
/** The password hash of this user. If null, this user is not from our domain */
@Column({ nullable: true, type: String })
password_hash: string | null;
/** The email address of this user */
@Column({ type: String, nullable: true })
email: string | null;
/** the invite code used to register to this instance */
@ManyToOne(() => InstanceInvite, {
nullable: true,
onDelete: "CASCADE",
})
invite: InstanceInvite | null;
/** User customisation fields start here */
/** The preferred/display name of this user */
@Column()
display_name: string;
/** The user's bio */
@Column({ nullable: true, type: String })
summary: string | null;
/**
* user avatar. if null, no avatar
* if URL, this is a remote avatar and should be fetched via media proxy
* if not, this is a hash
*/
@Column({
nullable: true,
type: String,
transformer: UrlTransformer,
})
avatar: URL | string | null;
/**
* user banner. if null, no banner
* if URL, this is a remote banner and should be fetched via media proxy
* if not, this is a hash
*/
@Column({
nullable: true,
type: String,
transformer: UrlTransformer,
})
banner: URL | string | null;
public get mention(): ActorMention {
return `${this.name}@${this.domain}`;
}
public toPublic = (): PublicUser => {
let avatarUrl;
if (this.avatar instanceof URL) avatarUrl = getImageProxyUrl(this.avatar, 500, 500).href;
else if (this.avatar)
avatarUrl = makeInstanceUrl(`/users/${this.id}/attachments/${this.avatar}`);
let bannerUrl;
if (this.banner instanceof URL) bannerUrl = getImageProxyUrl(this.banner, 1500, 500).href;
else if (this.banner)
bannerUrl = makeInstanceUrl(`/users/${this.id}/attachments/${this.banner}`);
return {
mention: this.mention,
name: this.name,
display_name: this.display_name,
summary: this.summary,
avatar: avatarUrl,
banner: bannerUrl,
};
};
public toPrivate = (): PrivateUser => {
return {
email: this.email,
...this.toPublic(),
};
};
}
export type PublicUser = Pick<User, "name" | "summary" | "display_name"> & {
mention: ActorMention;
avatar?: string;
banner?: string;
};
export type PrivateUser = PublicUser & Pick<User, "email">;
export const PublicUser: z.ZodType<PublicUser> = z
.object({
mention: ActorMention,
name: z.string(),
summary: z.string(),
display_name: z.string(),
avatar: z.url().optional(),
banner: z.url().optional(),
})
.openapi("PublicUser");
export const PrivateUser: z.ZodType<PrivateUser> = PublicUser.and(
z.object({
email: z.string(),
}),
).openapi("PrivateUser");