rhythia-api 239.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.
@@ -1,15 +1,18 @@
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";
4
+ import { protectedApi, validUser } from "../utils/requestUtils";
5
5
  import { supabase } from "../utils/supabase";
6
6
  import { getUserBySession } from "../utils/getUserBySession";
7
+ import { COUNTRY_LIST } from "../utils/countryList";
7
8
  import { User } from "@supabase/supabase-js";
8
9
  import validator from "validator";
9
10
  import removeZeroWidth from "zero-width";
10
11
 
11
12
  const USERNAME_CHANGE_COOLDOWN_ERROR =
12
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);
13
16
 
14
17
  function getNextUsernameChangeAt(
15
18
  lastChangedAt: string | null | undefined
@@ -42,14 +45,16 @@ function isUsernameChangeLocked(
42
45
  export const Schema = {
43
46
  input: z.strictObject({
44
47
  session: z.string(),
45
- data: z.object({
46
- avatar_url: z.string().optional(),
47
- profile_image: z.string().optional(),
48
- username: z.string().optional(),
49
- }),
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
+ }),
50
54
  }),
51
55
  output: z.object({
52
56
  error: z.string().optional(),
57
+ can_change_flag: z.boolean().optional(),
53
58
  next_username_change_at: z.string().nullable().optional(),
54
59
  }),
55
60
  };
@@ -95,18 +100,33 @@ export async function handler(
95
100
  }
96
101
  }
97
102
 
98
- if (validator.trim(data.data.username || "") !== (data.data.username || "")) {
99
- return NextResponse.json(
100
- {
101
- error: "Username can't start or end with spaces.",
102
- },
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
+ },
103
108
  { status: 404 }
104
109
  );
105
110
  }
106
111
 
107
112
  data.data.username = removeZeroWidth(data.data.username || "");
108
-
109
- const user = (await getUserBySession(data.session)) as User;
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;
110
130
 
111
131
  let userData: Database["public"]["Tables"]["profiles"]["Update"];
112
132
 
@@ -144,8 +164,11 @@ export async function handler(
144
164
 
145
165
  const usernameHasChanged =
146
166
  data.data.username !== undefined && data.data.username !== userData.username;
167
+ const flagHasChanged =
168
+ data.data.flag !== undefined && data.data.flag !== userData.flag;
147
169
 
148
170
  let nextUsernameChangeAt: string | null = null;
171
+ let canChangeFlag = true;
149
172
 
150
173
  if (usernameHasChanged) {
151
174
  const { data: usernameHistory, error: usernameHistoryError } = await supabase
@@ -180,6 +203,35 @@ export async function handler(
180
203
  }
181
204
  }
182
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
+
183
235
  const upsertPayload: Database["public"]["Tables"]["profiles"]["Update"] = {
184
236
  id: userData.id,
185
237
  computedUsername: data.data.username?.toLowerCase(),
@@ -222,6 +274,16 @@ export async function handler(
222
274
  );
223
275
  }
224
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
+
225
287
  return NextResponse.json(
226
288
  {
227
289
  error: "Can't update, username might be used by someone else!",
package/api/getProfile.ts CHANGED
@@ -53,6 +53,7 @@ export const Schema = {
53
53
  activity_status: z.enum(["active", "inactive"]),
54
54
  is_online: z.boolean(),
55
55
  last_active_timestamp: z.number().nullable(),
56
+ can_change_flag: z.boolean(),
56
57
  next_username_change_at: z.string().nullable(),
57
58
  previous_usernames: z.array(
58
59
  z.object({
@@ -153,6 +154,12 @@ export async function handler(
153
154
  .select("username,changed_at")
154
155
  .eq("profile_id", user.id)
155
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);
156
163
 
157
164
  //last 30 minutes
158
165
  if (activityData && activityData.last_activity) {
@@ -214,6 +221,7 @@ export async function handler(
214
221
  activity_status: activityStatus,
215
222
  is_online: isOnline,
216
223
  last_active_timestamp: activityData?.last_activity ?? null,
224
+ can_change_flag: !flagHistoryData?.length,
217
225
  next_username_change_at: getNextUsernameChangeAt(latestUsernameChangeAt),
218
226
  previous_usernames: previousUsernames,
219
227
  },
package/index.ts CHANGED
@@ -304,14 +304,16 @@ export const editCollection = handleApi({url:"/api/editCollection",...EditCollec
304
304
  export const Schema = {
305
305
  input: z.strictObject({
306
306
  session: z.string(),
307
- data: z.object({
308
- avatar_url: z.string().optional(),
309
- profile_image: z.string().optional(),
310
- username: z.string().optional(),
311
- }),
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
+ }),
312
313
  }),
313
314
  output: z.object({
314
315
  error: z.string().optional(),
316
+ can_change_flag: z.boolean().optional(),
315
317
  next_username_change_at: z.string().nullable().optional(),
316
318
  }),
317
319
  };*/
@@ -993,6 +995,7 @@ export const Schema = {
993
995
  activity_status: z.enum(["active", "inactive"]),
994
996
  is_online: z.boolean(),
995
997
  last_active_timestamp: z.number().nullable(),
998
+ can_change_flag: z.boolean(),
996
999
  next_username_change_at: z.string().nullable(),
997
1000
  previous_usernames: z.array(
998
1001
  z.object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhythia-api",
3
- "version": "239.0.0",
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,35 @@ 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
+ }
561
590
  profileUsernames: {
562
591
  Row: {
563
592
  changed_at: string