rhythia-api 238.0.0 → 240.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/editProfile.ts +211 -49
- package/api/getProfile.ts +59 -18
- package/index.ts +25 -14
- package/package.json +2 -1
- package/types/database.ts +58 -0
package/api/editProfile.ts
CHANGED
|
@@ -1,26 +1,63 @@
|
|
|
1
1
|
import { NextResponse } from "../utils/response";
|
|
2
2
|
import z from "zod";
|
|
3
3
|
import { Database } from "../types/database";
|
|
4
|
-
import { protectedApi, validUser } from "../utils/requestUtils";
|
|
5
|
-
import { supabase } from "../utils/supabase";
|
|
6
|
-
import { getUserBySession } from "../utils/getUserBySession";
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
4
|
+
import { protectedApi, validUser } from "../utils/requestUtils";
|
|
5
|
+
import { supabase } from "../utils/supabase";
|
|
6
|
+
import { getUserBySession } from "../utils/getUserBySession";
|
|
7
|
+
import { COUNTRY_LIST } from "../utils/countryList";
|
|
8
|
+
import { User } from "@supabase/supabase-js";
|
|
9
|
+
import validator from "validator";
|
|
10
|
+
import removeZeroWidth from "zero-width";
|
|
11
|
+
|
|
12
|
+
const USERNAME_CHANGE_COOLDOWN_ERROR =
|
|
13
|
+
"Username can only be changed once every 6 months";
|
|
14
|
+
const FLAG_CHANGE_ERROR = "Flag can only be changed once";
|
|
15
|
+
const countryCodes = new Set(COUNTRY_LIST);
|
|
16
|
+
|
|
17
|
+
function getNextUsernameChangeAt(
|
|
18
|
+
lastChangedAt: string | null | undefined
|
|
19
|
+
): string | null {
|
|
20
|
+
if (!lastChangedAt) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const nextAllowedAt = new Date(lastChangedAt);
|
|
25
|
+
nextAllowedAt.setUTCMonth(nextAllowedAt.getUTCMonth() + 6);
|
|
26
|
+
return nextAllowedAt.toISOString();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function formatUsernameChangeDate(date: string): string {
|
|
30
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
31
|
+
year: "numeric",
|
|
32
|
+
month: "short",
|
|
33
|
+
day: "numeric",
|
|
34
|
+
}).format(new Date(date));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isUsernameChangeLocked(
|
|
38
|
+
lastChangedAt: string | null | undefined,
|
|
39
|
+
now = Date.now()
|
|
40
|
+
): boolean {
|
|
41
|
+
const nextAllowedAt = getNextUsernameChangeAt(lastChangedAt);
|
|
42
|
+
return nextAllowedAt !== null && new Date(nextAllowedAt).getTime() > now;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const Schema = {
|
|
46
|
+
input: z.strictObject({
|
|
47
|
+
session: z.string(),
|
|
48
|
+
data: z.object({
|
|
49
|
+
avatar_url: z.string().optional(),
|
|
50
|
+
flag: z.string().optional(),
|
|
51
|
+
profile_image: z.string().optional(),
|
|
52
|
+
username: z.string().optional(),
|
|
53
|
+
}),
|
|
54
|
+
}),
|
|
55
|
+
output: z.object({
|
|
56
|
+
error: z.string().optional(),
|
|
57
|
+
can_change_flag: z.boolean().optional(),
|
|
58
|
+
next_username_change_at: z.string().nullable().optional(),
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
24
61
|
|
|
25
62
|
export async function POST(request: Request): Promise<NextResponse> {
|
|
26
63
|
return protectedApi({
|
|
@@ -63,18 +100,33 @@ export async function handler(
|
|
|
63
100
|
}
|
|
64
101
|
}
|
|
65
102
|
|
|
66
|
-
if (validator.trim(data.data.username || "") !== (data.data.username || "")) {
|
|
67
|
-
return NextResponse.json(
|
|
68
|
-
{
|
|
69
|
-
error: "Username can't start or end with spaces.",
|
|
70
|
-
},
|
|
103
|
+
if (validator.trim(data.data.username || "") !== (data.data.username || "")) {
|
|
104
|
+
return NextResponse.json(
|
|
105
|
+
{
|
|
106
|
+
error: "Username can't start or end with spaces.",
|
|
107
|
+
},
|
|
71
108
|
{ status: 404 }
|
|
72
109
|
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
data.data.username = removeZeroWidth(data.data.username || "");
|
|
76
|
-
|
|
77
|
-
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
data.data.username = removeZeroWidth(data.data.username || "");
|
|
113
|
+
|
|
114
|
+
if (data.data.flag !== undefined) {
|
|
115
|
+
const normalizedFlag = validator.trim(data.data.flag).toUpperCase();
|
|
116
|
+
|
|
117
|
+
if (!countryCodes.has(normalizedFlag as (typeof COUNTRY_LIST)[number])) {
|
|
118
|
+
return NextResponse.json(
|
|
119
|
+
{
|
|
120
|
+
error: "Flag must be a valid country code.",
|
|
121
|
+
},
|
|
122
|
+
{ status: 404 }
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
data.data.flag = normalizedFlag;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const user = (await getUserBySession(data.session)) as User;
|
|
78
130
|
|
|
79
131
|
let userData: Database["public"]["Tables"]["profiles"]["Update"];
|
|
80
132
|
|
|
@@ -96,24 +148,94 @@ export async function handler(
|
|
|
96
148
|
userData = queryUserData[0];
|
|
97
149
|
}
|
|
98
150
|
|
|
99
|
-
if (
|
|
100
|
-
userData.ban == "excluded" ||
|
|
101
|
-
userData.ban == "restricted" ||
|
|
102
|
-
userData.ban == "silenced"
|
|
103
|
-
) {
|
|
151
|
+
if (
|
|
152
|
+
userData.ban == "excluded" ||
|
|
153
|
+
userData.ban == "restricted" ||
|
|
154
|
+
userData.ban == "silenced"
|
|
155
|
+
) {
|
|
104
156
|
return NextResponse.json(
|
|
105
157
|
{
|
|
106
158
|
error:
|
|
107
159
|
"Silenced, restricted or excluded players can't update their profile.",
|
|
108
160
|
},
|
|
109
161
|
{ status: 404 }
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const usernameHasChanged =
|
|
166
|
+
data.data.username !== undefined && data.data.username !== userData.username;
|
|
167
|
+
const flagHasChanged =
|
|
168
|
+
data.data.flag !== undefined && data.data.flag !== userData.flag;
|
|
169
|
+
|
|
170
|
+
let nextUsernameChangeAt: string | null = null;
|
|
171
|
+
let canChangeFlag = true;
|
|
172
|
+
|
|
173
|
+
if (usernameHasChanged) {
|
|
174
|
+
const { data: usernameHistory, error: usernameHistoryError } = await supabase
|
|
175
|
+
.from("profileUsernames")
|
|
176
|
+
.select("changed_at")
|
|
177
|
+
.eq("profile_id", userData.id!)
|
|
178
|
+
.order("changed_at", { ascending: false })
|
|
179
|
+
.limit(1);
|
|
180
|
+
|
|
181
|
+
if (usernameHistoryError) {
|
|
182
|
+
return NextResponse.json(
|
|
183
|
+
{
|
|
184
|
+
error: "Could not verify username change availability.",
|
|
185
|
+
},
|
|
186
|
+
{ status: 500 }
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const lastChangedAt = usernameHistory?.[0]?.changed_at ?? null;
|
|
191
|
+
nextUsernameChangeAt = getNextUsernameChangeAt(lastChangedAt);
|
|
192
|
+
|
|
193
|
+
if (isUsernameChangeLocked(lastChangedAt)) {
|
|
194
|
+
return NextResponse.json(
|
|
195
|
+
{
|
|
196
|
+
error: `${USERNAME_CHANGE_COOLDOWN_ERROR}. Next change available ${formatUsernameChangeDate(
|
|
197
|
+
nextUsernameChangeAt!
|
|
198
|
+
)}.`,
|
|
199
|
+
next_username_change_at: nextUsernameChangeAt,
|
|
200
|
+
},
|
|
201
|
+
{ status: 404 }
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (flagHasChanged) {
|
|
207
|
+
const { data: flagHistory, error: flagHistoryError } = await supabase
|
|
208
|
+
.from("profileFlags")
|
|
209
|
+
.select("id")
|
|
210
|
+
.eq("profile_id", userData.id!)
|
|
211
|
+
.limit(1);
|
|
212
|
+
|
|
213
|
+
if (flagHistoryError) {
|
|
214
|
+
return NextResponse.json(
|
|
215
|
+
{
|
|
216
|
+
error: "Could not verify flag change availability.",
|
|
217
|
+
},
|
|
218
|
+
{ status: 500 }
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
canChangeFlag = !flagHistory?.length;
|
|
223
|
+
|
|
224
|
+
if (!canChangeFlag) {
|
|
225
|
+
return NextResponse.json(
|
|
226
|
+
{
|
|
227
|
+
error: FLAG_CHANGE_ERROR,
|
|
228
|
+
can_change_flag: false,
|
|
229
|
+
},
|
|
230
|
+
{ status: 404 }
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const upsertPayload: Database["public"]["Tables"]["profiles"]["Update"] = {
|
|
236
|
+
id: userData.id,
|
|
237
|
+
computedUsername: data.data.username?.toLowerCase(),
|
|
238
|
+
...data.data,
|
|
117
239
|
};
|
|
118
240
|
|
|
119
241
|
const upsertResult = await supabase
|
|
@@ -121,14 +243,54 @@ export async function handler(
|
|
|
121
243
|
.upsert(upsertPayload)
|
|
122
244
|
.select();
|
|
123
245
|
|
|
124
|
-
if (upsertResult.error) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
246
|
+
if (upsertResult.error) {
|
|
247
|
+
if (
|
|
248
|
+
usernameHasChanged &&
|
|
249
|
+
upsertResult.error.message.includes(USERNAME_CHANGE_COOLDOWN_ERROR)
|
|
250
|
+
) {
|
|
251
|
+
if (!nextUsernameChangeAt) {
|
|
252
|
+
const { data: usernameHistory } = await supabase
|
|
253
|
+
.from("profileUsernames")
|
|
254
|
+
.select("changed_at")
|
|
255
|
+
.eq("profile_id", userData.id!)
|
|
256
|
+
.order("changed_at", { ascending: false })
|
|
257
|
+
.limit(1);
|
|
258
|
+
|
|
259
|
+
nextUsernameChangeAt = getNextUsernameChangeAt(
|
|
260
|
+
usernameHistory?.[0]?.changed_at ?? null
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return NextResponse.json(
|
|
265
|
+
{
|
|
266
|
+
error: `${USERNAME_CHANGE_COOLDOWN_ERROR}. Next change available ${
|
|
267
|
+
nextUsernameChangeAt
|
|
268
|
+
? formatUsernameChangeDate(nextUsernameChangeAt)
|
|
269
|
+
: "later"
|
|
270
|
+
}.`,
|
|
271
|
+
next_username_change_at: nextUsernameChangeAt,
|
|
272
|
+
},
|
|
273
|
+
{ status: 404 }
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (flagHasChanged && upsertResult.error.message.includes(FLAG_CHANGE_ERROR)) {
|
|
278
|
+
return NextResponse.json(
|
|
279
|
+
{
|
|
280
|
+
error: FLAG_CHANGE_ERROR,
|
|
281
|
+
can_change_flag: false,
|
|
282
|
+
},
|
|
283
|
+
{ status: 404 }
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return NextResponse.json(
|
|
288
|
+
{
|
|
289
|
+
error: "Can't update, username might be used by someone else!",
|
|
128
290
|
},
|
|
129
291
|
{ status: 404 }
|
|
130
292
|
);
|
|
131
293
|
}
|
|
132
|
-
|
|
133
|
-
return NextResponse.json({});
|
|
134
|
-
}
|
|
294
|
+
|
|
295
|
+
return NextResponse.json({});
|
|
296
|
+
}
|
package/api/getProfile.ts
CHANGED
|
@@ -5,14 +5,26 @@ import { Database } from "../types/database";
|
|
|
5
5
|
import { protectedApi } from "../utils/requestUtils";
|
|
6
6
|
import { supabase } from "../utils/supabase";
|
|
7
7
|
import { getUserBySession } from "../utils/getUserBySession";
|
|
8
|
-
import { User } from "@supabase/supabase-js";
|
|
9
|
-
import {
|
|
10
|
-
getActivityStatusForUserId,
|
|
11
|
-
getScoreActivityCutoffIso,
|
|
12
|
-
} from "../utils/activityStatus";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
import { User } from "@supabase/supabase-js";
|
|
9
|
+
import {
|
|
10
|
+
getActivityStatusForUserId,
|
|
11
|
+
getScoreActivityCutoffIso,
|
|
12
|
+
} from "../utils/activityStatus";
|
|
13
|
+
|
|
14
|
+
function getNextUsernameChangeAt(
|
|
15
|
+
lastChangedAt: string | null | undefined
|
|
16
|
+
): string | null {
|
|
17
|
+
if (!lastChangedAt) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const nextAllowedAt = new Date(lastChangedAt);
|
|
22
|
+
nextAllowedAt.setUTCMonth(nextAllowedAt.getUTCMonth() + 6);
|
|
23
|
+
return nextAllowedAt.toISOString();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const Schema = {
|
|
27
|
+
input: z.strictObject({
|
|
16
28
|
session: z.string(),
|
|
17
29
|
id: z.number().nullable().optional(),
|
|
18
30
|
}),
|
|
@@ -41,6 +53,14 @@ export const Schema = {
|
|
|
41
53
|
activity_status: z.enum(["active", "inactive"]),
|
|
42
54
|
is_online: z.boolean(),
|
|
43
55
|
last_active_timestamp: z.number().nullable(),
|
|
56
|
+
can_change_flag: z.boolean(),
|
|
57
|
+
next_username_change_at: z.string().nullable(),
|
|
58
|
+
previous_usernames: z.array(
|
|
59
|
+
z.object({
|
|
60
|
+
username: z.string(),
|
|
61
|
+
changed_at: z.string(),
|
|
62
|
+
})
|
|
63
|
+
),
|
|
44
64
|
clans: z
|
|
45
65
|
.object({
|
|
46
66
|
id: z.number(),
|
|
@@ -128,6 +148,18 @@ export async function handler(
|
|
|
128
148
|
.select("last_activity")
|
|
129
149
|
.eq("uid", user.uid || "")
|
|
130
150
|
.single();
|
|
151
|
+
|
|
152
|
+
const { data: usernameHistoryData } = await supabase
|
|
153
|
+
.from("profileUsernames")
|
|
154
|
+
.select("username,changed_at")
|
|
155
|
+
.eq("profile_id", user.id)
|
|
156
|
+
.order("changed_at", { ascending: false });
|
|
157
|
+
|
|
158
|
+
const { data: flagHistoryData } = await supabase
|
|
159
|
+
.from("profileFlags")
|
|
160
|
+
.select("id")
|
|
161
|
+
.eq("profile_id", user.id)
|
|
162
|
+
.limit(1);
|
|
131
163
|
|
|
132
164
|
//last 30 minutes
|
|
133
165
|
if (activityData && activityData.last_activity) {
|
|
@@ -165,24 +197,33 @@ export async function handler(
|
|
|
165
197
|
: null;
|
|
166
198
|
}
|
|
167
199
|
|
|
168
|
-
if (user.verificationDeadline < Date.now()) {
|
|
169
|
-
await supabase
|
|
170
|
-
.from("profiles")
|
|
171
|
-
.upsert({
|
|
200
|
+
if (user.verificationDeadline < Date.now()) {
|
|
201
|
+
await supabase
|
|
202
|
+
.from("profiles")
|
|
203
|
+
.upsert({
|
|
172
204
|
id: user.id,
|
|
173
205
|
verified: false,
|
|
174
|
-
})
|
|
175
|
-
.select();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
user
|
|
206
|
+
})
|
|
207
|
+
.select();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const previousUsernames = (usernameHistoryData || []).filter((entry) => {
|
|
211
|
+
return entry.username.toLowerCase() !== (user.username || "").toLowerCase();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const latestUsernameChangeAt = usernameHistoryData?.[0]?.changed_at ?? null;
|
|
215
|
+
|
|
216
|
+
return NextResponse.json({
|
|
217
|
+
user: {
|
|
180
218
|
...user,
|
|
181
219
|
position,
|
|
182
220
|
country_position: countryPosition,
|
|
183
221
|
activity_status: activityStatus,
|
|
184
222
|
is_online: isOnline,
|
|
185
223
|
last_active_timestamp: activityData?.last_activity ?? null,
|
|
224
|
+
can_change_flag: !flagHistoryData?.length,
|
|
225
|
+
next_username_change_at: getNextUsernameChangeAt(latestUsernameChangeAt),
|
|
226
|
+
previous_usernames: previousUsernames,
|
|
186
227
|
},
|
|
187
228
|
});
|
|
188
229
|
}
|
package/index.ts
CHANGED
|
@@ -301,18 +301,21 @@ export const editCollection = handleApi({url:"/api/editCollection",...EditCollec
|
|
|
301
301
|
// ./api/editProfile.ts API
|
|
302
302
|
|
|
303
303
|
/*
|
|
304
|
-
export const Schema = {
|
|
305
|
-
input: z.strictObject({
|
|
306
|
-
session: z.string(),
|
|
307
|
-
data: z.object({
|
|
308
|
-
avatar_url: z.string().optional(),
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
304
|
+
export const Schema = {
|
|
305
|
+
input: z.strictObject({
|
|
306
|
+
session: z.string(),
|
|
307
|
+
data: z.object({
|
|
308
|
+
avatar_url: z.string().optional(),
|
|
309
|
+
flag: z.string().optional(),
|
|
310
|
+
profile_image: z.string().optional(),
|
|
311
|
+
username: z.string().optional(),
|
|
312
|
+
}),
|
|
313
|
+
}),
|
|
314
|
+
output: z.object({
|
|
315
|
+
error: z.string().optional(),
|
|
316
|
+
can_change_flag: z.boolean().optional(),
|
|
317
|
+
next_username_change_at: z.string().nullable().optional(),
|
|
318
|
+
}),
|
|
316
319
|
};*/
|
|
317
320
|
import { Schema as EditProfile } from "./api/editProfile"
|
|
318
321
|
export { Schema as SchemaEditProfile } from "./api/editProfile"
|
|
@@ -962,8 +965,8 @@ export const getPassToken = handleApi({url:"/api/getPassToken",...GetPassToken})
|
|
|
962
965
|
// ./api/getProfile.ts API
|
|
963
966
|
|
|
964
967
|
/*
|
|
965
|
-
export const Schema = {
|
|
966
|
-
input: z.strictObject({
|
|
968
|
+
export const Schema = {
|
|
969
|
+
input: z.strictObject({
|
|
967
970
|
session: z.string(),
|
|
968
971
|
id: z.number().nullable().optional(),
|
|
969
972
|
}),
|
|
@@ -992,6 +995,14 @@ export const Schema = {
|
|
|
992
995
|
activity_status: z.enum(["active", "inactive"]),
|
|
993
996
|
is_online: z.boolean(),
|
|
994
997
|
last_active_timestamp: z.number().nullable(),
|
|
998
|
+
can_change_flag: z.boolean(),
|
|
999
|
+
next_username_change_at: z.string().nullable(),
|
|
1000
|
+
previous_usernames: z.array(
|
|
1001
|
+
z.object({
|
|
1002
|
+
username: z.string(),
|
|
1003
|
+
changed_at: z.string(),
|
|
1004
|
+
})
|
|
1005
|
+
),
|
|
995
1006
|
clans: z
|
|
996
1007
|
.object({
|
|
997
1008
|
id: z.number(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rhythia-api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "240.0.0",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"author": "online-contributors-cunev",
|
|
6
6
|
"scripts": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"test": "tsx ./scripts/test.ts",
|
|
11
11
|
"cache:clear-user-scores": "bun run scripts/clear-user-score-cache.ts",
|
|
12
12
|
"db:optimize-user-scores": "bun run scripts/optimize-user-scores-indexes.ts",
|
|
13
|
+
"db:create-profile-flags": "node scripts/create-profile-flags-table.ts",
|
|
13
14
|
"db:create-profile-reports": "node scripts/create-profile-reports-table.ts",
|
|
14
15
|
"query-pull": "bun run scripts/pull-queries.ts",
|
|
15
16
|
"query-push": "bun run scripts/deploy-queries.ts",
|
package/types/database.ts
CHANGED
|
@@ -558,6 +558,64 @@ export type Database = {
|
|
|
558
558
|
},
|
|
559
559
|
]
|
|
560
560
|
}
|
|
561
|
+
profileFlags: {
|
|
562
|
+
Row: {
|
|
563
|
+
changed_at: string
|
|
564
|
+
flag: string | null
|
|
565
|
+
id: number
|
|
566
|
+
profile_id: number
|
|
567
|
+
}
|
|
568
|
+
Insert: {
|
|
569
|
+
changed_at?: string
|
|
570
|
+
flag?: string | null
|
|
571
|
+
id?: number
|
|
572
|
+
profile_id: number
|
|
573
|
+
}
|
|
574
|
+
Update: {
|
|
575
|
+
changed_at?: string
|
|
576
|
+
flag?: string | null
|
|
577
|
+
id?: number
|
|
578
|
+
profile_id?: number
|
|
579
|
+
}
|
|
580
|
+
Relationships: [
|
|
581
|
+
{
|
|
582
|
+
foreignKeyName: "profileFlags_profile_id_fkey"
|
|
583
|
+
columns: ["profile_id"]
|
|
584
|
+
isOneToOne: false
|
|
585
|
+
referencedRelation: "profiles"
|
|
586
|
+
referencedColumns: ["id"]
|
|
587
|
+
},
|
|
588
|
+
]
|
|
589
|
+
}
|
|
590
|
+
profileUsernames: {
|
|
591
|
+
Row: {
|
|
592
|
+
changed_at: string
|
|
593
|
+
id: number
|
|
594
|
+
profile_id: number
|
|
595
|
+
username: string
|
|
596
|
+
}
|
|
597
|
+
Insert: {
|
|
598
|
+
changed_at?: string
|
|
599
|
+
id?: number
|
|
600
|
+
profile_id: number
|
|
601
|
+
username: string
|
|
602
|
+
}
|
|
603
|
+
Update: {
|
|
604
|
+
changed_at?: string
|
|
605
|
+
id?: number
|
|
606
|
+
profile_id?: number
|
|
607
|
+
username?: string
|
|
608
|
+
}
|
|
609
|
+
Relationships: [
|
|
610
|
+
{
|
|
611
|
+
foreignKeyName: "profileUsernames_profile_id_fkey"
|
|
612
|
+
columns: ["profile_id"]
|
|
613
|
+
isOneToOne: false
|
|
614
|
+
referencedRelation: "profiles"
|
|
615
|
+
referencedColumns: ["id"]
|
|
616
|
+
},
|
|
617
|
+
]
|
|
618
|
+
}
|
|
561
619
|
profiles: {
|
|
562
620
|
Row: {
|
|
563
621
|
about_me: string | null
|