rhythia-api 233.0.0 → 234.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.
Files changed (90) hide show
  1. package/.codex +0 -0
  2. package/.env +1 -12
  3. package/README.md +4 -4
  4. package/api/acceptInvite.ts +1 -1
  5. package/api/addCollectionMap.ts +1 -1
  6. package/api/chartPublicStats.ts +1 -1
  7. package/api/checkQualified.ts +93 -93
  8. package/api/createBeatmap.ts +53 -62
  9. package/api/createBeatmapPage.ts +1 -1
  10. package/api/createClan.ts +1 -1
  11. package/api/createCollection.ts +1 -1
  12. package/api/createInvite.ts +1 -1
  13. package/api/createSupporter.ts +1 -1
  14. package/api/deleteBeatmapPage.ts +2 -5
  15. package/api/deleteCollection.ts +1 -1
  16. package/api/deleteCollectionMap.ts +1 -1
  17. package/api/editAboutMe.ts +1 -1
  18. package/api/editClan.ts +1 -1
  19. package/api/editCollection.ts +1 -2
  20. package/api/editProfile.ts +1 -1
  21. package/api/enhancedSearch.ts +113 -113
  22. package/api/executeAdminOperation.ts +1 -22
  23. package/api/getAvatarUploadUrl.ts +1 -1
  24. package/api/getBadgeLeaders.ts +1 -1
  25. package/api/getBadgedUsers.ts +1 -1
  26. package/api/getBeatmapComments.ts +1 -1
  27. package/api/getBeatmapPage.ts +74 -106
  28. package/api/getBeatmapPageById.ts +70 -109
  29. package/api/getBeatmapStarRating.ts +1 -1
  30. package/api/getBeatmaps.ts +1 -1
  31. package/api/getClan.ts +1 -1
  32. package/api/getClans.ts +1 -1
  33. package/api/getCollection.ts +1 -1
  34. package/api/getCollections.ts +1 -1
  35. package/api/getInventory.ts +1 -1
  36. package/api/getLeaderboard.ts +1 -1
  37. package/api/getMapUploadUrl.ts +2 -2
  38. package/api/getOnlinePlayers.ts +1 -1
  39. package/api/getPassToken.ts +1 -1
  40. package/api/getProfile.ts +51 -31
  41. package/api/getPublicStats.ts +5 -5
  42. package/api/getRawStarRating.ts +1 -1
  43. package/api/getScore.ts +1 -1
  44. package/api/getStoryBeatmaps.ts +1 -1
  45. package/api/getTimestamp.ts +1 -1
  46. package/api/getUserScores.ts +19 -19
  47. package/api/getVerified.ts +1 -1
  48. package/api/getVideoUploadUrl.ts +1 -1
  49. package/api/postBeatmapComment.ts +1 -1
  50. package/api/qualifyMap.ts +97 -92
  51. package/api/rankMapsArchive.ts +20 -20
  52. package/api/searchUsers.ts +1 -1
  53. package/api/setPasskey.ts +1 -1
  54. package/api/submitScore.ts +1 -6
  55. package/api/submitScoreInternal.ts +461 -449
  56. package/api/updateBeatmapPage.ts +1 -1
  57. package/api/vetoMap.ts +101 -101
  58. package/index.ts +165 -153
  59. package/package.json +7 -12
  60. package/queries/admin_delete_user.sql +39 -39
  61. package/queries/admin_exclude_user.sql +21 -21
  62. package/queries/admin_invalidate_ranked_scores.sql +18 -18
  63. package/queries/admin_log_action.sql +10 -10
  64. package/queries/admin_profanity_clear.sql +29 -29
  65. package/queries/admin_remove_all_scores.sql +29 -29
  66. package/queries/admin_restrict_user.sql +21 -21
  67. package/queries/admin_search_users.sql +24 -24
  68. package/queries/admin_silence_user.sql +21 -21
  69. package/queries/admin_unban_user.sql +21 -21
  70. package/queries/enhanced_search.sql +217 -217
  71. package/queries/get_badge_leaderboard.sql +50 -50
  72. package/queries/get_clan_leaderboard.sql +68 -68
  73. package/queries/get_collections_v4.sql +109 -109
  74. package/queries/get_top_scores_for_beatmap.sql +44 -44
  75. package/queries/get_top_scores_for_beatmap3.sql +38 -0
  76. package/queries/get_user_by_email.sql +32 -32
  77. package/queries/get_user_scores_lastday.sql +47 -47
  78. package/queries/get_user_scores_reign.sql +31 -31
  79. package/queries/get_user_scores_top_and_stats.sql +84 -84
  80. package/queries/grant_special_badges.sql +69 -69
  81. package/types/database.ts +1288 -1248
  82. package/utils/beatmapTopScores.ts +84 -0
  83. package/utils/mapLifecycleWebhook.ts +287 -277
  84. package/utils/requestGeo.ts +13 -0
  85. package/utils/requestUtils.ts +127 -127
  86. package/utils/response.ts +11 -0
  87. package/worker.ts +189 -0
  88. package/wrangler.jsonc +10 -0
  89. package/index.html +0 -3
  90. package/vercel.json +0 -13
@@ -1,68 +1,68 @@
1
- CREATE OR REPLACE FUNCTION public.get_clan_leaderboard(page_number integer DEFAULT 1, items_per_page integer DEFAULT 10)
2
- RETURNS TABLE(id integer, name text, acronym text, avatar_url text, description text, member_count bigint, total_skill_points numeric, total_pages integer)
3
- LANGUAGE plpgsql
4
- SECURITY DEFINER
5
- SET search_path TO 'public'
6
- AS $function$
7
- DECLARE
8
- total_clan_pages integer;
9
- total_clan_count integer;
10
- BEGIN
11
- -- Calculate total clans
12
- SELECT COUNT(*) INTO total_clan_count FROM clans;
13
-
14
- -- Calculate total pages, ensuring at least 1 page even if no clans exist
15
- SELECT GREATEST(1, CEIL(total_clan_count::numeric / items_per_page)) INTO total_clan_pages;
16
-
17
- -- Set page_number to 1 if it's out of bounds
18
- IF page_number <= 0 OR page_number > total_clan_pages THEN
19
- page_number := 1;
20
- END IF;
21
-
22
- RETURN QUERY
23
- WITH clan_stats AS (
24
- SELECT
25
- c.id,
26
- c.name,
27
- c.acronym,
28
- c.avatar_url,
29
- c.description,
30
- COUNT(p.id) FILTER (WHERE p.ban != 'excluded' OR p.ban IS NULL) AS member_count,
31
- COALESCE(SUM(p.skill_points) FILTER (WHERE p.ban != 'excluded' OR p.ban IS NULL), 0)::numeric AS total_skill_points
32
- FROM
33
- clans c
34
- LEFT JOIN
35
- profiles p ON p.clan = c.id
36
- GROUP BY
37
- c.id, c.name, c.acronym, c.avatar_url, c.description
38
- ),
39
- clan_ranking AS (
40
- SELECT
41
- cs.id::integer,
42
- cs.name,
43
- cs.acronym,
44
- cs.avatar_url,
45
- cs.description,
46
- cs.member_count,
47
- cs.total_skill_points,
48
- ROW_NUMBER() OVER (ORDER BY cs.total_skill_points DESC, cs.member_count DESC, cs.name ASC) AS rank
49
- FROM
50
- clan_stats cs
51
- )
52
- SELECT
53
- cr.id,
54
- cr.name,
55
- cr.acronym,
56
- cr.avatar_url,
57
- cr.description,
58
- cr.member_count,
59
- cr.total_skill_points,
60
- total_clan_pages
61
- FROM
62
- clan_ranking cr
63
- WHERE
64
- (total_clan_count = 0 OR (cr.rank > (page_number - 1) * items_per_page AND cr.rank <= page_number * items_per_page))
65
- ORDER BY
66
- cr.total_skill_points DESC, cr.member_count DESC, cr.name ASC;
67
- END;
68
- $function$
1
+ CREATE OR REPLACE FUNCTION public.get_clan_leaderboard(page_number integer DEFAULT 1, items_per_page integer DEFAULT 10)
2
+ RETURNS TABLE(id integer, name text, acronym text, avatar_url text, description text, member_count bigint, total_skill_points numeric, total_pages integer)
3
+ LANGUAGE plpgsql
4
+ SECURITY DEFINER
5
+ SET search_path TO 'public'
6
+ AS $function$
7
+ DECLARE
8
+ total_clan_pages integer;
9
+ total_clan_count integer;
10
+ BEGIN
11
+ -- Calculate total clans
12
+ SELECT COUNT(*) INTO total_clan_count FROM clans;
13
+
14
+ -- Calculate total pages, ensuring at least 1 page even if no clans exist
15
+ SELECT GREATEST(1, CEIL(total_clan_count::numeric / items_per_page)) INTO total_clan_pages;
16
+
17
+ -- Set page_number to 1 if it's out of bounds
18
+ IF page_number <= 0 OR page_number > total_clan_pages THEN
19
+ page_number := 1;
20
+ END IF;
21
+
22
+ RETURN QUERY
23
+ WITH clan_stats AS (
24
+ SELECT
25
+ c.id,
26
+ c.name,
27
+ c.acronym,
28
+ c.avatar_url,
29
+ c.description,
30
+ COUNT(p.id) FILTER (WHERE p.ban != 'excluded' OR p.ban IS NULL) AS member_count,
31
+ COALESCE(SUM(p.skill_points) FILTER (WHERE p.ban != 'excluded' OR p.ban IS NULL), 0)::numeric AS total_skill_points
32
+ FROM
33
+ clans c
34
+ LEFT JOIN
35
+ profiles p ON p.clan = c.id
36
+ GROUP BY
37
+ c.id, c.name, c.acronym, c.avatar_url, c.description
38
+ ),
39
+ clan_ranking AS (
40
+ SELECT
41
+ cs.id::integer,
42
+ cs.name,
43
+ cs.acronym,
44
+ cs.avatar_url,
45
+ cs.description,
46
+ cs.member_count,
47
+ cs.total_skill_points,
48
+ ROW_NUMBER() OVER (ORDER BY cs.total_skill_points DESC, cs.member_count DESC, cs.name ASC) AS rank
49
+ FROM
50
+ clan_stats cs
51
+ )
52
+ SELECT
53
+ cr.id,
54
+ cr.name,
55
+ cr.acronym,
56
+ cr.avatar_url,
57
+ cr.description,
58
+ cr.member_count,
59
+ cr.total_skill_points,
60
+ total_clan_pages
61
+ FROM
62
+ clan_ranking cr
63
+ WHERE
64
+ (total_clan_count = 0 OR (cr.rank > (page_number - 1) * items_per_page AND cr.rank <= page_number * items_per_page))
65
+ ORDER BY
66
+ cr.total_skill_points DESC, cr.member_count DESC, cr.name ASC;
67
+ END;
68
+ $function$
@@ -1,109 +1,109 @@
1
- CREATE OR REPLACE FUNCTION public.get_collections_v4(page_number integer DEFAULT 1, items_per_page integer DEFAULT 10, owner_filter bigint DEFAULT NULL::bigint, search_query text DEFAULT NULL::text, author_filter text DEFAULT NULL::text, min_beatmaps integer DEFAULT NULL::integer)
2
- RETURNS TABLE(id bigint, title text, description text, created_at timestamp with time zone, owner bigint, owner_username text, owner_avatar_url text, beatmap_count bigint, star1 bigint, star2 bigint, star3 bigint, star4 bigint, star5 bigint, star6 bigint, star7 bigint, star8 bigint, star9 bigint, star10 bigint, star11 bigint, star12 bigint, star13 bigint, star14 bigint, star15 bigint, star16 bigint, star17 bigint, star18 bigint, total_pages bigint)
3
- LANGUAGE plpgsql
4
- AS $function$
5
- DECLARE
6
- total_count bigint;
7
- BEGIN
8
- -- Get total count with beatmap count filter
9
- WITH collection_counts AS (
10
- SELECT
11
- bc.id,
12
- COUNT(DISTINCT cr.id) as map_count
13
- FROM "beatmapCollections" bc
14
- LEFT JOIN "collectionRelations" cr ON cr.collection = bc.id
15
- LEFT JOIN "profiles" p ON p."id" = bc."owner"
16
- WHERE (owner_filter IS NULL OR bc.owner = owner_filter)
17
- AND (author_filter IS NULL OR p.username ILIKE '%' || author_filter || '%')
18
- AND (
19
- search_query IS NULL
20
- OR bc.title ILIKE '%' || search_query || '%'
21
- OR bc.description ILIKE '%' || search_query || '%'
22
- OR p.username ILIKE '%' || search_query || '%'
23
- )
24
- GROUP BY bc.id
25
- )
26
- SELECT COUNT(*)
27
- INTO total_count
28
- FROM collection_counts
29
- WHERE (min_beatmaps IS NULL OR map_count >= min_beatmaps);
30
-
31
- RETURN QUERY
32
- WITH collection_counts AS (
33
- SELECT
34
- bc.id,
35
- bc.title,
36
- bc.description,
37
- bc.created_at,
38
- bc.owner,
39
- p.username,
40
- p."avatar_url",
41
- COUNT(DISTINCT cr.id) as map_count,
42
- COUNT(*) FILTER (WHERE round(b."starRating") = 1) as star1,
43
- COUNT(*) FILTER (WHERE round(b."starRating") = 2) as star2,
44
- COUNT(*) FILTER (WHERE round(b."starRating") = 3) as star3,
45
- COUNT(*) FILTER (WHERE round(b."starRating") = 4) as star4,
46
- COUNT(*) FILTER (WHERE round(b."starRating") = 5) as star5,
47
- COUNT(*) FILTER (WHERE round(b."starRating") = 6) as star6,
48
- COUNT(*) FILTER (WHERE round(b."starRating") = 7) as star7,
49
- COUNT(*) FILTER (WHERE round(b."starRating") = 8) as star8,
50
- COUNT(*) FILTER (WHERE round(b."starRating") = 9) as star9,
51
- COUNT(*) FILTER (WHERE round(b."starRating") = 10) as star10,
52
- COUNT(*) FILTER (WHERE round(b."starRating") = 11) as star11,
53
- COUNT(*) FILTER (WHERE round(b."starRating") = 12) as star12,
54
- COUNT(*) FILTER (WHERE round(b."starRating") = 13) as star13,
55
- COUNT(*) FILTER (WHERE round(b."starRating") = 14) as star14,
56
- COUNT(*) FILTER (WHERE round(b."starRating") = 15) as star15,
57
- COUNT(*) FILTER (WHERE round(b."starRating") = 16) as star16,
58
- COUNT(*) FILTER (WHERE round(b."starRating") = 17) as star17,
59
- COUNT(*) FILTER (WHERE round(b."starRating") = 18) as star18
60
- FROM "beatmapCollections" bc
61
- LEFT JOIN "collectionRelations" cr ON cr.collection = bc.id
62
- LEFT JOIN "beatmapPages" bp ON bp.id = cr."beatmapPage"
63
- LEFT JOIN "beatmaps" b ON b."beatmapHash" = bp."latestBeatmapHash"
64
- LEFT JOIN "profiles" p ON p."id" = bc."owner"
65
- WHERE (owner_filter IS NULL OR bc.owner = owner_filter)
66
- AND (author_filter IS NULL OR p.username ILIKE '%' || author_filter || '%')
67
- AND (
68
- search_query IS NULL
69
- OR bc.title ILIKE '%' || search_query || '%'
70
- OR bc.description ILIKE '%' || search_query || '%'
71
- OR p.username ILIKE '%' || search_query || '%'
72
- )
73
- GROUP BY bc.id, bc.title, bc.description, bc.created_at, bc.owner, p.username, p."avatar_url"
74
- HAVING (min_beatmaps IS NULL OR COUNT(DISTINCT cr.id) >= min_beatmaps)
75
- )
76
- SELECT
77
- cc.id,
78
- cc.title,
79
- cc.description,
80
- cc.created_at,
81
- cc.owner,
82
- cc.username as owner_username,
83
- cc."avatar_url" as owner_avatar_url,
84
- cc.map_count as beatmap_count,
85
- cc.star1,
86
- cc.star2,
87
- cc.star3,
88
- cc.star4,
89
- cc.star5,
90
- cc.star6,
91
- cc.star7,
92
- cc.star8,
93
- cc.star9,
94
- cc.star10,
95
- cc.star11,
96
- cc.star12,
97
- cc.star13,
98
- cc.star14,
99
- cc.star15,
100
- cc.star16,
101
- cc.star17,
102
- cc.star18,
103
- CEILING(total_count::numeric / items_per_page)::bigint as total_pages
104
- FROM collection_counts cc
105
- ORDER BY cc.created_at DESC
106
- LIMIT items_per_page
107
- OFFSET ((page_number - 1) * items_per_page);
108
- END;
109
- $function$
1
+ CREATE OR REPLACE FUNCTION public.get_collections_v4(page_number integer DEFAULT 1, items_per_page integer DEFAULT 10, owner_filter bigint DEFAULT NULL::bigint, search_query text DEFAULT NULL::text, author_filter text DEFAULT NULL::text, min_beatmaps integer DEFAULT NULL::integer)
2
+ RETURNS TABLE(id bigint, title text, description text, created_at timestamp with time zone, owner bigint, owner_username text, owner_avatar_url text, beatmap_count bigint, star1 bigint, star2 bigint, star3 bigint, star4 bigint, star5 bigint, star6 bigint, star7 bigint, star8 bigint, star9 bigint, star10 bigint, star11 bigint, star12 bigint, star13 bigint, star14 bigint, star15 bigint, star16 bigint, star17 bigint, star18 bigint, total_pages bigint)
3
+ LANGUAGE plpgsql
4
+ AS $function$
5
+ DECLARE
6
+ total_count bigint;
7
+ BEGIN
8
+ -- Get total count with beatmap count filter
9
+ WITH collection_counts AS (
10
+ SELECT
11
+ bc.id,
12
+ COUNT(DISTINCT cr.id) as map_count
13
+ FROM "beatmapCollections" bc
14
+ LEFT JOIN "collectionRelations" cr ON cr.collection = bc.id
15
+ LEFT JOIN "profiles" p ON p."id" = bc."owner"
16
+ WHERE (owner_filter IS NULL OR bc.owner = owner_filter)
17
+ AND (author_filter IS NULL OR p.username ILIKE '%' || author_filter || '%')
18
+ AND (
19
+ search_query IS NULL
20
+ OR bc.title ILIKE '%' || search_query || '%'
21
+ OR bc.description ILIKE '%' || search_query || '%'
22
+ OR p.username ILIKE '%' || search_query || '%'
23
+ )
24
+ GROUP BY bc.id
25
+ )
26
+ SELECT COUNT(*)
27
+ INTO total_count
28
+ FROM collection_counts
29
+ WHERE (min_beatmaps IS NULL OR map_count >= min_beatmaps);
30
+
31
+ RETURN QUERY
32
+ WITH collection_counts AS (
33
+ SELECT
34
+ bc.id,
35
+ bc.title,
36
+ bc.description,
37
+ bc.created_at,
38
+ bc.owner,
39
+ p.username,
40
+ p."avatar_url",
41
+ COUNT(DISTINCT cr.id) as map_count,
42
+ COUNT(*) FILTER (WHERE round(b."starRating") = 1) as star1,
43
+ COUNT(*) FILTER (WHERE round(b."starRating") = 2) as star2,
44
+ COUNT(*) FILTER (WHERE round(b."starRating") = 3) as star3,
45
+ COUNT(*) FILTER (WHERE round(b."starRating") = 4) as star4,
46
+ COUNT(*) FILTER (WHERE round(b."starRating") = 5) as star5,
47
+ COUNT(*) FILTER (WHERE round(b."starRating") = 6) as star6,
48
+ COUNT(*) FILTER (WHERE round(b."starRating") = 7) as star7,
49
+ COUNT(*) FILTER (WHERE round(b."starRating") = 8) as star8,
50
+ COUNT(*) FILTER (WHERE round(b."starRating") = 9) as star9,
51
+ COUNT(*) FILTER (WHERE round(b."starRating") = 10) as star10,
52
+ COUNT(*) FILTER (WHERE round(b."starRating") = 11) as star11,
53
+ COUNT(*) FILTER (WHERE round(b."starRating") = 12) as star12,
54
+ COUNT(*) FILTER (WHERE round(b."starRating") = 13) as star13,
55
+ COUNT(*) FILTER (WHERE round(b."starRating") = 14) as star14,
56
+ COUNT(*) FILTER (WHERE round(b."starRating") = 15) as star15,
57
+ COUNT(*) FILTER (WHERE round(b."starRating") = 16) as star16,
58
+ COUNT(*) FILTER (WHERE round(b."starRating") = 17) as star17,
59
+ COUNT(*) FILTER (WHERE round(b."starRating") = 18) as star18
60
+ FROM "beatmapCollections" bc
61
+ LEFT JOIN "collectionRelations" cr ON cr.collection = bc.id
62
+ LEFT JOIN "beatmapPages" bp ON bp.id = cr."beatmapPage"
63
+ LEFT JOIN "beatmaps" b ON b."beatmapHash" = bp."latestBeatmapHash"
64
+ LEFT JOIN "profiles" p ON p."id" = bc."owner"
65
+ WHERE (owner_filter IS NULL OR bc.owner = owner_filter)
66
+ AND (author_filter IS NULL OR p.username ILIKE '%' || author_filter || '%')
67
+ AND (
68
+ search_query IS NULL
69
+ OR bc.title ILIKE '%' || search_query || '%'
70
+ OR bc.description ILIKE '%' || search_query || '%'
71
+ OR p.username ILIKE '%' || search_query || '%'
72
+ )
73
+ GROUP BY bc.id, bc.title, bc.description, bc.created_at, bc.owner, p.username, p."avatar_url"
74
+ HAVING (min_beatmaps IS NULL OR COUNT(DISTINCT cr.id) >= min_beatmaps)
75
+ )
76
+ SELECT
77
+ cc.id,
78
+ cc.title,
79
+ cc.description,
80
+ cc.created_at,
81
+ cc.owner,
82
+ cc.username as owner_username,
83
+ cc."avatar_url" as owner_avatar_url,
84
+ cc.map_count as beatmap_count,
85
+ cc.star1,
86
+ cc.star2,
87
+ cc.star3,
88
+ cc.star4,
89
+ cc.star5,
90
+ cc.star6,
91
+ cc.star7,
92
+ cc.star8,
93
+ cc.star9,
94
+ cc.star10,
95
+ cc.star11,
96
+ cc.star12,
97
+ cc.star13,
98
+ cc.star14,
99
+ cc.star15,
100
+ cc.star16,
101
+ cc.star17,
102
+ cc.star18,
103
+ CEILING(total_count::numeric / items_per_page)::bigint as total_pages
104
+ FROM collection_counts cc
105
+ ORDER BY cc.created_at DESC
106
+ LIMIT items_per_page
107
+ OFFSET ((page_number - 1) * items_per_page);
108
+ END;
109
+ $function$
@@ -1,44 +1,44 @@
1
- CREATE OR REPLACE FUNCTION public.get_top_scores_for_beatmap(beatmap_hash text)
2
- RETURNS TABLE(id bigint, awarded_sp numeric, created_at timestamp with time zone, misses numeric, mods json, passed boolean, replayhwid text, songid text, speed numeric, spin boolean, userid bigint, username text, avatar_url text, accuracy numeric)
3
- LANGUAGE plpgsql
4
- AS $function$
5
- BEGIN
6
- RETURN QUERY
7
- WITH user_top_scores AS (
8
- SELECT DISTINCT ON (s."userId")
9
- s.id,
10
- s.awarded_sp,
11
- s.created_at,
12
- s.misses,
13
- s.mods,
14
- s.passed,
15
- s."replayHwid",
16
- s."songId",
17
- s.speed,
18
- s.spin,
19
- s."userId",
20
- p.username,
21
- p.avatar_url,
22
- CASE
23
- WHEN b."noteCount" > 0 THEN
24
- ROUND(((b."noteCount" - s.misses) / b."noteCount" * 100)::numeric, 2)
25
- ELSE 0
26
- END AS accuracy
27
- FROM
28
- scores s
29
- JOIN
30
- profiles p ON s."userId" = p.id
31
- JOIN
32
- beatmaps b ON s."beatmapHash" = b."beatmapHash"
33
- WHERE
34
- s."beatmapHash" = beatmap_hash
35
- AND p.ban <> 'excluded'
36
- ORDER BY
37
- s."userId", s.awarded_sp DESC
38
- )
39
- SELECT *
40
- FROM user_top_scores
41
- ORDER BY awarded_sp DESC
42
- LIMIT 10;
43
- END;
44
- $function$
1
+ CREATE OR REPLACE FUNCTION public.get_top_scores_for_beatmap(beatmap_hash text)
2
+ RETURNS TABLE(id bigint, awarded_sp numeric, created_at timestamp with time zone, misses numeric, mods json, passed boolean, replayhwid text, songid text, speed numeric, spin boolean, userid bigint, username text, avatar_url text, accuracy numeric)
3
+ LANGUAGE plpgsql
4
+ AS $function$
5
+ BEGIN
6
+ RETURN QUERY
7
+ WITH user_top_scores AS (
8
+ SELECT DISTINCT ON (s."userId")
9
+ s.id,
10
+ s.awarded_sp,
11
+ s.created_at,
12
+ s.misses,
13
+ s.mods,
14
+ s.passed,
15
+ s."replayHwid",
16
+ s."songId",
17
+ s.speed,
18
+ s.spin,
19
+ s."userId",
20
+ p.username,
21
+ p.avatar_url,
22
+ CASE
23
+ WHEN b."noteCount" > 0 THEN
24
+ ROUND(((b."noteCount" - s.misses) / b."noteCount" * 100)::numeric, 2)
25
+ ELSE 0
26
+ END AS accuracy
27
+ FROM
28
+ scores s
29
+ JOIN
30
+ profiles p ON s."userId" = p.id
31
+ JOIN
32
+ beatmaps b ON s."beatmapHash" = b."beatmapHash"
33
+ WHERE
34
+ s."beatmapHash" = beatmap_hash
35
+ AND p.ban <> 'excluded'
36
+ ORDER BY
37
+ s."userId", s.awarded_sp DESC
38
+ )
39
+ SELECT *
40
+ FROM user_top_scores
41
+ ORDER BY awarded_sp DESC
42
+ LIMIT 10;
43
+ END;
44
+ $function$
@@ -0,0 +1,38 @@
1
+ CREATE OR REPLACE FUNCTION public.get_top_scores_for_beatmap3(beatmap_hash text)
2
+ RETURNS TABLE(id bigint, awarded_sp numeric, created_at timestamp without time zone, misses integer, mods json, passed boolean, "replayHwid" text, "songId" text, speed numeric, spin boolean, "userId" bigint, username text, avatar_url text, accuracy numeric)
3
+ LANGUAGE sql
4
+ STABLE
5
+ AS $function$
6
+ WITH bm AS (
7
+ SELECT "noteCount"
8
+ FROM beatmaps
9
+ WHERE "beatmapHash" = beatmap_hash
10
+ )
11
+ SELECT
12
+ s.id,
13
+ s.awarded_sp,
14
+ s.created_at,
15
+ s.misses,
16
+ s.mods,
17
+ s.passed,
18
+ s."replayHwid",
19
+ s."songId",
20
+ s.speed,
21
+ s.spin,
22
+ s."userId",
23
+ p.username,
24
+ p.avatar_url,
25
+ CASE
26
+ WHEN bm."noteCount" > 0 THEN
27
+ ROUND(((bm."noteCount" - s.misses) / bm."noteCount" * 100)::numeric, 2)
28
+ ELSE 0
29
+ END AS accuracy
30
+ FROM leaderboard_map_user l
31
+ JOIN scores s ON s.id = l.best_score_id
32
+ JOIN profiles p ON s."userId" = p.id
33
+ CROSS JOIN bm
34
+ WHERE l."beatmapHash" = beatmap_hash
35
+ AND p.ban <> 'excluded'
36
+ ORDER BY l.best_sp DESC
37
+ LIMIT 10;
38
+ $function$
@@ -1,32 +1,32 @@
1
- CREATE OR REPLACE FUNCTION public.get_user_by_email(email_address text)
2
- RETURNS json
3
- LANGUAGE plpgsql
4
- SECURITY DEFINER
5
- SET search_path TO 'public'
6
- AS $function$
7
- DECLARE
8
- user_data json;
9
- BEGIN
10
- -- Check that the executing user has permission to access user data
11
- -- You can add more permission checks here as needed
12
-
13
- -- Query the auth.users table to find the user with the matching email
14
- SELECT json_build_object(
15
- 'id', id,
16
- 'email', email,
17
- 'created_at', created_at,
18
- 'last_sign_in_at', last_sign_in_at,
19
- 'user_metadata', raw_user_meta_data
20
- -- Add or remove fields as needed
21
- ) INTO user_data
22
- FROM auth.users
23
- WHERE email = email_address;
24
-
25
- -- Return NULL if no user was found
26
- IF user_data IS NULL THEN
27
- RETURN NULL;
28
- END IF;
29
-
30
- RETURN user_data;
31
- END;
32
- $function$
1
+ CREATE OR REPLACE FUNCTION public.get_user_by_email(email_address text)
2
+ RETURNS json
3
+ LANGUAGE plpgsql
4
+ SECURITY DEFINER
5
+ SET search_path TO 'public'
6
+ AS $function$
7
+ DECLARE
8
+ user_data json;
9
+ BEGIN
10
+ -- Check that the executing user has permission to access user data
11
+ -- You can add more permission checks here as needed
12
+
13
+ -- Query the auth.users table to find the user with the matching email
14
+ SELECT json_build_object(
15
+ 'id', id,
16
+ 'email', email,
17
+ 'created_at', created_at,
18
+ 'last_sign_in_at', last_sign_in_at,
19
+ 'user_metadata', raw_user_meta_data
20
+ -- Add or remove fields as needed
21
+ ) INTO user_data
22
+ FROM auth.users
23
+ WHERE email = email_address;
24
+
25
+ -- Return NULL if no user was found
26
+ IF user_data IS NULL THEN
27
+ RETURN NULL;
28
+ END IF;
29
+
30
+ RETURN user_data;
31
+ END;
32
+ $function$
@@ -1,47 +1,47 @@
1
- CREATE OR REPLACE FUNCTION public.get_user_scores_lastday(userid integer, limit_param integer)
2
- RETURNS jsonb
3
- LANGUAGE sql
4
- AS $function$
5
- with s as (
6
- select
7
- sc.created_at,
8
- sc.id,
9
- sc.passed,
10
- sc."userId",
11
- sc.awarded_sp,
12
- sc."beatmapHash",
13
- sc.misses,
14
- sc.replay_url,
15
- sc."songId",
16
- sc.speed,
17
- sc.spin
18
- from public.scores sc
19
- where sc."userId" = userid
20
- and sc.passed = true
21
- order by sc.created_at desc
22
- limit least(coalesce(limit_param, 100), 100)
23
- )
24
- select coalesce(
25
- jsonb_agg(
26
- jsonb_build_object(
27
- 'created_at', s.created_at,
28
- 'id', s.id,
29
- 'passed', s.passed,
30
- 'userId', s."userId",
31
- 'awarded_sp', s.awarded_sp,
32
- 'beatmapHash', s."beatmapHash",
33
- 'misses', s.misses,
34
- 'replay_url', s.replay_url,
35
- 'songId', s."songId",
36
- 'beatmapDifficulty', b.difficulty,
37
- 'beatmapNotes', b."noteCount",
38
- 'beatmapTitle', b.title,
39
- 'speed', s.speed,
40
- 'spin', s.spin
41
- )
42
- ),
43
- '[]'::jsonb
44
- )
45
- from s
46
- left join public.beatmaps b on b."beatmapHash" = s."beatmapHash";
47
- $function$
1
+ CREATE OR REPLACE FUNCTION public.get_user_scores_lastday(userid integer, limit_param integer)
2
+ RETURNS jsonb
3
+ LANGUAGE sql
4
+ AS $function$
5
+ with s as (
6
+ select
7
+ sc.created_at,
8
+ sc.id,
9
+ sc.passed,
10
+ sc."userId",
11
+ sc.awarded_sp,
12
+ sc."beatmapHash",
13
+ sc.misses,
14
+ sc.replay_url,
15
+ sc."songId",
16
+ sc.speed,
17
+ sc.spin
18
+ from public.scores sc
19
+ where sc."userId" = userid
20
+ and sc.passed = true
21
+ order by sc.created_at desc
22
+ limit least(coalesce(limit_param, 100), 100)
23
+ )
24
+ select coalesce(
25
+ jsonb_agg(
26
+ jsonb_build_object(
27
+ 'created_at', s.created_at,
28
+ 'id', s.id,
29
+ 'passed', s.passed,
30
+ 'userId', s."userId",
31
+ 'awarded_sp', s.awarded_sp,
32
+ 'beatmapHash', s."beatmapHash",
33
+ 'misses', s.misses,
34
+ 'replay_url', s.replay_url,
35
+ 'songId', s."songId",
36
+ 'beatmapDifficulty', b.difficulty,
37
+ 'beatmapNotes', b."noteCount",
38
+ 'beatmapTitle', b.title,
39
+ 'speed', s.speed,
40
+ 'spin', s.spin
41
+ )
42
+ ),
43
+ '[]'::jsonb
44
+ )
45
+ from s
46
+ left join public.beatmaps b on b."beatmapHash" = s."beatmapHash";
47
+ $function$